Merge remote-tracking branch 'upstream/master' into CMH-GuiltyConscienceAndBackfire

This commit is contained in:
Clint Herron 2017-03-23 19:26:34 -04:00
commit bce7e690a8
13 changed files with 360 additions and 144 deletions

View file

@ -45,6 +45,7 @@ import java.io.InputStreamReader;
import java.io.Writer; import java.io.Writer;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.net.SocketException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.URL; import java.net.URL;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -391,7 +392,12 @@ public class ConnectDialog extends MageDialog {
connection.setUsername(this.txtUserName.getText().trim()); connection.setUsername(this.txtUserName.getText().trim());
connection.setPassword(this.txtPassword.getText().trim()); connection.setPassword(this.txtPassword.getText().trim());
connection.setForceDBComparison(this.chkForceUpdateDB.isSelected()); connection.setForceDBComparison(this.chkForceUpdateDB.isSelected());
connection.setUserIdStr(System.getProperty("user.name") + ':' + MagePreferences.getUserNames()); String allMAC = "";
try {
allMAC = connection.getMAC();
} catch (SocketException ex) {
}
connection.setUserIdStr(System.getProperty("user.name") + ":" + System.getProperty("os.name") + ":" + MagePreferences.getUserNames() + ":" + allMAC);
MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor) cbFlag.getEditor()).getImageItem()); MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor) cbFlag.getEditor()).getImageItem());
PreferencesDialog.setProxyInformation(connection); PreferencesDialog.setProxyInformation(connection);

View file

@ -258,6 +258,24 @@ public class Connection {
return null; return null;
} }
public static String getMAC() throws SocketException {
StringBuilder allMACs = new StringBuilder();
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) {
NetworkInterface iface = interfaces.nextElement();
byte[] mac = iface.getHardwareAddress();
if (mac != null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
}
sb.append(';');
allMACs.append(sb.toString());
}
}
return allMACs.toString();
}
public void setUserData(UserData userData) { public void setUserData(UserData userData) {
this.userData = userData; this.userData = userData;
} }

View file

@ -106,27 +106,28 @@ public enum ChatManager {
ChatSession chatSession = chatSessions.get(chatId); ChatSession chatSession = chatSessions.get(chatId);
if (chatSession != null) { if (chatSession != null) {
if (message.startsWith("\\") || message.startsWith("/")) { if (message.startsWith("\\") || message.startsWith("/")) {
User user = UserManager.instance.getUserByName(userName); Optional<User> user = UserManager.instance.getUserByName(userName);
if (user != null) { if (user.isPresent()) {
if (!performUserCommand(user, message, chatId, false)) { if (!performUserCommand(user.get(), message, chatId, false)) {
performUserCommand(user, message, chatId, true); performUserCommand(user.get(), message, chatId, true);
} }
return; return;
} }
} }
if (messageType != MessageType.GAME) { if (messageType != MessageType.GAME) {
User user = UserManager.instance.getUserByName(userName); Optional<User> u = UserManager.instance.getUserByName(userName);
if (message != null && userName != null && !userName.isEmpty()) { if (u.isPresent()) {
User user = u.get();
if (message.equals(userMessages.get(userName))) { if (message.equals(userMessages.get(userName))) {
// prevent identical messages // prevent identical messages
String informUser = "Your message appears to be identical to your last message"; String informUser = "Your message appears to be identical to your last message";
chatSessions.get(chatId).broadcastInfoToUser(user, informUser); chatSessions.get(chatId).broadcastInfoToUser(user, informUser);
return; return;
} }
if (message.length() > 500) { if (message.length() > 500) {
message = message.replaceFirst("^(.{500}).*", "$1 (rest of message truncated)"); message = message.replaceFirst("^(.{500}).*", "$1 (rest of message truncated)");
} }
@ -157,10 +158,9 @@ public enum ChatManager {
} }
userMessages.put(userName, message); userMessages.put(userName, message);
}
if (messageType == MessageType.TALK) {
if (user != null) { if (messageType == MessageType.TALK) {
if (user.getChatLockedUntil() != null) { if (user.getChatLockedUntil() != null) {
if (user.getChatLockedUntil().compareTo(Calendar.getInstance().getTime()) > 0) { if (user.getChatLockedUntil().compareTo(Calendar.getInstance().getTime()) > 0) {
chatSessions.get(chatId).broadcastInfoToUser(user, "Your chat is muted until " + SystemUtil.dateFormat.format(user.getChatLockedUntil())); chatSessions.get(chatId).broadcastInfoToUser(user, "Your chat is muted until " + SystemUtil.dateFormat.format(user.getChatLockedUntil()));
@ -169,11 +169,12 @@ public enum ChatManager {
user.setChatLockedUntil(null); user.setChatLockedUntil(null);
} }
} }
}
}
}
}
chatSession.broadcast(userName, message, color, withTime, messageType, soundToPlay);
} }
chatSession.broadcast(userName, message, color, withTime, messageType, soundToPlay);
} }
} }
@ -213,9 +214,9 @@ public enum ChatManager {
if (first > 1) { if (first > 1) {
String userToName = rest.substring(0, first); String userToName = rest.substring(0, first);
rest = rest.substring(first + 1).trim(); rest = rest.substring(first + 1).trim();
User userTo = UserManager.instance.getUserByName(userToName); Optional<User> userTo = UserManager.instance.getUserByName(userToName);
if (userTo != null) { if (userTo.isPresent()) {
if (!chatSessions.get(chatId).broadcastWhisperToUser(user, userTo, rest)) { if (!chatSessions.get(chatId).broadcastWhisperToUser(user, userTo.get(), rest)) {
message += new StringBuilder("<br/>User ").append(userToName).append(" not found").toString(); message += new StringBuilder("<br/>User ").append(userToName).append(" not found").toString();
chatSessions.get(chatId).broadcastInfoToUser(user, message); chatSessions.get(chatId).broadcastInfoToUser(user, message);
} }
@ -244,7 +245,7 @@ public enum ChatManager {
* @param color * @param color
*/ */
public void broadcast(UUID userId, String message, MessageColor color) throws UserNotFoundException { public void broadcast(UUID userId, String message, MessageColor color) throws UserNotFoundException {
UserManager.instance.getUser(userId).ifPresent(user-> { UserManager.instance.getUser(userId).ifPresent(user -> {
chatSessions.values() chatSessions.values()
.stream() .stream()
.filter(chat -> chat.hasUser(userId)) .filter(chat -> chat.hasUser(userId))

View file

@ -1071,12 +1071,11 @@ public class MageServerImpl implements MageServer {
@Override @Override
public void muteUser(final String sessionId, final String userName, final long durationMinutes) throws MageException { public void muteUser(final String sessionId, final String userName, final long durationMinutes) throws MageException {
execute("muteUser", sessionId, () -> { execute("muteUser", sessionId, () -> {
User user = UserManager.instance.getUserByName(userName); UserManager.instance.getUserByName(userName).ifPresent(user -> {
if (user != null) {
Date muteUntil = new Date(Calendar.getInstance().getTimeInMillis() + (durationMinutes * Timer.ONE_MINUTE)); Date muteUntil = new Date(Calendar.getInstance().getTimeInMillis() + (durationMinutes * Timer.ONE_MINUTE));
user.showUserMessage("Admin info", "You were muted for chat messages until " + SystemUtil.dateFormat.format(muteUntil) + '.'); user.showUserMessage("Admin info", "You were muted for chat messages until " + SystemUtil.dateFormat.format(muteUntil) + '.');
user.setChatLockedUntil(muteUntil); user.setChatLockedUntil(muteUntil);
} });
}); });
} }
@ -1084,15 +1083,14 @@ public class MageServerImpl implements MageServer {
@Override @Override
public void lockUser(final String sessionId, final String userName, final long durationMinutes) throws MageException { public void lockUser(final String sessionId, final String userName, final long durationMinutes) throws MageException {
execute("lockUser", sessionId, () -> { execute("lockUser", sessionId, () -> {
User user = UserManager.instance.getUserByName(userName); UserManager.instance.getUserByName(userName).ifPresent(user -> {
if (user != null) {
Date lockUntil = new Date(Calendar.getInstance().getTimeInMillis() + (durationMinutes * Timer.ONE_MINUTE)); Date lockUntil = new Date(Calendar.getInstance().getTimeInMillis() + (durationMinutes * Timer.ONE_MINUTE));
user.showUserMessage("Admin info", "Your user profile was locked until " + SystemUtil.dateFormat.format(lockUntil) + '.'); user.showUserMessage("Admin info", "Your user profile was locked until " + SystemUtil.dateFormat.format(lockUntil) + '.');
user.setLockedUntil(lockUntil); user.setLockedUntil(lockUntil);
if (user.isConnected()) { if (user.isConnected()) {
SessionManager.instance.disconnectUser(sessionId, user.getSessionId()); SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
} }
} });
}); });
} }
@ -1101,8 +1099,9 @@ public class MageServerImpl implements MageServer {
public void setActivation(final String sessionId, final String userName, boolean active) throws MageException { public void setActivation(final String sessionId, final String userName, boolean active) throws MageException {
execute("setActivation", sessionId, () -> { execute("setActivation", sessionId, () -> {
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByName(userName); AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
User user = UserManager.instance.getUserByName(userName); Optional<User> u = UserManager.instance.getUserByName(userName);
if (user != null) { if (u.isPresent()) {
User user = u.get();
user.setActive(active); user.setActive(active);
if (!user.isActive() && user.isConnected()) { if (!user.isActive() && user.isConnected()) {
SessionManager.instance.disconnectUser(sessionId, user.getSessionId()); SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
@ -1117,16 +1116,14 @@ public class MageServerImpl implements MageServer {
@Override @Override
public void toggleActivation(final String sessionId, final String userName) throws MageException { public void toggleActivation(final String sessionId, final String userName) throws MageException {
execute("toggleActivation", sessionId, () -> { execute("toggleActivation", sessionId, () ->
User user = UserManager.instance.getUserByName(userName); UserManager.instance.getUserByName(userName).ifPresent(user ->
if (user != null) { {
user.setActive(!user.isActive()); user.setActive(!user.isActive());
if (!user.isActive() && user.isConnected()) { if (!user.isActive() && user.isConnected()) {
SessionManager.instance.disconnectUser(sessionId, user.getSessionId()); SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
} }
} }));
});
} }
@Override @Override
@ -1144,13 +1141,10 @@ public class MageServerImpl implements MageServer {
@Override @Override
public void removeTable(final String sessionId, final UUID tableId) throws MageException { public void removeTable(final String sessionId, final UUID tableId) throws MageException {
execute("removeTable", sessionId, () -> { execute("removeTable", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId); SessionManager.instance.getSession(sessionId).ifPresent(session -> {
if (!session.isPresent()) { UUID userId = session.getUserId();
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.removeTable(userId, tableId); TableManager.instance.removeTable(userId, tableId);
} });
}); });
} }
@ -1162,15 +1156,12 @@ public class MageServerImpl implements MageServer {
@Override @Override
public void sendFeedbackMessage(final String sessionId, final String username, final String title, final String type, final String message, final String email) throws MageException { public void sendFeedbackMessage(final String sessionId, final String username, final String title, final String type, final String message, final String email) throws MageException {
if (title != null && message != null) { if (title != null && message != null) {
execute("sendFeedbackMessage", sessionId, () -> { execute("sendFeedbackMessage", sessionId, () ->
Optional<Session> session = SessionManager.instance.getSession(sessionId); SessionManager.instance.getSession(sessionId).ifPresent(
if (!session.isPresent()) { session -> FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.getHost())
logger.error(String.format("Session not found: %s", sessionId));
} else {
FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.get().getHost());
}
}); ));
} }
} }

View file

@ -217,55 +217,59 @@ public class Session {
if (authorizedUser.lockedUntil.compareTo(Calendar.getInstance().getTime()) > 0) { if (authorizedUser.lockedUntil.compareTo(Calendar.getInstance().getTime()) > 0) {
return "Your profile is deactivated until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil); return "Your profile is deactivated until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil);
} else { } else {
User user = UserManager.instance.createUser(userName, host, authorizedUser); UserManager.instance.createUser(userName, host, authorizedUser).ifPresent(user ->
if (user != null && authorizedUser.lockedUntil != null) { user.setLockedUntil(null)
user.setLockedUntil(null); );
}
Optional<User> selectUser = UserManager.instance.createUser(userName, host, authorizedUser);
boolean reconnect = false;
if (!selectUser.isPresent()) { // user already exists
selectUser = UserManager.instance.getUserByName(userName);
if (selectUser.isPresent()) {
User user = selectUser.get();
// If authentication is not activated, check the identity using IP address.
if (ConfigSettings.instance.isAuthenticationActivated() || user.getHost().equals(host)) {
user.updateLastActivity(null); // minimizes possible expiration
this.userId = user.getId();
if (user.getSessionId().isEmpty()) {
logger.info("Reconnecting session for " + userName);
reconnect = true;
} else {
//disconnect previous session
logger.info("Disconnecting another user instance: " + userName);
SessionManager.instance.disconnect(user.getSessionId(), DisconnectReason.ConnectingOtherInstance);
}
} else {
return "User name " + userName + " already in use (or your IP address changed)";
}
} }
} }
} User user = selectUser.get();
} if (!UserManager.instance.connectToSession(sessionId, user.getId())) {
User user = UserManager.instance.createUser(userName, host, authorizedUser); return "Error connecting " + userName;
boolean reconnect = false; }
if (user == null) { // user already exists this.userId = user.getId();
user = UserManager.instance.getUserByName(userName); if (reconnect) { // must be connected to receive the message
// If authentication is not activated, check the identity using IP address. Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(GamesRoomManager.instance.getMainRoomId());
if (ConfigSettings.instance.isAuthenticationActivated() || user.getHost().equals(host)) { if (!room.isPresent()) {
user.updateLastActivity(null); // minimizes possible expiration logger.error("main room not found");
this.userId = user.getId(); return null;
if (user.getSessionId().isEmpty()) { }
logger.info("Reconnecting session for " + userName); ChatManager.instance.joinChat(room.get().getChatId(), userId);
reconnect = true; ChatManager.instance.sendReconnectMessage(userId);
} else {
//disconnect previous session
logger.info("Disconnecting another user instance: " + userName);
SessionManager.instance.disconnect(user.getSessionId(), DisconnectReason.ConnectingOtherInstance);
} }
} else {
return "User name " + userName + " already in use (or your IP address changed)";
} }
} }
if (!UserManager.instance.connectToSession(sessionId, user.getId())) {
return "Error connecting " + userName;
}
this.userId = user.getId();
if (reconnect) { // must be connected to receive the message
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(GamesRoomManager.instance.getMainRoomId());
if (!room.isPresent()) {
logger.error("main room not found");
return null;
}
ChatManager.instance.joinChat(room.get().getChatId(), userId);
ChatManager.instance.sendReconnectMessage(userId);
}
return null; return null;
} }
public void connectAdmin() { public void connectAdmin() {
this.isAdmin = true; this.isAdmin = true;
User user = UserManager.instance.createUser("Admin", host, null); User user = UserManager.instance.createUser("Admin", host, null).orElse(
if (user == null) { UserManager.instance.getUserByName("Admin").get());
user = UserManager.instance.getUserByName("Admin");
}
UserData adminUserData = UserData.getDefaultUserDataView(); UserData adminUserData = UserData.getDefaultUserDataView();
adminUserData.setGroupId(UserGroup.ADMIN.getGroupId()); adminUserData.setGroupId(UserGroup.ADMIN.getGroupId());
user.setUserData(adminUserData); user.setUserData(adminUserData);
@ -276,8 +280,8 @@ public class Session {
} }
public boolean setUserData(String userName, UserData userData, String clientVersion, String userIdStr) { public boolean setUserData(String userName, UserData userData, String clientVersion, String userIdStr) {
User user = UserManager.instance.getUserByName(userName); Optional<User> _user = UserManager.instance.getUserByName(userName);
if (user != null) { _user.ifPresent(user -> {
if (clientVersion != null) { if (clientVersion != null) {
user.setClientVersion(clientVersion); user.setClientVersion(clientVersion);
} }
@ -294,9 +298,8 @@ public class Session {
if (user.getUserData().getAvatarId() == 11) { if (user.getUserData().getAvatarId() == 11) {
user.getUserData().setAvatarId(updateAvatar(user.getName())); user.getUserData().setAvatarId(updateAvatar(user.getName()));
} }
return true; });
} return _user.isPresent();
return false;
} }
private int updateAvatar(String userName) { private int updateAvatar(String userName) {

View file

@ -51,7 +51,11 @@ public enum SessionManager {
public Optional<Session> getSession(@Nonnull String sessionId) { public Optional<Session> getSession(@Nonnull String sessionId) {
Session session = sessions.get(sessionId); Session session = sessions.get(sessionId);
if (session != null && session.getUserId() != null && UserManager.instance.getUser(session.getUserId()) == null) { if(session == null){
logger.error("Session with sessionId " + sessionId + " is not found");
return Optional.empty();
}
if (session.getUserId() != null && UserManager.instance.getUser(session.getUserId()) == null) {
logger.error("User for session " + sessionId + " with userId " + session.getUserId() + " is missing. Session removed."); logger.error("User for session " + sessionId + " with userId " + session.getUserId() + " is missing. Session removed.");
// can happen if user from same host signs in multiple time with multiple clients, after he disconnects with one client // can happen if user from same host signs in multiple time with multiple clients, after he disconnects with one client
disconnect(sessionId, DisconnectReason.ConnectingOtherInstance); disconnect(sessionId, DisconnectReason.ConnectingOtherInstance);

View file

@ -42,7 +42,7 @@ import java.util.concurrent.*;
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public enum UserManager { public enum UserManager {
instance; instance;
protected final ScheduledExecutorService expireExecutor = Executors.newSingleThreadScheduledExecutor(); protected final ScheduledExecutorService expireExecutor = Executors.newSingleThreadScheduledExecutor();
@ -50,7 +50,6 @@ public enum UserManager {
private static final Logger LOGGER = Logger.getLogger(UserManager.class); private static final Logger LOGGER = Logger.getLogger(UserManager.class);
private final ConcurrentHashMap<UUID, User> users = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, User> users = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, User> usersByName = new ConcurrentHashMap<>();
private static final ExecutorService USER_EXECUTOR = ThreadExecutor.getInstance().getCallExecutor(); private static final ExecutorService USER_EXECUTOR = ThreadExecutor.getInstance().getCallExecutor();
@ -58,32 +57,36 @@ public enum UserManager {
expireExecutor.scheduleAtFixedRate(this::checkExpired, 60, 60, TimeUnit.SECONDS); expireExecutor.scheduleAtFixedRate(this::checkExpired, 60, 60, TimeUnit.SECONDS);
} }
public User createUser(String userName, String host, AuthorizedUser authorizedUser) { public Optional<User> createUser(String userName, String host, AuthorizedUser authorizedUser) {
if (getUserByName(userName) != null) { if (getUserByName(userName).isPresent()) {
return null; //user already exists return Optional.empty(); //user already exists
} }
User user = new User(userName, host, authorizedUser); User user = new User(userName, host, authorizedUser);
users.put(user.getId(), user); users.put(user.getId(), user);
usersByName.put(userName, user); return Optional.of(user);
return user;
} }
public Optional<User> getUser(UUID userId) { public Optional<User> getUser(UUID userId) {
if (users.get(userId) == null) { if (!users.containsKey(userId)) {
LOGGER.error(String.format("User with id %s could not be found", userId)); LOGGER.error(String.format("User with id %s could not be found", userId));
return Optional.empty(); return Optional.empty();
} else { } else {
return Optional.of(users.get(userId)); return Optional.of(users.get(userId));
} }
/* if (userId != null) {
return users.get(userId);
}
return null;
*/
} }
public User getUserByName(String userName) { public Optional<User> getUserByName(String userName) {
return usersByName.get(userName); Optional<User> u = users.values().stream().filter(user -> user.getName().equals(userName))
.findFirst();
if (u.isPresent()) {
return u;
} else {
LOGGER.error("User with name " + userName + " could not be found");
return Optional.empty();
}
} }
public Collection<User> getUsers() { public Collection<User> getUsers() {
@ -103,12 +106,9 @@ public enum UserManager {
public void disconnect(UUID userId, DisconnectReason reason) { public void disconnect(UUID userId, DisconnectReason reason) {
if (userId != null) { if (userId != null) {
User user = users.get(userId); getUser(userId).ifPresent(user -> user.setSessionId(""));// Session will be set again with new id if user reconnects
if (user != null) {
user.setSessionId(""); // Session will be set again with new id if user reconnects
}
ChatManager.instance.removeUser(userId, reason);
} }
ChatManager.instance.removeUser(userId, reason);
} }
public boolean isAdmin(UUID userId) { public boolean isAdmin(UUID userId) {
@ -123,25 +123,21 @@ public enum UserManager {
public void removeUser(final UUID userId, final DisconnectReason reason) { public void removeUser(final UUID userId, final DisconnectReason reason) {
if (userId != null) { if (userId != null) {
final User user = users.get(userId); getUser(userId).ifPresent(user ->
if (user != null) { USER_EXECUTOR.execute(
USER_EXECUTOR.execute( () -> {
() -> { try {
try { LOGGER.info("USER REMOVE - " + user.getName() + " (" + reason.toString() + ") userId: " + userId + " [" + user.getGameInfo() + ']');
LOGGER.info("USER REMOVE - " + user.getName() + " (" + reason.toString() + ") userId: " + userId + " [" + user.getGameInfo() + ']'); user.remove(reason);
user.remove(reason); LOGGER.debug("USER REMOVE END - " + user.getName());
LOGGER.debug("USER REMOVE END - " + user.getName()); } catch (Exception ex) {
} catch (Exception ex) { handleException(ex);
handleException(ex); } finally {
} finally { users.remove(userId);
users.remove(userId); }
usersByName.remove(user.getName());
} }
} ));
);
} else {
LOGGER.warn("Trying to remove userId: " + userId + " - but it does not exist.");
}
} }
} }
@ -183,9 +179,9 @@ public enum UserManager {
} }
public String getUserHistory(String userName) { public String getUserHistory(String userName) {
User user = getUserByName(userName); Optional<User> user = getUserByName(userName);
if (user != null) { if (user.isPresent()) {
return "History of user " + userName + " - " + user.getUserData().getHistory(); return "History of user " + userName + " - " + user.get().getUserData().getHistory();
} }
UserStats userStats = UserStatsRepository.instance.getUser(userName); UserStats userStats = UserStatsRepository.instance.getUser(userName);
@ -199,10 +195,7 @@ public enum UserManager {
public void updateUserHistory() { public void updateUserHistory() {
USER_EXECUTOR.execute(() -> { USER_EXECUTOR.execute(() -> {
for (String updatedUser : UserStatsRepository.instance.updateUserStats()) { for (String updatedUser : UserStatsRepository.instance.updateUserStats()) {
User user = getUserByName(updatedUser); getUserByName(updatedUser).ifPresent(User::resetUserStats);
if (user != null) {
user.resetUserStats();
}
} }
}); });
} }

View file

@ -89,7 +89,7 @@ class GeistOfSaintTraftEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null && effect.apply(game, source)) { if (controller != null && effect.apply(game, source)) {
effect.exileTokensCreatedAtNextEndStep(game, source); effect.exileTokensCreatedAtEndOfCombat(game, source);
return true; return true;
} }
return false; return false;

View file

@ -97,7 +97,7 @@ class InvocationOfSaintTraftEffect extends OneShotEffect {
CreateTokenEffect effect = new CreateTokenEffect(new AngelToken(), 1, true, true); CreateTokenEffect effect = new CreateTokenEffect(new AngelToken(), 1, true, true);
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null && (effect.apply(game, source))) { if (controller != null && (effect.apply(game, source))) {
effect.exileTokensCreatedAtNextEndStep(game, source); effect.exileTokensCreatedAtEndOfCombat(game, source);
return true; return true;
} }
return false; return false;

View file

@ -94,7 +94,7 @@ class KariZevSkyshipRaiderEffect extends OneShotEffect {
CreateTokenEffect effect = new CreateTokenEffect(new RagavanToken(), 1, true, true); CreateTokenEffect effect = new CreateTokenEffect(new RagavanToken(), 1, true, true);
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null && effect.apply(game, source)) { if (controller != null && effect.apply(game, source)) {
effect.exileTokensCreatedAtNextEndStep(game, source); effect.exileTokensCreatedAtEndOfCombat(game, source);
return true; return true;
} }
return false; return false;

View file

@ -0,0 +1,187 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.q;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetLandPermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author jeffwadsworth
*/
public class QuicksilverFountain extends CardImpl {
public QuicksilverFountain(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// At the beginning of each player's upkeep, that player puts a flood counter on target non-Island land he or she controls of his or her choice. That land is an Island for as long as it has a flood counter on it.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new QuicksilverFountainEffect(), TargetController.ANY, false, true));
// At the beginning of each end step, if all lands on the battlefield are Islands, remove all flood counters from them.
Condition condition = new AllLandsAreSubtypeCondition("Island");
this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new QuicksilverFountainEffect2(), TargetController.ANY, condition, false));
}
public QuicksilverFountain(final QuicksilverFountain card) {
super(card);
}
@Override
public QuicksilverFountain copy() {
return new QuicksilverFountain(this);
}
}
class QuicksilverFountainEffect extends OneShotEffect {
static final private FilterLandPermanent filterNonIslandLand = new FilterLandPermanent("non-Island land");
static {
filterNonIslandLand.add(Predicates.not(new SubtypePredicate("Island")));
}
public QuicksilverFountainEffect() {
super(Outcome.Neutral);
staticText = "that player puts a flood counter on target non-Island land he or she controls of his or her choice. That land is an Island for as long as it has a flood counter on it";
}
public QuicksilverFountainEffect(final QuicksilverFountainEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source));
Target targetNonIslandLand = new TargetLandPermanent(filterNonIslandLand);
if (player != null) {
if (player.choose(Outcome.Neutral, targetNonIslandLand, source.getId(), game)) {
Permanent landChosen = game.getPermanent(targetNonIslandLand.getFirstTarget());
landChosen.addCounters(CounterType.FLOOD.createInstance(), source, game);
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new BecomesBasicLandTargetEffect(Duration.OneUse, "Island"), new LandHasFloodCounterCondition(this), staticText);
this.setTargetPointer(new FixedTarget(landChosen, game));
effect.setTargetPointer(new FixedTarget(landChosen, game));
game.addEffect(effect, source);
return true;
}
}
return false;
}
@Override
public QuicksilverFountainEffect copy() {
return new QuicksilverFountainEffect(this);
}
}
class QuicksilverFountainEffect2 extends OneShotEffect {
public QuicksilverFountainEffect2() {
super(Outcome.Neutral);
staticText = "remove all flood counters from them";
}
public QuicksilverFountainEffect2(final QuicksilverFountainEffect2 effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
for (Permanent land : game.getBattlefield().getAllActivePermanents(CardType.LAND)) {
land.removeCounters(CounterType.FLOOD.createInstance(land.getCounters(game).getCount(CounterType.FLOOD)), game);
}
return true;
}
@Override
public QuicksilverFountainEffect2 copy() {
return new QuicksilverFountainEffect2(this);
}
}
class AllLandsAreSubtypeCondition implements Condition {
private final String subtype;
public AllLandsAreSubtypeCondition(String subtype) {
this.subtype = subtype;
}
@Override
public boolean apply(Game game, Ability source) {
FilterLandPermanent filterLand = new FilterLandPermanent();
filterLand.add(new SubtypePredicate(subtype));
int landCount = game.getBattlefield().getAllActivePermanents(CardType.LAND).size();
return game.getBattlefield().getAllActivePermanents(filterLand, game).size() == landCount;
}
@Override
public String toString() {
return "if all lands on the battlefield are " + subtype + "s";
}
}
class LandHasFloodCounterCondition implements Condition {
private final Effect effect;
public LandHasFloodCounterCondition(Effect effect) {
this.effect = effect;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(effect.getTargetPointer().getFirst(game, source));
return permanent != null
&& permanent.getCounters(game).getCount(CounterType.FLOOD) > 0;
}
}

View file

@ -208,6 +208,7 @@ public class Mirrodin extends ExpansionSet {
cards.add(new SetCardInfo("Psychogenic Probe", 231, Rarity.RARE, mage.cards.p.PsychogenicProbe.class)); cards.add(new SetCardInfo("Psychogenic Probe", 231, Rarity.RARE, mage.cards.p.PsychogenicProbe.class));
cards.add(new SetCardInfo("Pyrite Spellbomb", 232, Rarity.COMMON, mage.cards.p.PyriteSpellbomb.class)); cards.add(new SetCardInfo("Pyrite Spellbomb", 232, Rarity.COMMON, mage.cards.p.PyriteSpellbomb.class));
cards.add(new SetCardInfo("Quicksilver Elemental", 47, Rarity.RARE, mage.cards.q.QuicksilverElemental.class)); cards.add(new SetCardInfo("Quicksilver Elemental", 47, Rarity.RARE, mage.cards.q.QuicksilverElemental.class));
cards.add(new SetCardInfo("Quicksilver Fountain", 233, Rarity.RARE, mage.cards.q.QuicksilverFountain.class));
cards.add(new SetCardInfo("Raise the Alarm", 16, Rarity.COMMON, mage.cards.r.RaiseTheAlarm.class)); cards.add(new SetCardInfo("Raise the Alarm", 16, Rarity.COMMON, mage.cards.r.RaiseTheAlarm.class));
cards.add(new SetCardInfo("Razor Barrier", 17, Rarity.COMMON, mage.cards.r.RazorBarrier.class)); cards.add(new SetCardInfo("Razor Barrier", 17, Rarity.COMMON, mage.cards.r.RazorBarrier.class));
cards.add(new SetCardInfo("Regress", 48, Rarity.COMMON, mage.cards.r.Regress.class)); cards.add(new SetCardInfo("Regress", 48, Rarity.COMMON, mage.cards.r.Regress.class));

View file

@ -31,6 +31,7 @@ import java.util.ArrayList;
import java.util.UUID; import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
@ -124,6 +125,17 @@ public class CreateTokenEffect extends OneShotEffect {
} }
} }
public void exileTokensCreatedAtEndOfCombat(Game game, Ability source) {
for (UUID tokenId : this.getLastAddedTokenIds()) {
Permanent tokenPermanent = game.getPermanent(tokenId);
if (tokenPermanent != null) {
ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD);
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source);
}
}
}
private void setText() { private void setText() {
StringBuilder sb = new StringBuilder("create "); StringBuilder sb = new StringBuilder("create ");
if (amount.toString().equals("1")) { if (amount.toString().equals("1")) {