This commit is contained in:
Ingmar Goudt 2020-12-26 15:26:17 +01:00
commit 34e9c2fdfb
118 changed files with 3090 additions and 1075 deletions

View file

@ -168,8 +168,13 @@
}
}
if (card instanceof StackAbilityView) {
// replace ability by original card
CardView tmp = ((StackAbilityView) card).getSourceCard();
tmp.overrideRules(card.getRules());
tmp.setChoosable(card.isChoosable());
tmp.setPlayable(card.isPlayable());
tmp.setPlayableAmount(card.getPlayableAmount());
tmp.setSelected(card.isSelected());
tmp.setIsAbility(true);
tmp.overrideTargets(card.getTargets());
tmp.overrideId(card.getId());

View file

@ -112,8 +112,13 @@ public class StackDialog extends IDialogPanel {
for (CardView card : cards.values()) {
if (card instanceof StackAbilityView) {
// replace ability by original card
CardView tmp = ((StackAbilityView) card).getSourceCard();
tmp.overrideRules(card.getRules());
tmp.setChoosable(card.isChoosable());
tmp.setPlayable(card.isPlayable());
tmp.setPlayableAmount(card.getPlayableAmount());
tmp.setSelected(card.isSelected());
tmp.setIsAbility(true);
tmp.overrideTargets(card.getTargets());
tmp.overrideId(card.getId());

View file

@ -56,6 +56,21 @@
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- to get the reference to local repository with com\googlecode\jspf\jspf-core\0.9.1\ -->
<repositories>
@ -82,7 +97,10 @@
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
<finalName>mage-common</finalName>

View file

@ -0,0 +1,41 @@
package mage.utils;
import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* A base class for fluent, immutable, composable builders.
*
* @see <a href="https://github.com/fburato/functionalutils/blob/master/utils/src/main/java/com/github/fburato/functionalutils/utils/Builder.java">Builder</a>
*/
public abstract class FluentBuilder<ToBuild, RealBuilder extends FluentBuilder<ToBuild, RealBuilder>> {
final ArrayList<Consumer<RealBuilder>> buildSequence;
private final Supplier<RealBuilder> newReference;
protected FluentBuilder(Supplier<RealBuilder> newReference) {
this.buildSequence = new ArrayList<>();
this.newReference = newReference;
}
private RealBuilder copy() {
final RealBuilder realBuilder = newReference.get();
realBuilder.buildSequence.addAll(buildSequence);
return realBuilder;
}
protected abstract ToBuild makeValue();
public RealBuilder with(Consumer<RealBuilder> consumer) {
final RealBuilder nextBuilder = this.copy();
nextBuilder.buildSequence.add(consumer);
return nextBuilder;
}
public ToBuild build() {
final RealBuilder instance = this.copy();
instance.buildSequence.forEach(c -> c.accept(instance));
return instance.makeValue();
}
}

View file

@ -121,8 +121,17 @@ public class CardView extends SimpleCardView {
protected Card originalCard = null;
/**
* Non game usage like deck editor
*
* @param card
*/
public CardView(Card card) {
this(card, null, false);
this(card, (Game) null);
}
public CardView(Card card, Game game) {
this(card, game, false);
}
public CardView(Card card, SimpleCardView simpleCardView) {
@ -450,7 +459,7 @@ public class CardView extends SimpleCardView {
Card secondSideCard = card.getSecondCardFace();
if (secondSideCard != null) {
this.secondCardFace = new CardView(secondSideCard);
this.secondCardFace = new CardView(secondSideCard, game);
this.alternateName = secondCardFace.getName();
this.originalName = card.getName();
}
@ -464,7 +473,7 @@ public class CardView extends SimpleCardView {
if (card instanceof ModalDoubleFacesCard) {
this.transformable = true; // enable GUI day/night button
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card;
this.secondCardFace = new CardView(mdfCard.getRightHalfCard());
this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game);
this.alternateName = mdfCard.getRightHalfCard().getName();
this.originalName = card.getName();
}

View file

@ -6,7 +6,6 @@ import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.GameState;
import mage.game.command.Emblem;
import mage.game.command.Plane;
import mage.game.permanent.Permanent;
@ -29,7 +28,7 @@ public class CardsView extends LinkedHashMap<UUID, CardView> {
}
/**
* Uses for card render tests
* Non game usage like card render tests
*
* @param cardViews
*/
@ -39,6 +38,11 @@ public class CardsView extends LinkedHashMap<UUID, CardView> {
}
}
/**
* Non game usage like deck editor
*
* @param cards
*/
public CardsView(Collection<? extends Card> cards) {
for (Card card : cards) {
this.put(card.getId(), new CardView(card));
@ -116,9 +120,9 @@ public class CardsView extends LinkedHashMap<UUID, CardView> {
if (abilityView == null) {
CardView sourceCardView;
if (isPermanent) {
sourceCardView = new CardView((Permanent) sourceObject);
sourceCardView = new CardView((Permanent) sourceObject, game);
} else if (isCard) {
sourceCardView = new CardView((Card) sourceObject);
sourceCardView = new CardView((Card) sourceObject, game);
} else {
sourceCardView = new CardView(sourceObject, game);
}
@ -164,14 +168,4 @@ public class CardsView extends LinkedHashMap<UUID, CardView> {
+ abilities.stream().map(a -> a.getClass().getSimpleName() + " - " + a.getRule()).collect(Collectors.joining("\n")));
}
}
public CardsView(Collection<? extends Ability> abilities, GameState state) {
for (Ability ability : abilities) {
Card sourceCard = state.getPermanent(ability.getSourceId());
if (sourceCard != null) {
this.put(ability.getId(), new AbilityView(ability, sourceCard.getName(), new CardView(sourceCard)));
}
}
}
}

View file

@ -94,7 +94,7 @@ public class GameView implements Serializable {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), new CardView(card, game, false, false, false)));
}
} else {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, "", new CardView(card)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, "", new CardView(card, game)));
}
if (card.isTransformable()) {
updateLatestCardView(game, card, stackObject.getId());
@ -103,7 +103,7 @@ public class GameView implements Serializable {
} else if (object != null) {
if (object instanceof PermanentToken) {
PermanentToken token = (PermanentToken) object;
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, token.getName(), new CardView(token)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, token.getName(), new CardView(token, game)));
checkPaid(stackObject.getId(), (StackAbility) stackObject);
} else if (object instanceof Emblem) {
CardView cardView = new CardView(new EmblemView((Emblem) object));

View file

@ -1,9 +1,5 @@
package mage.view;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.TurnFaceUpAbility;
import mage.cards.Card;
@ -12,8 +8,11 @@ import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class PermanentView extends CardView {
@ -36,8 +35,8 @@ public class PermanentView extends CardView {
private final boolean attachedToPermanent;
public PermanentView(Permanent permanent, Card card, UUID createdForPlayerId, Game game) {
super(permanent, game, (permanent.getControllerId() == null) ? false : permanent.getControllerId().equals(createdForPlayerId));
this.controlled = (permanent.getControllerId() == null) ? false : permanent.getControllerId().equals(createdForPlayerId);
super(permanent, game, permanent.getControllerId() != null && permanent.getControllerId().equals(createdForPlayerId));
this.controlled = permanent.getControllerId() != null && permanent.getControllerId().equals(createdForPlayerId);
this.rules = permanent.getRules(game);
this.tapped = permanent.isTapped();
this.flipped = permanent.isFlipped();
@ -59,7 +58,7 @@ public class PermanentView extends CardView {
} else {
if (card != null) {
// original may not be face down
original = new CardView(card);
original = new CardView(card, game);
} else {
original = null;
}

View file

@ -1,13 +1,5 @@
package mage.view;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import mage.cards.Card;
import mage.counters.Counters;
import mage.designations.Designation;
@ -22,6 +14,9 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.players.net.UserData;
import java.io.Serializable;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -105,7 +100,7 @@ public class PlayerView implements Serializable {
}
Card cardOnTop = (player.isTopCardRevealed() && player.getLibrary().hasCards())
? player.getLibrary().getFromTop(game) : null;
this.topCard = cardOnTop != null ? new CardView(cardOnTop) : null;
this.topCard = cardOnTop != null ? new CardView(cardOnTop, game) : null;
if (player.getUserData() != null) {
this.userData = player.getUserData();
} else {

View file

@ -27,6 +27,8 @@ public class StackAbilityView extends CardView {
private static final long serialVersionUID = 1L;
// in GUI: that's view will be replaced by sourceCard, so don't forget to sync settings like
// selectable, chooseable, etc. Search by getSourceCard
private final CardView sourceCard;
public StackAbilityView(Game game, StackAbility ability, String sourceName, CardView sourceCard) {

View file

@ -0,0 +1,123 @@
package mage.remote;
import mage.utils.FluentBuilder;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.net.URI;
import static org.assertj.core.api.Assertions.assertThat;
public class ConnectionTest {
static class ConnectionBuilder extends FluentBuilder<Connection, ConnectionBuilder> {
public int port;
public String host;
public String parameter;
private ConnectionBuilder() {
super(ConnectionBuilder::new);
}
@Override
protected Connection makeValue() {
final Connection result = new Connection(parameter);
result.setHost(host);
result.setPort(port);
return result;
}
}
private ConnectionBuilder baseBuilder() {
return new ConnectionBuilder();
}
class TestsTemplate {
final ConnectionBuilder testeeBuilder;
TestsTemplate(ConnectionBuilder testeeBuilder) {
this.testeeBuilder = testeeBuilder;
}
@Test
@DisplayName("produce the expected scheme")
void scheme() throws Exception {
final URI testee = make(testeeBuilder);
assertThat(testee.getScheme()).isEqualTo("bisocket");
}
URI make(ConnectionBuilder builder) {
try {
return new URI(builder.build().getURI());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Test
@DisplayName("generate the expected port")
void port() {
final int expected = RandomUtils.nextInt(1000, 65000);
final int port = make(testeeBuilder.with(c -> c.port = expected)).getPort();
assertThat(port).isEqualTo(expected);
}
@Test
@DisplayName("generate the expected serialisation parameter")
void serialisation() {
final String query = make(testeeBuilder).getQuery();
assertThat(query).contains("serializationtype=jboss");
}
@Test
@DisplayName("generate the expected threadpool parameter")
void threadpool() {
final String parameter = RandomStringUtils.randomAlphanumeric(12);
final String query = make(testeeBuilder.with(c -> c.parameter = parameter)).getQuery();
assertThat(query).contains("onewayThreadPool=mage.remote.CustomThreadPool" + parameter);
}
}
@Nested
@DisplayName("getUri when host is localhost should")
class LocalhostTest extends TestsTemplate {
LocalhostTest() {
super(baseBuilder().with(c -> c.host = "localhost"));
}
@Test
@DisplayName("generate an ipv4 as host")
void ipv4Gen() {
final String host = make(testeeBuilder).getHost();
assertThat(host).matches("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+");
}
}
private final String randomHost = RandomStringUtils.randomAlphabetic(15);
@Nested
@DisplayName("getUri when host is not localhost should")
class StandardHostTest extends TestsTemplate {
StandardHostTest() {
super(baseBuilder().with(c -> c.host = randomHost));
}
@Test
@DisplayName("generate the selected host as host")
void hostGen() {
final String host = make(testeeBuilder).getHost();
assertThat(host).isEqualTo(randomHost);
}
}
}

View file

@ -0,0 +1,116 @@
package mage.utils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
public class FluentBuilderTest {
private ABuilder baseBuilder() {
return new ABuilder();
}
@Test
@DisplayName("build with default parameters")
void testDefault() {
final A actual = baseBuilder().build();
verifyAB(actual, null, 0);
}
private void verifyAB(A actual, String a, int b) {
assertThat(actual.getA()).isEqualTo(a);
assertThat(actual.getB()).isEqualTo(b);
}
@Test
@DisplayName("chain with clause and add new parameters")
void testBaseChain() {
final A actual = baseBuilder().with(a -> a.a = "hello").build();
verifyAB(actual, "hello", 0);
}
@Test
@DisplayName("chain multiple with clauses and add new parameters")
void testMultiChain() {
final A actual = baseBuilder().with(a -> a.a = "world").with(a -> a.b = 6).build();
verifyAB(actual, "world", 6);
}
@Test
@DisplayName("chain multiple with clauses and override latest writes")
void testMultiChainOverride() {
final A actual = baseBuilder().with(a -> a.a = "world").with(a -> a.b = 4).with(a -> a.a = "foobar").build();
verifyAB(actual, "foobar", 4);
}
@Test
@DisplayName("not mutate the state of previous builder in the chain")
void testImmutability() {
final ABuilder builder1 = baseBuilder().with(a -> a.a = "world");
final ABuilder builder2 = builder1.with(a -> {
a.a = "hello";
a.b = 42;
});
verifyAB(builder1.build(), "world", 0);
verifyAB(builder2.build(), "hello", 42);
}
@Test
@DisplayName("produce different objects")
void differentObjects() {
final ABuilder builder = baseBuilder().with(a -> {
a.a = "hello";
a.b = 42;
});
final A a1 = builder.build();
final A a2 = builder.build();
assertThat(a1).isNotSameAs(a2);
verifyAB(a1, "hello", 42);
verifyAB(a2, "hello", 42);
}
static class A {
public final String a;
private int b;
public A(String a) {
this.a = a;
}
public String getA() {
return a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
}
static class ABuilder extends FluentBuilder<A, ABuilder> {
public String a;
public int b;
private ABuilder() {
super(ABuilder::new);
}
@Override
protected A makeValue() {
final A result = new A(a);
result.setB(b);
return result;
}
}
}

View file

@ -1,5 +1,6 @@
package mage.player.ai;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.SpellAbility;
@ -29,6 +30,7 @@ import mage.player.ai.util.CombatInfo;
import mage.player.ai.util.CombatUtil;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetAmount;
import mage.target.TargetCard;
import mage.target.Targets;
import mage.util.RandomUtil;
@ -37,6 +39,7 @@ import org.apache.log4j.Logger;
import java.io.File;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
/**
* @author nantuko
@ -155,7 +158,13 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
boolean usedStack = false;
while (actions.peek() != null) {
Ability ability = actions.poll();
logger.info(new StringBuilder("===> Act [").append(game.getPlayer(playerId).getName()).append("] Action: ").append(ability.toString()).toString());
// log example: ===> Act [PlayerA] Action: Cast Blessings of Nature (target 1; target 2)
logger.info(new StringBuilder("===> Act [")
.append(game.getPlayer(playerId).getName())
.append("] Action: ")
.append(ability.toString())
.append(listTargets(game, ability.getTargets(), " (targeting %s)", ""))
.toString());
if (!ability.getTargets().isEmpty()) {
for (Target target : ability.getTargets()) {
for (UUID id : target.getTargets()) {
@ -506,7 +515,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
StringBuilder sb = new StringBuilder("Sim Prio [").append(depth).append("] #").append(counter)
.append(" <").append(val).append("> (").append(action)
.append(action.isModal() ? " Mode = " + action.getModes().getMode().toString() : "")
.append(listTargets(game, action.getTargets())).append(')')
.append(listTargets(game, action.getTargets(), " (targeting %s)", "")).append(')')
.append(logger.isTraceEnabled() ? " #" + newNode.hashCode() : "");
SimulationNode2 logNode = newNode;
while (logNode.getChildren() != null
@ -541,7 +550,11 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
if (depth == maxDepth) {
GameStateEvaluator2.PlayerEvaluateScore score = GameStateEvaluator2.evaluate(this.getId(), bestNode.game);
String scoreInfo = " [" + score.getPlayerInfoShort() + "-" + score.getOpponentInfoShort() + "]";
logger.info("Sim Prio [" + depth + "] -- Saved best node yet <" + bestNode.getScore() + scoreInfo + "> " + bestNode.getAbilities().toString());
String abilitiesInfo = bestNode.getAbilities()
.stream()
.map(a -> a.toString() + listTargets(game, a.getTargets(), " (targeting %s)", ""))
.collect(Collectors.joining("; "));
logger.info("Sim Prio [" + depth + "] -- Saved best node yet <" + bestNode.getScore() + scoreInfo + "> " + abilitiesInfo);
node.children.clear();
node.children.add(bestNode);
node.setScore(bestNode.getScore());
@ -1009,17 +1022,36 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
return suggestedActions.size();
}
protected String listTargets(Game game, Targets targets) {
StringBuilder sb = new StringBuilder();
if (targets != null) {
for (Target target : targets) {
sb.append('[').append(target.getTargetedName(game)).append(']');
}
if (sb.length() > 0) {
sb.insert(0, " targeting ");
/**
* Return info about targets list (targeting objects)
*
* @param game
* @param targets
* @param format example: my %s in data
* @param emptyText default text for empty targets list
* @return
*/
protected String listTargets(Game game, Targets targets, String format, String emptyText) {
List<String> res = new ArrayList<>();
for (Target target : targets) {
for (UUID id : target.getTargets()) {
MageObject object = game.getObject(id);
if (object != null) {
String prefix = "";
if (target instanceof TargetAmount) {
prefix = " " + target.getTargetAmount(id) + "x ";
}
res.add(prefix + object.getIdName());
}
}
}
return sb.toString();
String info = String.join("; ", res);
if (info.isEmpty()) {
return emptyText;
} else {
return String.format(format, info);
}
}
@Override

View file

@ -1130,7 +1130,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
log.warn("No proper AI target handling: " + target.getClass().getName());
// it's ok on no targets available
log.warn("No proper AI target handling or can't find permanents/cards to target: " + target.getClass().getName());
return false;
}

View file

@ -238,7 +238,7 @@
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.30.10</version>
<version>1.31.1</version>
<type>jar</type>
</dependency>
<dependency>
@ -256,7 +256,7 @@
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-jetty</artifactId>
<version>1.31.0</version>
<version>1.31.2</version>
<type>jar</type>
</dependency>
<dependency>
@ -285,6 +285,18 @@
<artifactId>sqlite-jdbc</artifactId>
<version>3.32.3.2</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -358,6 +370,10 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
<finalName>mage-server</finalName>

View file

@ -5,8 +5,8 @@ import mage.cards.repository.CardRepository;
import mage.game.Game;
import mage.server.exceptions.UserNotFoundException;
import mage.server.game.GameController;
import mage.server.game.GameManager;
import mage.server.game.GamesRoomManager;
import mage.server.managers.ChatManager;
import mage.server.managers.ManagerFactory;
import mage.server.util.SystemUtil;
import mage.view.ChatMessage.MessageColor;
import mage.view.ChatMessage.MessageType;
@ -26,21 +26,27 @@ import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
*/
public enum ChatManager {
public class ChatManagerImpl implements ChatManager {
instance;
private static final Logger logger = Logger.getLogger(ChatManager.class);
private static final Logger logger = Logger.getLogger(ChatManagerImpl.class);
private static final HashMap<String, String> userMessages = new HashMap<>();
private final ManagerFactory managerFactory;
private final ConcurrentHashMap<UUID, ChatSession> chatSessions = new ConcurrentHashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public ChatManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public UUID createChatSession(String info) {
ChatSession chatSession = new ChatSession(info);
ChatSession chatSession = new ChatSession(managerFactory, info);
chatSessions.put(chatSession.getChatId(), chatSession);
return chatSession.getChatId();
}
@Override
public void joinChat(UUID chatId, UUID userId) {
ChatSession chatSession = chatSessions.get(chatId);
if (chatSession != null) {
@ -51,10 +57,12 @@ public enum ChatManager {
}
@Override
public void clearUserMessageStorage() {
userMessages.clear();
}
@Override
public void leaveChat(UUID chatId, UUID userId) {
ChatSession chatSession = chatSessions.get(chatId);
if (chatSession != null && chatSession.hasUser(userId)) {
@ -62,6 +70,7 @@ public enum ChatManager {
}
}
@Override
public void destroyChatSession(UUID chatId) {
if (chatId != null) {
ChatSession chatSession = chatSessions.get(chatId);
@ -84,11 +93,12 @@ public enum ChatManager {
final Pattern cardNamePattern = Pattern.compile("\\[(.*?)\\]");
@Override
public void broadcast(UUID chatId, String userName, String message, MessageColor color, boolean withTime, Game game, MessageType messageType, SoundToPlay soundToPlay) {
ChatSession chatSession = chatSessions.get(chatId);
if (chatSession != null) {
if (message.startsWith("\\") || message.startsWith("/")) {
Optional<User> user = UserManager.instance.getUserByName(userName);
Optional<User> user = managerFactory.userManager().getUserByName(userName);
if (user.isPresent()) {
if (!performUserCommand(user.get(), message, chatId, false)) {
performUserCommand(user.get(), message, chatId, true);
@ -98,7 +108,7 @@ public enum ChatManager {
}
if (messageType != MessageType.GAME && !userName.isEmpty()) {
Optional<User> u = UserManager.instance.getUserByName(userName);
Optional<User> u = managerFactory.userManager().getUserByName(userName);
if (u.isPresent()) {
User user = u.get();
@ -184,12 +194,12 @@ public enum ChatManager {
}
if (command.startsWith("H ") || command.startsWith("HISTORY ")) {
message += "<br/>" + UserManager.instance.getUserHistory(message.substring(command.startsWith("H ") ? 3 : 9));
message += "<br/>" + managerFactory.userManager().getUserHistory(message.substring(command.startsWith("H ") ? 3 : 9));
chatSessions.get(chatId).broadcastInfoToUser(user, message);
return true;
}
if (command.equals("ME")) {
message += "<br/>" + UserManager.instance.getUserHistory(user.getName());
message += "<br/>" + managerFactory.userManager().getUserHistory(user.getName());
chatSessions.get(chatId).broadcastInfoToUser(user, message);
return true;
}
@ -200,7 +210,7 @@ public enum ChatManager {
String gameId = session.getInfo();
if (gameId.startsWith("Game ")) {
UUID id = java.util.UUID.fromString(gameId.substring(5));
for (Entry<UUID, GameController> entry : GameManager.instance.getGameController().entrySet()) {
for (Entry<UUID, GameController> entry : managerFactory.gameManager().getGameController().entrySet()) {
if (entry.getKey().equals(id)) {
GameController controller = entry.getValue();
if (controller != null) {
@ -221,7 +231,7 @@ public enum ChatManager {
String gameId = session.getInfo();
if (gameId.startsWith("Game ")) {
UUID id = java.util.UUID.fromString(gameId.substring(5));
for (Entry<UUID, GameController> entry : GameManager.instance.getGameController().entrySet()) {
for (Entry<UUID, GameController> entry : managerFactory.gameManager().getGameController().entrySet()) {
if (entry.getKey().equals(id)) {
GameController controller = entry.getValue();
if (controller != null) {
@ -242,7 +252,7 @@ public enum ChatManager {
String gameId = session.getInfo();
if (gameId.startsWith("Game ")) {
UUID id = java.util.UUID.fromString(gameId.substring(5));
for (Entry<UUID, GameController> entry : GameManager.instance.getGameController().entrySet()) {
for (Entry<UUID, GameController> entry : managerFactory.gameManager().getGameController().entrySet()) {
if (entry.getKey().equals(id)) {
GameController controller = entry.getValue();
if (controller != null) {
@ -281,7 +291,7 @@ public enum ChatManager {
if (first > 1) {
String userToName = rest.substring(0, first);
rest = rest.substring(first + 1).trim();
Optional<User> userTo = UserManager.instance.getUserByName(userToName);
Optional<User> userTo = managerFactory.userManager().getUserByName(userToName);
if (userTo.isPresent()) {
if (!chatSessions.get(chatId).broadcastWhisperToUser(user, userTo.get(), rest)) {
message += new StringBuilder("<br/>User ").append(userToName).append(" not found").toString();
@ -312,8 +322,9 @@ public enum ChatManager {
* @param color
* @throws mage.server.exceptions.UserNotFoundException
*/
@Override
public void broadcast(UUID userId, String message, MessageColor color) throws UserNotFoundException {
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
getChatSessions()
.stream()
.filter(chat -> chat.hasUser(userId))
@ -322,8 +333,9 @@ public enum ChatManager {
});
}
@Override
public void sendReconnectMessage(UUID userId) {
UserManager.instance.getUser(userId).ifPresent(user
managerFactory.userManager().getUser(userId).ifPresent(user
-> getChatSessions()
.stream()
.filter(chat -> chat.hasUser(userId))
@ -331,8 +343,9 @@ public enum ChatManager {
}
@Override
public void sendLostConnectionMessage(UUID userId, DisconnectReason reason) {
UserManager.instance.getUser(userId).ifPresent(user -> sendMessageToUserChats(userId, user.getName() + " " + reason.getMessage()));
managerFactory.userManager().getUser(userId).ifPresent(user -> sendMessageToUserChats(userId, user.getName() + " " + reason.getMessage()));
}
/**
@ -341,10 +354,11 @@ public enum ChatManager {
* @param userId
* @param message
*/
@Override
public void sendMessageToUserChats(UUID userId, String message) {
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
List<ChatSession> chatSessions = getChatSessions().stream()
.filter(chat -> !chat.getChatId().equals(GamesRoomManager.instance.getMainChatId())) // ignore main lobby
.filter(chat -> !chat.getChatId().equals(managerFactory.gamesRoomManager().getMainChatId())) // ignore main lobby
.filter(chat -> chat.hasUser(userId))
.collect(Collectors.toList());
@ -355,6 +369,7 @@ public enum ChatManager {
});
}
@Override
public void removeUser(UUID userId, DisconnectReason reason) {
for (ChatSession chatSession : getChatSessions()) {
if (chatSession.hasUser(userId)) {
@ -363,6 +378,7 @@ public enum ChatManager {
}
}
@Override
public List<ChatSession> getChatSessions() {
final Lock r = lock.readLock();
r.lock();

View file

@ -3,6 +3,7 @@ package mage.server;
import mage.game.Game;
import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.server.managers.ManagerFactory;
import mage.view.ChatMessage;
import mage.view.ChatMessage.MessageColor;
import mage.view.ChatMessage.MessageType;
@ -25,6 +26,7 @@ public class ChatSession {
private static final Logger logger = Logger.getLogger(ChatSession.class);
private static final DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT);
private final ManagerFactory managerFactory;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final ConcurrentMap<UUID, String> clients = new ConcurrentHashMap<>();
@ -32,14 +34,15 @@ public class ChatSession {
private final Date createTime;
private final String info;
public ChatSession(String info) {
public ChatSession(ManagerFactory managerFactory, String info) {
this.managerFactory = managerFactory;
chatId = UUID.randomUUID();
this.createTime = new Date();
this.info = info;
}
public void join(UUID userId) {
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
if (!clients.containsKey(userId)) {
String userName = user.getName();
final Lock w = lock.writeLock();
@ -121,7 +124,7 @@ public class ChatSession {
r.unlock();
}
for (UUID userId : chatUserIds) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (user.isPresent()) {
user.get().fireCallback(clientCallback);
} else {

View file

@ -1,91 +0,0 @@
package mage.server;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.Base64;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.Gmail.Builder;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.Message;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.Properties;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import mage.server.util.ConfigSettings;
import org.apache.log4j.Logger;
public final class GmailClient {
private static final Logger logger = Logger.getLogger(Main.class);
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final java.io.File DATA_STORE_DIR = new java.io.File(System.getProperty("user.home"), ".store/xmage");
private static FileDataStoreFactory dataStoreFactory;
private static HttpTransport httpTransport;
private static Credential credential;
public static boolean initilize() {
try {
dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new FileReader("client_secrets.json"));
if (clientSecrets.getDetails().getClientId().startsWith("Enter")
|| clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
logger.error("client_secrets.json not found");
return false;
}
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, JSON_FACTORY, clientSecrets,
Collections.singleton(GmailScopes.GMAIL_COMPOSE)).setDataStoreFactory(
dataStoreFactory).build();
credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
return true;
} catch (IOException | GeneralSecurityException ex) {
logger.error("Error initializing GmailClient", ex);
}
return false;
}
public static boolean sendMessage(String email, String subject, String text) {
if (email.isEmpty()) {
logger.info("Email is not sent because the address is empty");
return false;
}
try {
Gmail gmail = new Builder(httpTransport, JSON_FACTORY, credential).setApplicationName("XMage Server").build();
MimeMessage mimeMessage = new MimeMessage(Session.getDefaultInstance(new Properties()));
mimeMessage.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(email));
mimeMessage.setSubject(subject);
mimeMessage.setText(text);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mimeMessage.writeTo(baos);
Message message = new Message();
message.setRaw(Base64.encodeBase64URLSafeString(baos.toByteArray()));
gmail.users().messages().send(ConfigSettings.instance.getGoogleAccount()
+ (ConfigSettings.instance.getGoogleAccount().endsWith("@gmail.com") ? "" : "@gmail.com"), message).execute();
return true;
} catch (MessagingException | IOException ex) {
logger.error("Error sending message", ex);
}
return false;
}
}

View file

@ -23,15 +23,14 @@ import mage.players.PlayerType;
import mage.players.net.UserData;
import mage.remote.MageVersionException;
import mage.server.draft.CubeFactory;
import mage.server.draft.DraftManager;
import mage.server.game.*;
import mage.server.game.GameFactory;
import mage.server.game.GamesRoom;
import mage.server.game.PlayerFactory;
import mage.server.managers.ManagerFactory;
import mage.server.services.impl.FeedbackServiceImpl;
import mage.server.tournament.TournamentFactory;
import mage.server.tournament.TournamentManager;
import mage.server.util.ConfigSettings;
import mage.server.util.ServerMessagesUtil;
import mage.server.util.SystemUtil;
import mage.server.util.ThreadExecutor;
import mage.utils.*;
import mage.view.*;
import mage.view.ChatMessage.MessageColor;
@ -49,9 +48,10 @@ import java.util.concurrent.ExecutorService;
public class MageServerImpl implements MageServer {
private static final Logger logger = Logger.getLogger(MageServerImpl.class);
private static final ExecutorService callExecutor = ThreadExecutor.instance.getCallExecutor();
private final ExecutorService callExecutor;
private static final SecureRandom RANDOM = new SecureRandom();
private final ManagerFactory managerFactory;
private final String adminPassword;
private final boolean testMode;
private final LinkedHashMap<String, String> activeAuthTokens = new LinkedHashMap<String, String>() {
@ -62,15 +62,17 @@ public class MageServerImpl implements MageServer {
}
};
public MageServerImpl(String adminPassword, boolean testMode) {
public MageServerImpl(ManagerFactory managerFactory, String adminPassword, boolean testMode) {
this.managerFactory = managerFactory;
this.adminPassword = adminPassword;
this.testMode = testMode;
this.callExecutor = managerFactory.threadExecutor().getCallExecutor();
ServerMessagesUtil.instance.getMessages();
}
@Override
public boolean registerUser(String sessionId, String userName, String password, String email) throws MageException {
return SessionManager.instance.registerUser(sessionId, userName, password, email);
return managerFactory.sessionManager().registerUser(sessionId, userName, password, email);
}
// generateAuthToken returns a uniformly distributed 6-digits string.
@ -80,7 +82,7 @@ public class MageServerImpl implements MageServer {
@Override
public boolean emailAuthToken(String sessionId, String email) throws MageException {
if (!ConfigSettings.instance.isAuthenticationActivated()) {
if (!managerFactory.configSettings().isAuthenticationActivated()) {
sendErrorMessageToClient(sessionId, "Registration is disabled by the server config");
return false;
}
@ -96,10 +98,10 @@ public class MageServerImpl implements MageServer {
String text = "Use this auth token to reset " + authorizedUser.name + "'s password: " + authToken + '\n'
+ "It's valid until the next server restart.";
boolean success;
if (!ConfigSettings.instance.getMailUser().isEmpty()) {
success = MailClient.sendMessage(email, subject, text);
if (!managerFactory.configSettings().getMailUser().isEmpty()) {
success = managerFactory.mailClient().sendMessage(email, subject, text);
} else {
success = MailgunClient.sendMessage(email, subject, text);
success = managerFactory.mailgunClient().sendMessage(email, subject, text);
}
if (!success) {
sendErrorMessageToClient(sessionId, "There was an error inside the server while emailing an auth token");
@ -110,7 +112,7 @@ public class MageServerImpl implements MageServer {
@Override
public boolean resetPassword(String sessionId, String email, String authToken, String password) throws MageException {
if (!ConfigSettings.instance.isAuthenticationActivated()) {
if (!managerFactory.configSettings().isAuthenticationActivated()) {
sendErrorMessageToClient(sessionId, "Registration is disabled by the server config");
return false;
}
@ -139,7 +141,7 @@ public class MageServerImpl implements MageServer {
logger.info("MageVersionException: userName=" + userName + ", version=" + version + " sessionId=" + sessionId);
throw new MageVersionException(version, Main.getVersion());
}
return SessionManager.instance.connectUser(sessionId, userName, password, userIdStr);
return managerFactory.sessionManager().connectUser(sessionId, userName, password, userIdStr);
} catch (MageException ex) {
if (ex instanceof MageVersionException) {
throw ex;
@ -154,7 +156,7 @@ public class MageServerImpl implements MageServer {
return executeWithResult("setUserData", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
return SessionManager.instance.setUserData(userName, sessionId, userData, clientVersion, userIdStr);
return managerFactory.sessionManager().setUserData(userName, sessionId, userData, clientVersion, userIdStr);
}
});
}
@ -168,7 +170,7 @@ public class MageServerImpl implements MageServer {
if (!adminPassword.equals(this.adminPassword)) {
throw new MageException("Wrong password");
}
return SessionManager.instance.connectAdmin(sessionId);
return managerFactory.sessionManager().connectAdmin(sessionId);
} catch (Exception ex) {
handleException(ex);
}
@ -186,13 +188,13 @@ public class MageServerImpl implements MageServer {
@Override
public TableView execute() throws MageException {
try {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session to found : " + sessionId);
return null;
}
UUID userId = session.get().getUserId();
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (!_user.isPresent()) {
logger.error("User for session not found. session = " + sessionId);
return null;
@ -205,7 +207,7 @@ public class MageServerImpl implements MageServer {
throw new MageException("No message");
}
// check AI players max
String maxAiOpponents = ConfigSettings.instance.getMaxAiOpponents();
String maxAiOpponents = managerFactory.configSettings().getMaxAiOpponents();
if (maxAiOpponents != null) {
int aiPlayers = 0;
for (PlayerType playerType : options.getPlayerTypes()) {
@ -241,7 +243,7 @@ public class MageServerImpl implements MageServer {
user.showUserMessage("Create tournament", message);
throw new MageException("No message");
}
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
if (!room.isPresent()) {
} else {
@ -260,9 +262,9 @@ public class MageServerImpl implements MageServer {
@Override
public void removeTable(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
execute("removeTable", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
TableManager.instance.removeTable(userId, tableId);
managerFactory.tableManager().removeTable(userId, tableId);
});
});
}
@ -272,7 +274,7 @@ public class MageServerImpl implements MageServer {
return executeWithResult("joinTable", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
return false;
}
@ -282,7 +284,7 @@ public class MageServerImpl implements MageServer {
logger.fatal("Got no userId from sessionId" + sessionId + " tableId" + tableId);
return false;
}
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
if (!room.isPresent()) {
return false;
}
@ -297,20 +299,20 @@ public class MageServerImpl implements MageServer {
return executeWithResult("joinTournamentTable", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
return false;
}
UUID userId = session.get().getUserId();
if (logger.isTraceEnabled()) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
user.ifPresent(user1 -> logger.trace("join tourn. tableId: " + tableId + ' ' + name));
}
if (userId == null) {
logger.fatal("Got no userId from sessionId" + sessionId + " tableId" + tableId);
return false;
}
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
if (room.isPresent()) {
return room.get().joinTournamentTable(userId, tableId, name, playerType, skill, deckList, password);
}
@ -325,12 +327,12 @@ public class MageServerImpl implements MageServer {
return executeWithResult("submitDeck", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
return false;
} else {
UUID userId = session.get().getUserId();
boolean ret = TableManager.instance.submitDeck(userId, tableId, deckList);
boolean ret = managerFactory.tableManager().submitDeck(userId, tableId, deckList);
logger.debug("Session " + sessionId + " submitted deck");
return ret;
}
@ -341,13 +343,13 @@ public class MageServerImpl implements MageServer {
@Override
public void updateDeck(final String sessionId, final UUID tableId, final DeckCardLists deckList) throws MageException {
execute("updateDeck", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.updateDeck(userId, tableId, deckList);
managerFactory.tableManager().updateDeck(userId, tableId, deckList);
logger.trace("Session " + sessionId + " updated deck");
}
});
@ -357,7 +359,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public List<TableView> getTables(UUID roomId) throws MageException {
try {
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
if (room.isPresent()) {
return room.get().getTables();
} else {
@ -373,7 +375,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public List<MatchView> getFinishedMatches(UUID roomId) throws MageException {
try {
return GamesRoomManager.instance.getRoom(roomId).map(GamesRoom::getFinished).orElse(new ArrayList<>());
return managerFactory.gamesRoomManager().getRoom(roomId).map(GamesRoom::getFinished).orElse(new ArrayList<>());
} catch (Exception ex) {
handleException(ex);
}
@ -383,7 +385,7 @@ public class MageServerImpl implements MageServer {
@Override
public List<RoomUsersView> getRoomUsers(UUID roomId) throws MageException {
try {
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
if (room.isPresent()) {
return room.get().getRoomUsersInfo();
} else {
@ -399,7 +401,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public TableView getTable(UUID roomId, UUID tableId) throws MageException {
try {
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
return room.flatMap(r -> r.getTable(tableId)).orElse(null);
} catch (Exception ex) {
@ -410,22 +412,12 @@ public class MageServerImpl implements MageServer {
@Override
public boolean ping(String sessionId, String pingInfo) {
return SessionManager.instance.extendUserSession(sessionId, pingInfo);
return managerFactory.sessionManager().extendUserSession(sessionId, pingInfo);
}
// @Override
// public void deregisterClient(final String sessionId) throws MageException {
// execute("deregisterClient", sessionId, new Action() {
// @Override
// public void execute() {
// SessionManager.instance.disconnect(sessionId, true);
// logger.debug("Client deregistered ...");
// }
// });
// }
@Override
public boolean startMatch(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
Optional<TableController> controller = TableManager.instance.getController(tableId);
Optional<TableController> controller = managerFactory.tableManager().getController(tableId);
if (!controller.isPresent()) {
logger.error("table not found : " + tableId);
return false;
@ -434,30 +426,20 @@ public class MageServerImpl implements MageServer {
return false;
}
execute("startMatch", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.startMatch(userId, roomId, tableId);
managerFactory.tableManager().startMatch(userId, roomId, tableId);
}
});
return true;
}
// @Override
// public void startChallenge(final String sessionId, final UUID roomId, final UUID tableId, final UUID challengeId) throws MageException {
// execute("startChallenge", sessionId, new Action() {
// @Override
// public void execute() {
// UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
// TableManager.instance.startChallenge(userId, roomId, tableId, challengeId);
// }
// });
// }
@Override
public boolean startTournament(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
Optional<TableController> controller = TableManager.instance.getController(tableId);
Optional<TableController> controller = managerFactory.tableManager().getController(tableId);
if (!controller.isPresent()) {
logger.error("table not found : " + tableId);
return false;
@ -466,12 +448,12 @@ public class MageServerImpl implements MageServer {
return false;
}
execute("startTournament", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.startTournament(userId, roomId, tableId);
managerFactory.tableManager().startTournament(userId, roomId, tableId);
}
});
return true;
@ -481,7 +463,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public TournamentView getTournament(UUID tournamentId) throws MageException {
try {
return TournamentManager.instance.getTournamentView(tournamentId);
return managerFactory.tournamentManager().getTournamentView(tournamentId);
} catch (Exception ex) {
handleException(ex);
}
@ -493,7 +475,7 @@ public class MageServerImpl implements MageServer {
public void sendChatMessage(final UUID chatId, final String userName, final String message) throws MageException {
try {
callExecutor.execute(
() -> ChatManager.instance.broadcast(chatId, userName, StringEscapeUtils.escapeHtml4(message), MessageColor.BLUE, true, null, ChatMessage.MessageType.TALK, null)
() -> managerFactory.chatManager().broadcast(chatId, userName, StringEscapeUtils.escapeHtml4(message), MessageColor.BLUE, true, null, ChatMessage.MessageType.TALK, null)
);
} catch (Exception ex) {
handleException(ex);
@ -503,10 +485,10 @@ public class MageServerImpl implements MageServer {
@Override
public void joinChat(final UUID chatId, final String sessionId, final String userName) throws MageException {
execute("joinChat", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
ChatManager.instance.joinChat(chatId, userId);
managerFactory.chatManager().joinChat(chatId, userId);
});
});
}
@ -515,9 +497,9 @@ public class MageServerImpl implements MageServer {
public void leaveChat(final UUID chatId, final String sessionId) throws MageException {
execute("leaveChat", sessionId, () -> {
if (chatId != null) {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
ChatManager.instance.leaveChat(chatId, userId);
managerFactory.chatManager().leaveChat(chatId, userId);
});
}
});
@ -527,7 +509,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public UUID getMainRoomId() throws MageException {
try {
return GamesRoomManager.instance.getMainRoomId();
return managerFactory.gamesRoomManager().getMainRoomId();
} catch (Exception ex) {
handleException(ex);
}
@ -538,7 +520,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public UUID getRoomChatId(UUID roomId) throws MageException {
try {
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
if (!room.isPresent()) {
logger.error("roomId not found : " + roomId);
return null;
@ -555,12 +537,12 @@ public class MageServerImpl implements MageServer {
return executeWithResult("isTableOwner", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
return false;
} else {
UUID userId = session.get().getUserId();
return TableManager.instance.isTableOwner(tableId, userId);
return managerFactory.tableManager().isTableOwner(tableId, userId);
}
}
});
@ -569,16 +551,16 @@ public class MageServerImpl implements MageServer {
@Override
public void swapSeats(final String sessionId, final UUID roomId, final UUID tableId, final int seatNum1, final int seatNum2) throws MageException {
execute("swapSeats", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
TableManager.instance.swapSeats(tableId, userId, seatNum1, seatNum2);
managerFactory.tableManager().swapSeats(tableId, userId, seatNum1, seatNum2);
});
});
}
@Override
public boolean leaveTable(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
Optional<TableController> tableController = TableManager.instance.getController(tableId);
Optional<TableController> tableController = managerFactory.tableManager().getController(tableId);
if (tableController.isPresent()) {
TableState tableState = tableController.get().getTableState();
if (tableState != TableState.WAITING && tableState != TableState.READY_TO_START) {
@ -586,9 +568,9 @@ public class MageServerImpl implements MageServer {
return false;
}
execute("leaveTable", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
GamesRoomManager.instance.getRoom(roomId).ifPresent(room ->
managerFactory.gamesRoomManager().getRoom(roomId).ifPresent(room ->
room.leaveTable(userId, tableId));
});
@ -604,7 +586,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public UUID getTableChatId(UUID tableId) throws MageException {
try {
return TableManager.instance.getChatId(tableId).orElse(null);
return managerFactory.tableManager().getChatId(tableId).orElse(null);
} catch (Exception ex) {
handleException(ex);
}
@ -614,9 +596,9 @@ public class MageServerImpl implements MageServer {
@Override
public void joinGame(final UUID gameId, final String sessionId) throws MageException {
execute("joinGame", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
GameManager.instance.joinGame(gameId, userId);
managerFactory.gameManager().joinGame(gameId, userId);
});
});
}
@ -624,9 +606,9 @@ public class MageServerImpl implements MageServer {
@Override
public void joinDraft(final UUID draftId, final String sessionId) throws MageException {
execute("joinDraft", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
DraftManager.instance.joinDraft(draftId, userId);
managerFactory.draftManager().joinDraft(draftId, userId);
});
});
}
@ -634,12 +616,12 @@ public class MageServerImpl implements MageServer {
@Override
public void joinTournament(final UUID tournamentId, final String sessionId) throws MageException {
execute("joinTournament", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TournamentManager.instance.joinTournament(tournamentId, userId);
managerFactory.tournamentManager().joinTournament(tournamentId, userId);
}
});
}
@ -648,7 +630,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public UUID getGameChatId(UUID gameId) throws MageException {
try {
return GameManager.instance.getChatId(gameId).orElse(null);
return managerFactory.gameManager().getChatId(gameId).orElse(null);
} catch (Exception ex) {
handleException(ex);
}
@ -659,7 +641,7 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public UUID getTournamentChatId(UUID tournamentId) throws MageException {
try {
return TournamentManager.instance.getChatId(tournamentId).orElse(null);
return managerFactory.tournamentManager().getChatId(tournamentId).orElse(null);
} catch (Exception ex) {
handleException(ex);
}
@ -669,9 +651,8 @@ public class MageServerImpl implements MageServer {
@Override
public void sendPlayerUUID(final UUID gameId, final String sessionId, final UUID data) throws MageException {
execute("sendPlayerUUID", sessionId, () -> {
Optional<User> user = SessionManager.instance.getUser(sessionId);
Optional<User> user = managerFactory.sessionManager().getUser(sessionId);
if (user.isPresent()) {
// logger.warn("sendPlayerUUID gameId=" + gameId + " sessionId=" + sessionId + " username=" + user.getName());
user.get().sendPlayerUUID(gameId, data);
} else {
logger.warn("Your session expired: gameId=" + gameId + ", sessionId=" + sessionId);
@ -682,7 +663,7 @@ public class MageServerImpl implements MageServer {
@Override
public void sendPlayerString(final UUID gameId, final String sessionId, final String data) throws MageException {
execute("sendPlayerString", sessionId, () -> {
Optional<User> user = SessionManager.instance.getUser(sessionId);
Optional<User> user = managerFactory.sessionManager().getUser(sessionId);
if (user.isPresent()) {
user.get().sendPlayerString(gameId, data);
} else {
@ -694,7 +675,7 @@ public class MageServerImpl implements MageServer {
@Override
public void sendPlayerManaType(final UUID gameId, final UUID playerId, final String sessionId, final ManaType data) throws MageException {
execute("sendPlayerManaType", sessionId, () -> {
Optional<User> user = SessionManager.instance.getUser(sessionId);
Optional<User> user = managerFactory.sessionManager().getUser(sessionId);
if (user.isPresent()) {
user.get().sendPlayerManaType(gameId, playerId, data);
} else {
@ -706,7 +687,7 @@ public class MageServerImpl implements MageServer {
@Override
public void sendPlayerBoolean(final UUID gameId, final String sessionId, final Boolean data) throws MageException {
execute("sendPlayerBoolean", sessionId, () -> {
Optional<User> user = SessionManager.instance.getUser(sessionId);
Optional<User> user = managerFactory.sessionManager().getUser(sessionId);
if (user.isPresent()) {
user.get().sendPlayerBoolean(gameId, data);
} else {
@ -718,7 +699,7 @@ public class MageServerImpl implements MageServer {
@Override
public void sendPlayerInteger(final UUID gameId, final String sessionId, final Integer data) throws MageException {
execute("sendPlayerInteger", sessionId, () -> {
Optional<User> user = SessionManager.instance.getUser(sessionId);
Optional<User> user = managerFactory.sessionManager().getUser(sessionId);
if (user.isPresent()) {
user.get().sendPlayerInteger(gameId, data);
} else {
@ -735,9 +716,9 @@ public class MageServerImpl implements MageServer {
@Override
public void sendCardMark(final UUID draftId, final String sessionId, final UUID cardPick) throws MageException {
execute("sendCardMark", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
DraftManager.instance.sendCardMark(draftId, userId, cardPick);
managerFactory.draftManager().sendCardMark(draftId, userId, cardPick);
});
});
}
@ -748,9 +729,9 @@ public class MageServerImpl implements MageServer {
try {
callExecutor.execute(
() -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
GameManager.instance.quitMatch(gameId, userId);
managerFactory.gameManager().quitMatch(gameId, userId);
});
}
);
@ -766,10 +747,10 @@ public class MageServerImpl implements MageServer {
try {
callExecutor.execute(
() -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
TournamentManager.instance.quit(tournamentId, userId);
managerFactory.tournamentManager().quit(tournamentId, userId);
});
}
);
@ -786,14 +767,14 @@ public class MageServerImpl implements MageServer {
try {
callExecutor.execute(
() -> {
SessionManager.instance.getSession(sessionId).ifPresent(
managerFactory.sessionManager().getSession(sessionId).ifPresent(
session -> {
UUID userId = session.getUserId();
UUID tableId = DraftManager.instance.getControllerByDraftId(draftId).getTableId();
Table table = TableManager.instance.getTable(tableId);
UUID tableId = managerFactory.draftManager().getControllerByDraftId(draftId).getTableId();
Table table = managerFactory.tableManager().getTable(tableId);
if (table.isTournament()) {
UUID tournamentId = table.getTournament().getId();
TournamentManager.instance.quit(tournamentId, userId);
managerFactory.tournamentManager().quit(tournamentId, userId);
}
});
}
@ -808,9 +789,9 @@ public class MageServerImpl implements MageServer {
@Override
public void sendPlayerAction(final PlayerAction playerAction, final UUID gameId, final String sessionId, final Object data) throws MageException {
execute("sendPlayerAction", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
GameManager.instance.sendPlayerAction(playerAction, gameId, userId, data);
managerFactory.gameManager().sendPlayerAction(playerAction, gameId, userId, data);
});
});
}
@ -820,14 +801,14 @@ public class MageServerImpl implements MageServer {
return executeWithResult("setUserData", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return false;
} else {
UUID userId = session.get().getUserId();
if (GamesRoomManager.instance.getRoom(roomId).isPresent()) {
return GamesRoomManager.instance.getRoom(roomId).get().watchTable(userId, tableId);
if (managerFactory.gamesRoomManager().getRoom(roomId).isPresent()) {
return managerFactory.gamesRoomManager().getRoom(roomId).get().watchTable(userId, tableId);
} else {
return false;
}
@ -846,10 +827,10 @@ public class MageServerImpl implements MageServer {
return executeWithResult("watchGame", sessionId, new ActionWithResult<Boolean>() {
@Override
public Boolean execute() throws MageException {
return SessionManager.instance.getSession(sessionId)
return managerFactory.sessionManager().getSession(sessionId)
.map(session -> {
UUID userId = session.getUserId();
return GameManager.instance.watchGame(gameId, userId);
return managerFactory.gameManager().watchGame(gameId, userId);
}).orElse(false);
}
@ -863,10 +844,10 @@ public class MageServerImpl implements MageServer {
@Override
public void stopWatching(final UUID gameId, final String sessionId) throws MageException {
execute("stopWatching", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
UserManager.instance.getUser(userId).ifPresent(user -> {
GameManager.instance.stopWatching(gameId, userId);
managerFactory.userManager().getUser(userId).ifPresent(user -> {
managerFactory.gameManager().stopWatching(gameId, userId);
user.removeGameWatchInfo(gameId);
});
});
@ -877,9 +858,9 @@ public class MageServerImpl implements MageServer {
@Override
public void replayGame(final UUID gameId, final String sessionId) throws MageException {
execute("replayGame", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
ReplayManager.instance.replayGame(gameId, userId);
managerFactory.replayManager().replayGame(gameId, userId);
});
});
}
@ -887,9 +868,9 @@ public class MageServerImpl implements MageServer {
@Override
public void startReplay(final UUID gameId, final String sessionId) throws MageException {
execute("startReplay", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
ReplayManager.instance.startReplay(gameId, userId);
managerFactory.replayManager().startReplay(gameId, userId);
});
});
}
@ -897,12 +878,12 @@ public class MageServerImpl implements MageServer {
@Override
public void stopReplay(final UUID gameId, final String sessionId) throws MageException {
execute("stopReplay", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.stopReplay(gameId, userId);
managerFactory.replayManager().stopReplay(gameId, userId);
}
});
}
@ -910,12 +891,12 @@ public class MageServerImpl implements MageServer {
@Override
public void nextPlay(final UUID gameId, final String sessionId) throws MageException {
execute("nextPlay", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.nextPlay(gameId, userId);
managerFactory.replayManager().nextPlay(gameId, userId);
}
});
}
@ -923,12 +904,12 @@ public class MageServerImpl implements MageServer {
@Override
public void previousPlay(final UUID gameId, final String sessionId) throws MageException {
execute("previousPlay", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.previousPlay(gameId, userId);
managerFactory.replayManager().previousPlay(gameId, userId);
}
});
}
@ -936,12 +917,12 @@ public class MageServerImpl implements MageServer {
@Override
public void skipForward(final UUID gameId, final String sessionId, final int moves) throws MageException {
execute("skipForward", sessionId, () -> {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.skipForward(gameId, userId, moves);
managerFactory.replayManager().skipForward(gameId, userId, moves);
}
});
}
@ -971,9 +952,9 @@ public class MageServerImpl implements MageServer {
public void cheat(final UUID gameId, final String sessionId, final UUID playerId, final DeckCardLists deckList) throws MageException {
execute("cheat", sessionId, () -> {
if (testMode) {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
GameManager.instance.cheat(gameId, userId, playerId, deckList);
managerFactory.gameManager().cheat(gameId, userId, playerId, deckList);
});
}
});
@ -985,12 +966,12 @@ public class MageServerImpl implements MageServer {
@Override
public Boolean execute() {
if (testMode) {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
return GameManager.instance.cheat(gameId, userId, playerId, cardName);
return managerFactory.gameManager().cheat(gameId, userId, playerId, cardName);
}
}
return false;
@ -1024,13 +1005,13 @@ public class MageServerImpl implements MageServer {
@Override
public void disconnectUser(final String sessionId, final String userSessionId) throws MageException {
execute("disconnectUser", sessionId, () -> SessionManager.instance.disconnectUser(sessionId, userSessionId));
execute("disconnectUser", sessionId, () -> managerFactory.sessionManager().disconnectUser(sessionId, userSessionId));
}
@Override
public void muteUser(final String sessionId, final String userName, final long durationMinutes) throws MageException {
execute("muteUser", sessionId, () -> {
UserManager.instance.getUserByName(userName).ifPresent(user -> {
managerFactory.userManager().getUserByName(userName).ifPresent(user -> {
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.setChatLockedUntil(muteUntil);
@ -1042,12 +1023,12 @@ public class MageServerImpl implements MageServer {
@Override
public void lockUser(final String sessionId, final String userName, final long durationMinutes) throws MageException {
execute("lockUser", sessionId, () -> {
UserManager.instance.getUserByName(userName).ifPresent(user -> {
managerFactory.userManager().getUserByName(userName).ifPresent(user -> {
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.setLockedUntil(lockUntil);
if (user.isConnected()) {
SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
managerFactory.sessionManager().disconnectUser(sessionId, user.getSessionId());
}
});
@ -1058,15 +1039,15 @@ public class MageServerImpl implements MageServer {
public void setActivation(final String sessionId, final String userName, boolean active) throws MageException {
execute("setActivation", sessionId, () -> {
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
Optional<User> u = UserManager.instance.getUserByName(userName);
Optional<User> u = managerFactory.userManager().getUserByName(userName);
if (u.isPresent()) {
User user = u.get();
user.setActive(active);
if (!user.isActive() && user.isConnected()) {
SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
managerFactory.sessionManager().disconnectUser(sessionId, user.getSessionId());
}
} else if (authorizedUser != null) {
User theUser = new User(userName, "localhost", authorizedUser);
User theUser = new User(managerFactory, userName, "localhost", authorizedUser);
theUser.setActive(active);
}
@ -1076,18 +1057,18 @@ public class MageServerImpl implements MageServer {
@Override
public void toggleActivation(final String sessionId, final String userName) throws MageException {
execute("toggleActivation", sessionId, ()
-> UserManager.instance.getUserByName(userName).ifPresent(user
-> managerFactory.userManager().getUserByName(userName).ifPresent(user
-> {
user.setActive(!user.isActive());
if (!user.isActive() && user.isConnected()) {
SessionManager.instance.disconnectUser(sessionId, user.getSessionId());
managerFactory.sessionManager().disconnectUser(sessionId, user.getSessionId());
}
}));
}
@Override
public void endUserSession(final String sessionId, final String userSessionId) throws MageException {
execute("endUserSession", sessionId, () -> SessionManager.instance.endUserSession(sessionId, userSessionId));
execute("endUserSession", sessionId, () -> managerFactory.sessionManager().endUserSession(sessionId, userSessionId));
}
/**
@ -1100,9 +1081,9 @@ public class MageServerImpl implements MageServer {
@Override
public void removeTable(final String sessionId, final UUID tableId) throws MageException {
execute("removeTable", sessionId, () -> {
SessionManager.instance.getSession(sessionId).ifPresent(session -> {
managerFactory.sessionManager().getSession(sessionId).ifPresent(session -> {
UUID userId = session.getUserId();
TableManager.instance.removeTable(userId, tableId);
managerFactory.tableManager().removeTable(userId, tableId);
});
});
}
@ -1116,7 +1097,7 @@ public class MageServerImpl implements MageServer {
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) {
execute("sendFeedbackMessage", sessionId, ()
-> SessionManager.instance.getSession(sessionId).ifPresent(
-> managerFactory.sessionManager().getSession(sessionId).ifPresent(
session -> FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.getHost())
));
}
@ -1126,7 +1107,7 @@ public class MageServerImpl implements MageServer {
public void sendBroadcastMessage(final String sessionId, final String message) throws MageException {
if (message != null) {
execute("sendBroadcastMessage", sessionId, () -> {
for (User user : UserManager.instance.getUsers()) {
for (User user : managerFactory.userManager().getUsers()) {
if (message.toLowerCase(Locale.ENGLISH).startsWith("warn")) {
user.fireCallback(new ClientCallback(ClientCallbackMethod.SERVER_MESSAGE, null, new ChatMessage("SERVER", message, null, null, MessageColor.RED)));
} else {
@ -1138,12 +1119,12 @@ public class MageServerImpl implements MageServer {
}
private void sendErrorMessageToClient(final String sessionId, final String message) throws MageException {
execute("sendErrorMessageToClient", sessionId, () -> SessionManager.instance.sendErrorMessageToClient(sessionId, message));
execute("sendErrorMessageToClient", sessionId, () -> managerFactory.sessionManager().sendErrorMessageToClient(sessionId, message));
}
protected void execute(final String actionName, final String sessionId, final Action action, boolean checkAdminRights) throws MageException {
if (checkAdminRights) {
if (!SessionManager.instance.isAdmin(sessionId)) {
if (!managerFactory.sessionManager().isAdmin(sessionId)) {
return;
}
}
@ -1151,11 +1132,11 @@ public class MageServerImpl implements MageServer {
}
protected void execute(final String actionName, final String sessionId, final Action action) throws MageException {
if (SessionManager.instance.isValidSession(sessionId)) {
if (managerFactory.sessionManager().isValidSession(sessionId)) {
try {
callExecutor.execute(
() -> {
if (SessionManager.instance.isValidSession(sessionId)) {
if (managerFactory.sessionManager().isValidSession(sessionId)) {
try {
action.execute();
} catch (MageException me) {
@ -1172,7 +1153,7 @@ public class MageServerImpl implements MageServer {
protected <T> T executeWithResult(String actionName, final String sessionId, final ActionWithResult<T> action, boolean checkAdminRights) throws MageException {
if (checkAdminRights) {
if (!SessionManager.instance.isAdmin(sessionId)) {
if (!managerFactory.sessionManager().isAdmin(sessionId)) {
return action.negativeResult();
}
}
@ -1181,7 +1162,7 @@ public class MageServerImpl implements MageServer {
//TODO: also run in threads with future task
protected <T> T executeWithResult(String actionName, final String sessionId, final ActionWithResult<T> action) throws MageException {
if (SessionManager.instance.isValidSession(sessionId)) {
if (managerFactory.sessionManager().isValidSession(sessionId)) {
try {
return action.execute();
} catch (Exception ex) {
@ -1218,15 +1199,15 @@ public class MageServerImpl implements MageServer {
}
}
private static class ListActionWithNullNegativeResult extends ActionWithNullNegativeResult<List<UserView>> {
private class ListActionWithNullNegativeResult extends ActionWithNullNegativeResult<List<UserView>> {
@Override
public List<UserView> execute() throws MageException {
return UserManager.instance.getUserInfoList();
return managerFactory.userManager().getUserInfoList();
}
}
private static class GameViewActionWithNullNegativeResult extends ActionWithNullNegativeResult<GameView> {
private class GameViewActionWithNullNegativeResult extends ActionWithNullNegativeResult<GameView> {
private final String sessionId;
private final UUID gameId;
@ -1240,18 +1221,18 @@ public class MageServerImpl implements MageServer {
@Override
public GameView execute() throws MageException {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return null;
} else {
//UUID userId = session.get().getUserId();
return GameManager.instance.getGameView(gameId, playerId);
return managerFactory.gameManager().getGameView(gameId, playerId);
}
}
}
private static class MyActionWithBooleanResult extends ActionWithBooleanResult {
private class MyActionWithBooleanResult extends ActionWithBooleanResult {
private final String sessionId;
private final UUID tableId;
@ -1263,17 +1244,17 @@ public class MageServerImpl implements MageServer {
@Override
public Boolean execute() throws MageException {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
return false;
} else {
UUID userId = session.get().getUserId();
return TableManager.instance.watchTable(userId, tableId);
return managerFactory.tableManager().watchTable(userId, tableId);
}
}
}
private static class DraftPickViewActionWithNullNegativeResult extends ActionWithNullNegativeResult<DraftPickView> {
private class DraftPickViewActionWithNullNegativeResult extends ActionWithNullNegativeResult<DraftPickView> {
private final String sessionId;
private final UUID draftId;
@ -1289,9 +1270,9 @@ public class MageServerImpl implements MageServer {
@Override
public DraftPickView execute() {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (session.isPresent()) {
return DraftManager.instance.sendCardPick(draftId, session.get().getUserId(), cardPick, hiddenCards);
return managerFactory.draftManager().sendCardPick(draftId, session.get().getUserId(), cardPick, hiddenCards);
} else {
logger.error("Session not found sessionId: " + sessionId + " draftId:" + draftId);
}
@ -1299,7 +1280,7 @@ public class MageServerImpl implements MageServer {
}
}
private static class MyActionWithTableViewResult extends ActionWithTableViewResult {
private class MyActionWithTableViewResult extends ActionWithTableViewResult {
private final String sessionId;
private final MatchOptions options;
@ -1313,12 +1294,12 @@ public class MageServerImpl implements MageServer {
@Override
public TableView execute() throws MageException {
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
return null;
}
UUID userId = session.get().getUserId();
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (!_user.isPresent()) {
logger.error("User for session not found. session = " + sessionId);
return null;
@ -1349,13 +1330,13 @@ public class MageServerImpl implements MageServer {
user.showUserMessage("Create table", message);
throw new MageException("No message");
}
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(roomId);
if (room.isPresent()) {
TableView table = room.get().createTable(userId, options);
if (logger.isDebugEnabled()) {
logger.debug("TABLE created - tableId: " + table.getTableId() + ' ' + table.getTableName());
logger.debug("- " + user.getName() + " userId: " + user.getId());
logger.debug("- chatId: " + TableManager.instance.getChatId(table.getTableId()));
logger.debug("- chatId: " + managerFactory.tableManager().getChatId(table.getTableId()));
}
return table;
} else {

View file

@ -1,25 +1,32 @@
package mage.server;
import java.util.Properties;
import mage.server.managers.ConfigSettings;
import mage.server.managers.MailClient;
import org.apache.log4j.Logger;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import mage.server.util.ConfigSettings;
import org.apache.log4j.Logger;
import java.util.Properties;
public final class MailClient {
public class MailClientImpl implements MailClient {
private static final Logger logger = Logger.getLogger(Main.class);
public static boolean sendMessage(String email, String subject, String text) {
private final ConfigSettings config;
public MailClientImpl(ConfigSettings config) {
this.config = config;
}
public boolean sendMessage(String email, String subject, String text) {
if (email.isEmpty()) {
logger.info("Email is not sent because the address is empty");
return false;
}
ConfigSettings config = ConfigSettings.instance;
Properties properties = System.getProperties();
properties.setProperty("mail.smtps.host", config.getMailSmtpHost());
@ -30,7 +37,7 @@ public final class MailClient {
Session session = Session.getDefaultInstance(properties);
try{
try {
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(config.getMailFromAddress()));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
@ -44,7 +51,7 @@ public final class MailClient {
trnsport.close();
return true;
}catch (MessagingException ex) {
} catch (MessagingException ex) {
logger.error("Error sending message to " + email, ex);
}
return false;

View file

@ -5,29 +5,37 @@ import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import javax.ws.rs.core.MediaType;
import mage.server.util.ConfigSettings;
import mage.server.managers.ConfigSettings;
import mage.server.managers.MailClient;
import org.apache.log4j.Logger;
public final class MailgunClient {
import javax.ws.rs.core.MediaType;
public class MailgunClientImpl implements MailClient {
private static final Logger logger = Logger.getLogger(Main.class);
public static boolean sendMessage(String email, String subject, String text) {
private final ConfigSettings config;
public MailgunClientImpl(ConfigSettings config) {
this.config = config;
}
public boolean sendMessage(String email, String subject, String text) {
if (email.isEmpty()) {
logger.info("Email is not sent because the address is empty");
return false;
}
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter("api", ConfigSettings.instance.getMailgunApiKey()));
String domain = ConfigSettings.instance.getMailgunDomain();
client.addFilter(new HTTPBasicAuthFilter("api", config.getMailgunApiKey()));
String domain = config.getMailgunDomain();
WebResource webResource = client.resource("https://api.mailgun.net/v3/" + domain + "/messages");
MultivaluedMapImpl formData = new MultivaluedMapImpl();
formData.add("from", "XMage <postmaster@" + domain + '>');
formData.add("to", email);
formData.add("subject", subject);
formData.add("text", text);
ClientResponse response = webResource.type(MediaType.APPLICATION_FORM_URLENCODED).post(ClientResponse.class, formData);
ClientResponse response = webResource.type(MediaType.APPLICATION_FORM_URLENCODED).post(ClientResponse.class, formData);
boolean succeeded = response.getStatus() == 200;
if (!succeeded) {
logger.error("Error sending message to " + email + ". Status code: " + response.getStatus());

View file

@ -14,12 +14,11 @@ import mage.remote.Connection;
import mage.server.draft.CubeFactory;
import mage.server.game.GameFactory;
import mage.server.game.PlayerFactory;
import mage.server.managers.ConfigSettings;
import mage.server.managers.ManagerFactory;
import mage.server.record.UserStatsRepository;
import mage.server.tournament.TournamentFactory;
import mage.server.util.ConfigSettings;
import mage.server.util.PluginClassLoader;
import mage.server.util.ServerMessagesUtil;
import mage.server.util.SystemUtil;
import mage.server.util.*;
import mage.server.util.config.GamePlugin;
import mage.server.util.config.Plugin;
import mage.utils.MageVersion;
@ -40,6 +39,7 @@ import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.*;
/**
@ -53,9 +53,16 @@ public final class Main {
private static final String testModeArg = "-testMode=";
private static final String fastDBModeArg = "-fastDbMode=";
private static final String adminPasswordArg = "-adminPassword=";
/**
* The property that holds the path to the configuration file. Defaults to "config/config.xml".
*
* To set up a different one, start the application with the java option "-Dxmage.config.path=&lt;path&gt;"
*/
private static final String configPathProp = "xmage.config.path";
private static final File pluginFolder = new File("plugins");
private static final File extensionFolder = new File("extensions");
private static final String defaultConfigPath = Paths.get("config", "config.xml").toString();
public static final PluginClassLoader classLoader = new PluginClassLoader();
private static TransporterServer server;
@ -70,7 +77,6 @@ public final class Main {
logger.info("Starting MAGE server version " + version);
logger.info("Logging level: " + logger.getEffectiveLevel());
logger.info("Default charset: " + Charset.defaultCharset());
String adminPassword = "";
for (String arg : args) {
if (arg.startsWith(testModeArg)) {
@ -83,7 +89,14 @@ public final class Main {
}
}
if (ConfigSettings.instance.isAuthenticationActivated()) {
final String configPath = Optional.ofNullable(System.getProperty(configPathProp))
.orElse(defaultConfigPath);
logger.info(String.format("Reading configuration from path=%s", configPath));
final ConfigWrapper config = new ConfigWrapper(ConfigFactory.loadFromFile(configPath));
if (config.isAuthenticationActivated()) {
logger.info("Check authorized user DB version ...");
if (!AuthorizedUserRepository.instance.checkAlterAndMigrateAuthorizedUser()) {
logger.fatal("Failed to start server.");
@ -148,7 +161,6 @@ public final class Main {
UserStatsRepository.instance.updateUserStats();
logger.info("Done.");
deleteSavedGames();
ConfigSettings config = ConfigSettings.instance;
for (GamePlugin plugin : config.getGameTypes()) {
GameFactory.instance.addGameType(plugin.getName(), loadGameType(plugin), loadPlugin(plugin));
}
@ -206,11 +218,12 @@ public final class Main {
Connection connection = new Connection("&maxPoolSize=" + config.getMaxPoolSize());
connection.setHost(config.getServerAddress());
connection.setPort(config.getPort());
final ManagerFactory managerFactory = new MainManagerFactory(config);
try {
// Parameter: serializationtype => jboss
InvokerLocator serverLocator = new InvokerLocator(connection.getURI());
if (!isAlreadyRunning(serverLocator)) {
server = new MageTransporterServer(serverLocator, new MageServerImpl(adminPassword, testMode), MageServer.class.getName(), new MageServerInvocationHandler());
if (!isAlreadyRunning(config, serverLocator)) {
server = new MageTransporterServer(managerFactory, serverLocator, new MageServerImpl(managerFactory, adminPassword, testMode), MageServer.class.getName(), new MageServerInvocationHandler(managerFactory));
server.start();
logger.info("Started MAGE server - listening on " + connection.toString());
@ -230,9 +243,9 @@ public final class Main {
ServerMessagesUtil.instance.setStartDate(System.currentTimeMillis());
}
static boolean isAlreadyRunning(InvokerLocator serverLocator) {
static boolean isAlreadyRunning(ConfigSettings config, InvokerLocator serverLocator) {
Map<String, String> metadata = new HashMap<>();
metadata.put(SocketWrapper.WRITE_TIMEOUT, String.valueOf(ConfigSettings.instance.getSocketWriteTimeout()));
metadata.put(SocketWrapper.WRITE_TIMEOUT, String.valueOf(config.getSocketWriteTimeout()));
metadata.put("generalizeSocketException", "true");
try {
MageServer testServer = (MageServer) TransporterClient.createTransporterClient(serverLocator.getLocatorURI(), MageServer.class, metadata);
@ -248,16 +261,22 @@ public final class Main {
static class ClientConnectionListener implements ConnectionListener {
private final ManagerFactory managerFactory;
public ClientConnectionListener(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public void handleConnectionException(Throwable throwable, Client client) {
String sessionId = client.getSessionId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.trace("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
StringBuilder sessionInfo = new StringBuilder();
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (user.isPresent()) {
sessionInfo.append(user.get().getName()).append(" [").append(user.get().getGameInfo()).append(']');
} else {
@ -267,12 +286,12 @@ public final class Main {
if (throwable instanceof ClientDisconnectedException) {
// Seems like the random diconnects from public server land here and should not be handled as explicit disconnects
// So it should be possible to reconnect to server and continue games if DisconnectReason is set to LostConnection
//SessionManager.instance.disconnect(client.getSessionId(), DisconnectReason.Disconnected);
SessionManager.instance.disconnect(client.getSessionId(), DisconnectReason.LostConnection);
//managerFactory.sessionManager().disconnect(client.getSessionId(), DisconnectReason.Disconnected);
managerFactory.sessionManager().disconnect(client.getSessionId(), DisconnectReason.LostConnection);
logger.info("CLIENT DISCONNECTED - " + sessionInfo);
logger.debug("Stack Trace", throwable);
} else {
SessionManager.instance.disconnect(client.getSessionId(), DisconnectReason.LostConnection);
managerFactory.sessionManager().disconnect(client.getSessionId(), DisconnectReason.LostConnection);
logger.info("LOST CONNECTION - " + sessionInfo);
if (logger.isDebugEnabled()) {
if (throwable == null) {
@ -292,11 +311,11 @@ public final class Main {
protected Connector connector;
public MageTransporterServer(InvokerLocator locator, Object target, String subsystem, MageServerInvocationHandler serverInvocationHandler) throws Exception {
public MageTransporterServer(ManagerFactory managerFactory, InvokerLocator locator, Object target, String subsystem, MageServerInvocationHandler serverInvocationHandler) throws Exception {
super(locator, target, subsystem);
connector.addInvocationHandler("callback", serverInvocationHandler);
connector.setLeasePeriod(ConfigSettings.instance.getLeasePeriod());
connector.addConnectionListener(new ClientConnectionListener());
connector.setLeasePeriod(managerFactory.configSettings().getLeasePeriod());
connector.addConnectionListener(new ClientConnectionListener(managerFactory));
}
public Connector getConnector() throws Exception {
@ -313,6 +332,12 @@ public final class Main {
static class MageServerInvocationHandler implements ServerInvocationHandler {
private final ManagerFactory managerFactory;
public MageServerInvocationHandler(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public void setMBeanServer(MBeanServer server) {
/**
@ -333,9 +358,9 @@ public final class Main {
@Override
public void setInvoker(ServerInvoker invoker) {
((BisocketServerInvoker) invoker).setSecondaryBindPort(ConfigSettings.instance.getSecondaryBindPort());
((BisocketServerInvoker) invoker).setBacklog(ConfigSettings.instance.getBacklogSize());
((BisocketServerInvoker) invoker).setNumAcceptThreads(ConfigSettings.instance.getNumAcceptThreads());
((BisocketServerInvoker) invoker).setSecondaryBindPort(managerFactory.configSettings().getSecondaryBindPort());
((BisocketServerInvoker) invoker).setBacklog(managerFactory.configSettings().getBacklogSize());
((BisocketServerInvoker) invoker).setNumAcceptThreads(managerFactory.configSettings().getNumAcceptThreads());
}
@Override
@ -344,7 +369,7 @@ public final class Main {
ServerInvokerCallbackHandler handler = (ServerInvokerCallbackHandler) callbackHandler;
try {
String sessionId = handler.getClientSessionId();
SessionManager.instance.createSession(sessionId, callbackHandler);
managerFactory.sessionManager().createSession(sessionId, callbackHandler);
} catch (Throwable ex) {
logger.fatal("", ex);
}
@ -362,7 +387,7 @@ public final class Main {
} else {
host = "localhost";
}
Optional<Session> session = SessionManager.instance.getSession(sessionId);
Optional<Session> session = managerFactory.sessionManager().getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
@ -375,7 +400,7 @@ public final class Main {
public void removeListener(InvokerCallbackHandler callbackHandler) {
ServerInvokerCallbackHandler handler = (ServerInvokerCallbackHandler) callbackHandler;
String sessionId = handler.getClientSessionId();
SessionManager.instance.disconnect(sessionId, DisconnectReason.Disconnected);
managerFactory.sessionManager().disconnect(sessionId, DisconnectReason.Disconnected);
}
}

View file

@ -0,0 +1,124 @@
package mage.server;
import mage.server.draft.DraftManagerImpl;
import mage.server.game.GameManagerImpl;
import mage.server.game.GamesRoomManagerImpl;
import mage.server.game.ReplayManagerImpl;
import mage.server.managers.*;
import mage.server.tournament.TournamentManagerImpl;
import mage.server.util.ThreadExecutorImpl;
public class MainManagerFactory implements ManagerFactory {
private final ConfigSettings configSettings;
private final ThreadExecutor threadExecutor;
private final ChatManager chatManager;
private final DraftManager draftManager;
private final GameManager gameManager;
private final GamesRoomManager gamesRoomManager;
private final MailClient mailClient;
private final MailClient mailgunClient;
private final ReplayManager replayManager;
private final SessionManager sessionManager;
private final TableManager tableManager;
private final UserManager userManager;
private final TournamentManager tournamentManager;
public MainManagerFactory(ConfigSettings configSettings) {
this.configSettings = configSettings;
// ThreadExecutorImpl, MailClientImpl and MailGunClient depend only on the config, so they are initialised first
this.threadExecutor = new ThreadExecutorImpl(configSettings);
this.mailClient = new MailClientImpl(configSettings);
this.mailgunClient = new MailgunClientImpl(configSettings);
// Chat, Draft, Game, Replay, Session and Tournament managers only require access to the ManagerFactory
// but do not use them in initialisation
this.chatManager = new ChatManagerImpl(this);
this.draftManager = new DraftManagerImpl(this);
this.gameManager = new GameManagerImpl(this);
this.replayManager = new ReplayManagerImpl(this);
this.sessionManager = new SessionManagerImpl(this);
this.tournamentManager = new TournamentManagerImpl(this);
// GamesRoom, Table, User managers depend on the ManagerFactory and have an initialisation block which is delayed
// to the end of the construction
final GamesRoomManagerImpl gamesRoomManager = new GamesRoomManagerImpl(this);
final TableManagerImpl tableManager = new TableManagerImpl(this);
final UserManagerImpl userManager = new UserManagerImpl(this);
this.gamesRoomManager = gamesRoomManager;
this.tableManager = tableManager;
this.userManager = userManager;
// execute the initialisation block of the relevant manager (they start the executor services)
startThreads(gamesRoomManager, tableManager, userManager);
}
private void startThreads(GamesRoomManagerImpl gamesRoomManager, TableManagerImpl tableManager, UserManagerImpl userManager) {
userManager.init();
tableManager.init();
gamesRoomManager.init();
}
@Override
public ChatManager chatManager() {
return chatManager;
}
@Override
public DraftManager draftManager() {
return draftManager;
}
@Override
public GameManager gameManager() {
return gameManager;
}
@Override
public GamesRoomManager gamesRoomManager() {
return gamesRoomManager;
}
@Override
public MailClient mailClient() {
return mailClient;
}
@Override
public MailClient mailgunClient() {
return mailgunClient;
}
@Override
public ReplayManager replayManager() {
return replayManager;
}
@Override
public SessionManager sessionManager() {
return sessionManager;
}
@Override
public TableManager tableManager() {
return tableManager;
}
@Override
public UserManager userManager() {
return userManager;
}
@Override
public ConfigSettings configSettings() {
return configSettings;
}
@Override
public ThreadExecutor threadExecutor() {
return threadExecutor;
}
@Override
public TournamentManager tournamentManager() {
return tournamentManager;
}
}

View file

@ -1,11 +1,10 @@
package mage.server;
import mage.server.managers.ChatManager;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class RoomImpl implements Room {
@ -13,9 +12,9 @@ public abstract class RoomImpl implements Room {
private final UUID chatId;
private final UUID roomId;
public RoomImpl() {
public RoomImpl(ChatManager chatManager) {
roomId = UUID.randomUUID();
chatId = ChatManager.instance.createChatSession("Room " + roomId);
chatId = chatManager.createChatSession("Room " + roomId);
}
/**
@ -35,5 +34,4 @@ public abstract class RoomImpl implements Room {
}
}

View file

@ -7,8 +7,8 @@ import mage.interfaces.callback.ClientCallbackMethod;
import mage.players.net.UserData;
import mage.players.net.UserGroup;
import mage.server.game.GamesRoom;
import mage.server.game.GamesRoomManager;
import mage.server.util.ConfigSettings;
import mage.server.managers.ConfigSettings;
import mage.server.managers.ManagerFactory;
import mage.server.util.SystemUtil;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
@ -34,6 +34,7 @@ public class Session {
private static final Pattern alphabetsPattern = Pattern.compile("[a-zA-Z]");
private static final Pattern digitsPattern = Pattern.compile("[0-9]");
private final ManagerFactory managerFactory;
private final String sessionId;
private UUID userId;
private String host;
@ -46,7 +47,8 @@ public class Session {
private final ReentrantLock lock;
private final ReentrantLock callBackLock;
public Session(String sessionId, InvokerCallbackHandler callbackHandler) {
public Session(ManagerFactory managerFactory, String sessionId, InvokerCallbackHandler callbackHandler) {
this.managerFactory = managerFactory;
this.sessionId = sessionId;
this.callbackHandler = (AsynchInvokerCallbackHandler) callbackHandler;
this.isAdmin = false;
@ -56,7 +58,7 @@ public class Session {
}
public String registerUser(String userName, String password, String email) throws MageException {
if (!ConfigSettings.instance.isAuthenticationActivated()) {
if (!managerFactory.configSettings().isAuthenticationActivated()) {
String returnMessage = "Registration is disabled by the server config";
sendErrorMessageToClient(returnMessage);
return returnMessage;
@ -86,10 +88,10 @@ public class Session {
boolean success;
String subject = "XMage Registration Completed";
if (!ConfigSettings.instance.getMailUser().isEmpty()) {
success = MailClient.sendMessage(email, subject, text);
if (!managerFactory.configSettings().getMailUser().isEmpty()) {
success = managerFactory.mailClient().sendMessage(email, subject, text);
} else {
success = MailgunClient.sendMessage(email, subject, text);
success = managerFactory.mailgunClient().sendMessage(email, subject, text);
}
if (success) {
String ok = "Sent a registration confirmation / initial password email to " + email + " for " + userName;
@ -109,18 +111,18 @@ public class Session {
}
}
private static String validateUserName(String userName) {
private String validateUserName(String userName) {
if (userName.equals("Admin")) {
return "User name Admin already in use";
}
ConfigSettings config = ConfigSettings.instance;
ConfigSettings config = managerFactory.configSettings();
if (userName.length() < config.getMinUserNameLength()) {
return "User name may not be shorter than " + config.getMinUserNameLength() + " characters";
}
if (userName.length() > config.getMaxUserNameLength()) {
return "User name may not be longer than " + config.getMaxUserNameLength() + " characters";
}
Pattern invalidUserNamePattern = Pattern.compile(ConfigSettings.instance.getInvalidUserNamePattern(), Pattern.CASE_INSENSITIVE);
Pattern invalidUserNamePattern = Pattern.compile(managerFactory.configSettings().getInvalidUserNamePattern(), Pattern.CASE_INSENSITIVE);
Matcher m = invalidUserNamePattern.matcher(userName);
if (m.find()) {
return "User name '" + userName + "' includes not allowed characters: use a-z, A-Z and 0-9";
@ -132,8 +134,8 @@ public class Session {
return null;
}
private static String validatePassword(String password, String userName) {
ConfigSettings config = ConfigSettings.instance;
private String validatePassword(String password, String userName) {
ConfigSettings config = managerFactory.configSettings();
if (password.length() < config.getMinPasswordLength()) {
return "Password may not be shorter than " + config.getMinPasswordLength() + " characters";
}
@ -178,7 +180,7 @@ public class Session {
public String connectUserHandling(String userName, String password) throws MageException {
this.isAdmin = false;
AuthorizedUser authorizedUser = null;
if (ConfigSettings.instance.isAuthenticationActivated()) {
if (managerFactory.configSettings().isAuthenticationActivated()) {
authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
String errorMsg = "Wrong username or password. In case you haven't, please register your account first.";
if (authorizedUser == null) {
@ -196,7 +198,7 @@ public class Session {
if (authorizedUser.lockedUntil.compareTo(Calendar.getInstance().getTime()) > 0) {
return "Your profile is deactivated until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil);
} else {
UserManager.instance.createUser(userName, host, authorizedUser).ifPresent(user
managerFactory.userManager().createUser(userName, host, authorizedUser).ifPresent(user
-> user.setLockedUntil(null)
);
@ -204,15 +206,15 @@ public class Session {
}
}
Optional<User> selectUser = UserManager.instance.createUser(userName, host, authorizedUser);
Optional<User> selectUser = managerFactory.userManager().createUser(userName, host, authorizedUser);
boolean reconnect = false;
if (!selectUser.isPresent()) {
// user already connected
selectUser = UserManager.instance.getUserByName(userName);
selectUser = managerFactory.userManager().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)) {
if (managerFactory.configSettings().isAuthenticationActivated() || user.getHost().equals(host)) {
user.updateLastActivity(null); // minimizes possible expiration
this.userId = user.getId();
if (user.getSessionId().isEmpty()) {
@ -221,7 +223,7 @@ public class Session {
} else {
//disconnect previous session
logger.info("Disconnecting another user instance: " + userName);
SessionManager.instance.disconnect(user.getSessionId(), DisconnectReason.ConnectingOtherInstance);
managerFactory.sessionManager().disconnect(user.getSessionId(), DisconnectReason.ConnectingOtherInstance);
}
} else {
return "User name " + userName + " already in use (or your IP address changed)";
@ -232,18 +234,18 @@ public class Session {
}
}
User user = selectUser.get();
if (!UserManager.instance.connectToSession(sessionId, user.getId())) {
if (!managerFactory.userManager().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());
Optional<GamesRoom> room = managerFactory.gamesRoomManager().getRoom(managerFactory.gamesRoomManager().getMainRoomId());
if (!room.isPresent()) {
logger.warn("main room not found"); // after server restart users try to use old rooms on reconnect
return null;
}
ChatManager.instance.joinChat(room.get().getChatId(), userId);
ChatManager.instance.sendReconnectMessage(userId);
managerFactory.chatManager().joinChat(room.get().getChatId(), userId);
managerFactory.chatManager().sendReconnectMessage(userId);
}
return null;
@ -251,12 +253,12 @@ public class Session {
public void connectAdmin() {
this.isAdmin = true;
User user = UserManager.instance.createUser("Admin", host, null).orElse(
UserManager.instance.getUserByName("Admin").get());
User user = managerFactory.userManager().createUser("Admin", host, null).orElse(
managerFactory.userManager().getUserByName("Admin").get());
UserData adminUserData = UserData.getDefaultUserDataView();
adminUserData.setGroupId(UserGroup.ADMIN.getGroupId());
user.setUserData(adminUserData);
if (!UserManager.instance.connectToSession(sessionId, user.getId())) {
if (!managerFactory.userManager().connectToSession(sessionId, user.getId())) {
logger.info("Error connecting Admin!");
} else {
user.setUserState(User.UserState.Connected);
@ -265,7 +267,7 @@ public class Session {
}
public boolean setUserData(String userName, UserData userData, String clientVersion, String userIdStr) {
Optional<User> _user = UserManager.instance.getUserByName(userName);
Optional<User> _user = managerFactory.userManager().getUserByName(userName);
_user.ifPresent(user -> {
if (clientVersion != null) {
user.setClientVersion(clientVersion);
@ -313,7 +315,7 @@ public class Session {
// because different threads can activate this
public void userLostConnection() {
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (!_user.isPresent()) {
return; //user was already disconnected by other thread
}
@ -338,7 +340,7 @@ public class Session {
} else {
logger.error("SESSION LOCK - kill: userId " + userId);
}
UserManager.instance.removeUserFromAllTablesAndChat(userId, reason);
managerFactory.userManager().removeUserFromAllTablesAndChat(userId, reason);
} catch (InterruptedException ex) {
logger.error("SESSION LOCK - kill: userId " + userId, ex);
} finally {
@ -372,11 +374,11 @@ public class Session {
logger.warn("SESSION LOCK - fireCallback - userId: " + userId + " messageId: " + call.getMessageId(), ex);
} catch (HandleCallbackException ex) {
this.valid = false;
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
user.setUserState(User.UserState.Disconnected);
logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId + " messageId: " + call.getMessageId() + " - cause: " + getBasicCause(ex).toString());
logger.trace("Stack trace:", ex);
SessionManager.instance.disconnect(sessionId, LostConnection);
managerFactory.sessionManager().disconnect(sessionId, LostConnection);
});
} catch (Exception ex) {
logger.warn("Unspecific exception:", ex);

View file

@ -1,8 +1,9 @@
package mage.server;
import mage.MageException;
import mage.players.net.UserData;
import mage.server.managers.SessionManager;
import mage.server.managers.ManagerFactory;
import org.apache.log4j.Logger;
import org.jboss.remoting.callback.InvokerCallbackHandler;
@ -13,21 +14,25 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* @author BetaSteward_at_googlemail.com
*/
public enum SessionManager {
public class SessionManagerImpl implements SessionManager {
instance;
private static final Logger logger = Logger.getLogger(SessionManager.class);
private static final Logger logger = Logger.getLogger(SessionManagerImpl.class);
private final ManagerFactory managerFactory;
private final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
public SessionManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public Optional<Session> getSession(@Nonnull String sessionId) {
Session session = sessions.get(sessionId);
if (session == null) {
logger.trace("Session with sessionId " + sessionId + " is not found");
return Optional.empty();
}
if (session.getUserId() != null && !UserManager.instance.getUser(session.getUserId()).isPresent()) {
if (session.getUserId() != null && !managerFactory.userManager().getUser(session.getUserId()).isPresent()) {
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 they disconnect with one client
disconnect(sessionId, DisconnectReason.ConnectingOtherInstance, session); // direct disconnect
@ -36,11 +41,13 @@ public enum SessionManager {
return Optional.of(session);
}
@Override
public void createSession(String sessionId, InvokerCallbackHandler callbackHandler) {
Session session = new Session(sessionId, callbackHandler);
Session session = new Session(managerFactory, sessionId, callbackHandler);
sessions.put(sessionId, session);
}
@Override
public boolean registerUser(String sessionId, String userName, String password, String email) throws MageException {
Session session = sessions.get(sessionId);
if (session == null) {
@ -59,6 +66,7 @@ public enum SessionManager {
return true;
}
@Override
public boolean connectUser(String sessionId, String userName, String password, String userIdStr) throws MageException {
Session session = sessions.get(sessionId);
if (session != null) {
@ -78,6 +86,7 @@ public enum SessionManager {
return false;
}
@Override
public boolean connectAdmin(String sessionId) {
Session session = sessions.get(sessionId);
if (session != null) {
@ -88,17 +97,20 @@ public enum SessionManager {
return false;
}
@Override
public boolean setUserData(String userName, String sessionId, UserData userData, String clientVersion, String userIdStr) throws MageException {
return getSession(sessionId)
.map(session -> session.setUserData(userName,userData, clientVersion, userIdStr))
.orElse(false);
return getSession(sessionId)
.map(session -> session.setUserData(userName, userData, clientVersion, userIdStr))
.orElse(false);
}
@Override
public void disconnect(String sessionId, DisconnectReason reason) {
disconnect(sessionId, reason, null);
}
@Override
public void disconnect(String sessionId, DisconnectReason reason, Session directSession) {
if (directSession == null) {
// find real session to disconnects
@ -115,13 +127,13 @@ public enum SessionManager {
break;
case ConnectingOtherInstance:
case Disconnected: // regular session end or wrong client version
UserManager.instance.disconnect(session.getUserId(), reason);
managerFactory.userManager().disconnect(session.getUserId(), reason);
break;
case SessionExpired: // session ends after no reconnect happens in the defined time span
break;
case LostConnection: // user lost connection - session expires countdown starts
session.userLostConnection();
UserManager.instance.disconnect(session.getUserId(), reason);
managerFactory.userManager().disconnect(session.getUserId(), reason);
break;
default:
logger.trace("endSession: unexpected reason " + reason.toString() + " - sessionId: " + sessionId);
@ -141,6 +153,7 @@ public enum SessionManager {
* @param sessionId
* @param userSessionId
*/
@Override
public void disconnectUser(String sessionId, String userSessionId) {
if (isAdmin(sessionId)) {
getUserFromSession(sessionId).ifPresent(admin -> {
@ -159,40 +172,46 @@ public enum SessionManager {
private Optional<User> getUserFromSession(String sessionId) {
return getSession(sessionId)
.flatMap(s -> UserManager.instance.getUser(s.getUserId()));
.flatMap(s -> managerFactory.userManager().getUser(s.getUserId()));
}
@Override
public void endUserSession(String sessionId, String userSessionId) {
if (isAdmin(sessionId)) {
disconnect(userSessionId, DisconnectReason.AdminDisconnect);
}
}
@Override
public boolean isAdmin(String sessionId) {
return getSession(sessionId).map(Session::isAdmin).orElse(false);
}
@Override
public boolean isValidSession(@Nonnull String sessionId) {
return sessions.containsKey(sessionId);
}
@Override
public Optional<User> getUser(@Nonnull String sessionId) {
Session session = sessions.get(sessionId);
if (session != null) {
return UserManager.instance.getUser(sessions.get(sessionId).getUserId());
return managerFactory.userManager().getUser(sessions.get(sessionId).getUserId());
}
logger.error(String.format("Session %s could not be found", sessionId));
return Optional.empty();
}
@Override
public boolean extendUserSession(String sessionId, String pingInfo) {
return getSession(sessionId)
.map(session -> UserManager.instance.extendUserSession(session.getUserId(), pingInfo))
.map(session -> managerFactory.userManager().extendUserSession(session.getUserId(), pingInfo))
.orElse(false);
}
@Override
public void sendErrorMessageToClient(String sessionId, String message) {
Session session = sessions.get(sessionId);
if (session == null) {

View file

@ -20,16 +20,12 @@ import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentPlayer;
import mage.players.Player;
import mage.players.PlayerType;
import mage.server.draft.DraftManager;
import mage.server.game.GameFactory;
import mage.server.game.GameManager;
import mage.server.game.PlayerFactory;
import mage.server.managers.ManagerFactory;
import mage.server.record.TableRecorderImpl;
import mage.server.tournament.TournamentFactory;
import mage.server.tournament.TournamentManager;
import mage.server.util.ConfigSettings;
import mage.server.util.ServerMessagesUtil;
import mage.server.util.ThreadExecutor;
import mage.view.ChatMessage;
import org.apache.log4j.Logger;
@ -47,6 +43,7 @@ public class TableController {
private static final Logger logger = Logger.getLogger(TableController.class);
private final ManagerFactory managerFactory;
private final UUID userId;
private final UUID chatId;
private final String controllerName;
@ -58,32 +55,36 @@ public class TableController {
private Tournament tournament;
private ScheduledFuture<?> futureTimeout;
protected static final ScheduledExecutorService timeoutExecutor = ThreadExecutor.instance.getTimeoutExecutor();
protected final ScheduledExecutorService timeoutExecutor;
public TableController(UUID roomId, UUID userId, MatchOptions options) {
public TableController(ManagerFactory managerFactory, UUID roomId, UUID userId, MatchOptions options) {
this.managerFactory = managerFactory;
timeoutExecutor = managerFactory.threadExecutor().getTimeoutExecutor();
this.userId = userId;
this.options = options;
match = GameFactory.instance.createMatch(options.getGameType(), options);
if (userId != null) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
// TODO: Handle if user == null
controllerName = user.map(User::getName).orElse("undefined");
} else {
controllerName = "System";
}
table = new Table(roomId, options.getGameType(), options.getName(), controllerName, DeckValidatorFactory.instance.createDeckValidator(options.getDeckType()),
options.getPlayerTypes(), TableRecorderImpl.instance, match, options.getBannedUsers(), options.isPlaneChase());
chatId = ChatManager.instance.createChatSession("Match Table " + table.getId());
options.getPlayerTypes(), new TableRecorderImpl(managerFactory.userManager()), match, options.getBannedUsers(), options.isPlaneChase());
chatId = managerFactory.chatManager().createChatSession("Match Table " + table.getId());
init();
}
public TableController(UUID roomId, UUID userId, TournamentOptions options) {
public TableController(ManagerFactory managerFactory, UUID roomId, UUID userId, TournamentOptions options) {
this.managerFactory = managerFactory;
this.timeoutExecutor = managerFactory.threadExecutor().getTimeoutExecutor();
this.userId = userId;
tournament = TournamentFactory.instance.createTournament(options.getTournamentType(), options);
if (userId != null) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (!user.isPresent()) {
logger.fatal(new StringBuilder("User for userId ").append(userId).append(" could not be retrieved from UserManager").toString());
logger.fatal(new StringBuilder("User for userId ").append(userId).append(" could not be retrieved from UserManagerImpl").toString());
controllerName = "[unknown]";
} else {
controllerName = user.get().getName();
@ -92,8 +93,8 @@ public class TableController {
controllerName = "System";
}
table = new Table(roomId, options.getTournamentType(), options.getName(), controllerName, DeckValidatorFactory.instance.createDeckValidator(options.getMatchOptions().getDeckType()),
options.getPlayerTypes(), TableRecorderImpl.instance, tournament, options.getMatchOptions().getBannedUsers(), options.isPlaneChase());
chatId = ChatManager.instance.createChatSession("Tourn. table " + table.getId());
options.getPlayerTypes(), new TableRecorderImpl(managerFactory.userManager()), tournament, options.getMatchOptions().getBannedUsers(), options.isPlaneChase());
chatId = managerFactory.chatManager().createChatSession("Tourn. table " + table.getId());
}
private void init() {
@ -121,7 +122,7 @@ public class TableController {
if (seat == null) {
throw new GameException("No available seats.");
}
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (!_user.isPresent()) {
logger.fatal("couldn't get user " + name + " for join tournament userId = " + userId);
return false;
@ -156,7 +157,7 @@ public class TableController {
user.showUserMessage("Join Table", sb.toString());
if (isOwner(userId)) {
logger.debug("New table removed because owner submitted invalid deck tableId " + table.getId());
TableManager.instance.removeTable(table.getId());
managerFactory.tableManager().removeTable(table.getId());
}
return false;
}
@ -231,12 +232,12 @@ public class TableController {
newTournamentPlayer.setState(oldTournamentPlayer.getState());
newTournamentPlayer.setReplacedTournamentPlayer(oldTournamentPlayer);
DraftManager.instance.getController(table.getId()).ifPresent(controller -> controller.replacePlayer(oldPlayer, newPlayer));
managerFactory.draftManager().getController(table.getId()).ifPresent(controller -> controller.replacePlayer(oldPlayer, newPlayer));
return true;
}
public synchronized boolean joinTable(UUID userId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws MageException {
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (!_user.isPresent()) {
logger.error("Join Table: can't find user to join " + name + " Id = " + userId);
return false;
@ -274,7 +275,7 @@ public class TableController {
user.showUserMessage("Join Table", sb.toString());
if (isOwner(userId)) {
logger.debug("New table removed because owner submitted invalid deck tableId " + table.getId());
TableManager.instance.removeTable(table.getId());
managerFactory.tableManager().removeTable(table.getId());
}
return false;
}
@ -420,7 +421,7 @@ public class TableController {
}
}
if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (!_user.isPresent()) {
return false;
}
@ -453,10 +454,10 @@ public class TableController {
private void submitDeck(UUID userId, UUID playerId, Deck deck) {
if (table.getState() == TableState.SIDEBOARDING) {
match.submitDeck(playerId, deck);
UserManager.instance.getUser(userId).ifPresent(user -> user.removeSideboarding(table.getId()));
managerFactory.userManager().getUser(userId).ifPresent(user -> user.removeSideboarding(table.getId()));
} else {
TournamentManager.instance.submitDeck(tournament.getId(), playerId, deck);
UserManager.instance.getUser(userId).ifPresent(user -> user.removeConstructing(playerId));
managerFactory.tournamentManager().submitDeck(tournament.getId(), playerId, deck);
managerFactory.userManager().getUser(userId).ifPresent(user -> user.removeConstructing(playerId));
}
}
@ -464,7 +465,7 @@ public class TableController {
boolean validDeck = true;
if (table.isTournament()) {
if (tournament != null) {
validDeck = TournamentManager.instance.updateDeck(tournament.getId(), playerId, deck);
validDeck = managerFactory.tournamentManager().updateDeck(tournament.getId(), playerId, deck);
} else {
logger.fatal("Tournament == null table: " + table.getId() + " userId: " + userId);
}
@ -478,7 +479,7 @@ public class TableController {
public boolean watchTable(UUID userId) {
if (table.isTournament()) {
UserManager.instance.getUser(userId).ifPresent(user -> user.ccShowTournament(table.getTournament().getId()));
managerFactory.userManager().getUser(userId).ifPresent(user -> user.ccShowTournament(table.getTournament().getId()));
return true;
} else {
if (table.isTournamentSubTable() && !table.getTournament().getOptions().isWatchingAllowed()) {
@ -491,7 +492,7 @@ public class TableController {
if (userPlayerMap.get(userId) != null) {
return false;
}
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (!_user.isPresent()) {
return false;
}
@ -533,7 +534,7 @@ public class TableController {
&& (table.getState() == TableState.WAITING
|| table.getState() == TableState.READY_TO_START)) {
// table not started yet and user is the owner, removeUserFromAllTablesAndChat the table
TableManager.instance.removeTable(table.getId());
managerFactory.tableManager().removeTable(table.getId());
} else {
UUID playerId = userPlayerMap.get(userId);
if (playerId != null) {
@ -544,9 +545,9 @@ public class TableController {
} else {
match.quitMatch(playerId);
}
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (user.isPresent()) {
ChatManager.instance.broadcast(chatId, user.get().getName(), "has left the table", ChatMessage.MessageColor.BLUE, true, null, ChatMessage.MessageType.STATUS, ChatMessage.SoundToPlay.PlayerLeft);
managerFactory.chatManager().broadcast(chatId, user.get().getName(), "has left the table", ChatMessage.MessageColor.BLUE, true, null, ChatMessage.MessageType.STATUS, ChatMessage.SoundToPlay.PlayerLeft);
if (!table.isTournamentSubTable()) {
user.get().removeTable(playerId);
}
@ -557,9 +558,9 @@ public class TableController {
} else if (table.getState() != TableState.FINISHED) {
if (table.isTournament()) {
logger.debug("Quit tournament sub tables for userId: " + userId);
TableManager.instance.userQuitTournamentSubTables(tournament.getId(), userId);
managerFactory.tableManager().userQuitTournamentSubTables(tournament.getId(), userId);
logger.debug("Quit tournament Id: " + table.getTournament().getId() + '(' + table.getTournament().getTournamentState() + ')');
TournamentManager.instance.quit(tournament.getId(), userId);
managerFactory.tournamentManager().quit(tournament.getId(), userId);
} else {
MatchPlayer matchPlayer = match.getPlayer(playerId);
if (matchPlayer != null && !match.hasEnded() && !matchPlayer.hasQuit()) {
@ -567,7 +568,7 @@ public class TableController {
if (game != null && !game.hasEnded()) {
Player player = match.getPlayer(playerId).getPlayer();
if (player != null && player.isInGame()) {
GameManager.instance.quitMatch(game.getId(), userId);
managerFactory.gameManager().quitMatch(game.getId(), userId);
}
match.quitMatch(playerId);
} else {
@ -605,7 +606,7 @@ public class TableController {
if (table.isTournamentSubTable()) {
logger.info("Tourn. match started id:" + match.getId() + " tournId: " + table.getTournament().getId());
} else {
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
logger.info("MATCH started [" + match.getName() + "] " + match.getId() + '(' + user.getName() + ')');
logger.debug("- " + match.getOptions().getGameType() + " - " + match.getOptions().getDeckType());
});
@ -628,12 +629,12 @@ public class TableController {
gameOptions.bannedUsers = match.getOptions().getBannedUsers();
gameOptions.planeChase = match.getOptions().isPlaneChase();
match.getGame().setGameOptions(gameOptions);
GameManager.instance.createGameSession(match.getGame(), userPlayerMap, table.getId(), choosingPlayerId, gameOptions);
managerFactory.gameManager().createGameSession(match.getGame(), userPlayerMap, table.getId(), choosingPlayerId, gameOptions);
String creator = null;
StringBuilder opponent = new StringBuilder();
for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) { // do only for no AI players
if (match.getPlayer(entry.getValue()) != null && !match.getPlayer(entry.getValue()).hasQuit()) {
Optional<User> _user = UserManager.instance.getUser(entry.getKey());
Optional<User> _user = managerFactory.userManager().getUser(entry.getKey());
if (_user.isPresent()) {
User user = _user.get();
user.ccGameStarted(match.getGame().getId(), entry.getValue());
@ -670,17 +671,17 @@ public class TableController {
logger.info("GAME started " + (match.getGame() != null ? match.getGame().getId() : "no Game") + " [" + match.getName() + "] " + creator + " - " + opponent.toString());
logger.debug("- matchId: " + match.getId() + " [" + match.getName() + ']');
if (match.getGame() != null) {
logger.debug("- chatId: " + GameManager.instance.getChatId(match.getGame().getId()));
logger.debug("- chatId: " + managerFactory.gameManager().getChatId(match.getGame().getId()));
}
} catch (Exception ex) {
logger.fatal("Error starting game table: " + table.getId(), ex);
if (table != null) {
TableManager.instance.removeTable(table.getId());
managerFactory.tableManager().removeTable(table.getId());
}
if (match != null) {
Game game = match.getGame();
if (game != null) {
GameManager.instance.removeGame(game.getId());
managerFactory.gameManager().removeGame(game.getId());
// game ended by error, so don't add it to ended stats
}
}
@ -691,9 +692,9 @@ public class TableController {
try {
if (userId.equals(this.userId) && table.getState() == TableState.STARTING) {
tournament.setStartTime();
TournamentManager.instance.createTournamentSession(tournament, userPlayerMap, table.getId());
managerFactory.tournamentManager().createTournamentSession(tournament, userPlayerMap, table.getId());
for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
UserManager.instance.getUser(entry.getKey()).ifPresent(user -> {
managerFactory.userManager().getUser(entry.getKey()).ifPresent(user -> {
logger.info(new StringBuilder("User ").append(user.getName()).append(" tournament started: ").append(tournament.getId()).append(" userId: ").append(user.getId()));
user.ccTournamentStarted(tournament.getId(), entry.getValue());
});
@ -702,16 +703,16 @@ public class TableController {
}
} catch (Exception ex) {
logger.fatal("Error starting tournament", ex);
TableManager.instance.removeTable(table.getId());
TournamentManager.instance.quit(tournament.getId(), userId);
managerFactory.tableManager().removeTable(table.getId());
managerFactory.tournamentManager().quit(tournament.getId(), userId);
}
}
public void startDraft(Draft draft) {
table.initDraft();
DraftManager.instance.createDraftSession(draft, userPlayerMap, table.getId());
managerFactory.draftManager().createDraftSession(draft, userPlayerMap, table.getId());
for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
Optional<User> user = UserManager.instance.getUser(entry.getKey());
Optional<User> user = managerFactory.userManager().getUser(entry.getKey());
if (user.isPresent()) {
logger.info(new StringBuilder("User ").append(user.get().getName()).append(" draft started: ").append(draft.getId()).append(" userId: ").append(user.get().getId()));
user.get().ccDraftStarted(draft.getId(), entry.getValue());
@ -725,7 +726,7 @@ public class TableController {
for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
if (entry.getValue().equals(playerId)) {
Optional<User> user = UserManager.instance.getUser(entry.getKey());
Optional<User> user = managerFactory.userManager().getUser(entry.getKey());
int remaining = (int) futureTimeout.getDelay(TimeUnit.SECONDS);
user.ifPresent(user1 -> user1.ccSideboard(deck, table.getId(), remaining, options.isLimited()));
break;
@ -767,12 +768,12 @@ public class TableController {
}
UUID choosingPlayerId = match.getChooser();
match.endGame();
if (ConfigSettings.instance.isSaveGameActivated() && !game.isSimulation()) {
if (GameManager.instance.saveGame(game.getId())) {
if (managerFactory.configSettings().isSaveGameActivated() && !game.isSimulation()) {
if (managerFactory.gameManager().saveGame(game.getId())) {
match.setReplayAvailable(true);
}
}
GameManager.instance.removeGame(game.getId());
managerFactory.gameManager().removeGame(game.getId());
ServerMessagesUtil.instance.incGamesEnded();
try {
@ -835,7 +836,7 @@ public class TableController {
// opponent(s) left during sideboarding
if (matchPlayer != null) {
if (!matchPlayer.hasQuit()) {
UserManager.instance.getUser(entry.getKey()).ifPresent(user -> {
managerFactory.userManager().getUser(entry.getKey()).ifPresent(user -> {
if (table.getState() == TableState.SIDEBOARDING) {
StringBuilder sb = new StringBuilder();
if (table.isTournamentSubTable()) {
@ -860,7 +861,7 @@ public class TableController {
}
}
// free resources no longer needed
match.cleanUpOnMatchEnd(ConfigSettings.instance.isSaveGameActivated(), table.isTournament());
match.cleanUpOnMatchEnd(managerFactory.configSettings().isSaveGameActivated(), table.isTournament());
}
}
}
@ -932,13 +933,13 @@ public class TableController {
public boolean isTournamentStillValid() {
if (table.getTournament() != null) {
if (table.getState() != TableState.WAITING && table.getState() != TableState.READY_TO_START && table.getState() != TableState.STARTING) {
return TournamentManager.instance.getTournamentController(table.getTournament().getId())
return managerFactory.tournamentManager().getTournamentController(table.getTournament().getId())
.map(tc -> tc.isTournamentStillValid(table.getState()))
.orElse(false);
} else {
// check if table creator is still a valid user, if not removeUserFromAllTablesAndChat table
return UserManager.instance.getUser(userId).isPresent();
return managerFactory.userManager().getUser(userId).isPresent();
}
}
return false;
@ -1004,7 +1005,7 @@ public class TableController {
|| table.getState() == TableState.READY_TO_START)
|| !match.isDoneSideboarding()
|| (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) {
Optional<User> user = UserManager.instance.getUser(userPlayerEntry.getKey());
Optional<User> user = managerFactory.userManager().getUser(userPlayerEntry.getKey());
if (!user.isPresent() || !user.get().isActive()) {
logger.warn("- Active user of match is missing: " + matchPlayer.getName());
logger.warn("-- matchId:" + match.getId());
@ -1028,12 +1029,12 @@ public class TableController {
void cleanUp() {
if (!table.isTournamentSubTable()) {
for (Map.Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
UserManager.instance.getUser(entry.getKey()).ifPresent(user
managerFactory.userManager().getUser(entry.getKey()).ifPresent(user
-> user.removeTable(entry.getValue()));
}
}
ChatManager.instance.destroyChatSession(chatId);
managerFactory.chatManager().destroyChatSession(chatId);
}
public synchronized TableState getTableState() {

View file

@ -14,9 +14,8 @@ import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentPlayer;
import mage.players.PlayerType;
import mage.server.game.GameController;
import mage.server.game.GameManager;
import mage.server.game.GamesRoomManager;
import mage.server.util.ThreadExecutor;
import mage.server.managers.TableManager;
import mage.server.managers.ManagerFactory;
import org.apache.log4j.Logger;
import java.text.DateFormat;
@ -34,12 +33,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author BetaSteward_at_googlemail.com
*/
public enum TableManager {
instance;
public class TableManagerImpl implements TableManager {
protected final ScheduledExecutorService expireExecutor = Executors.newSingleThreadScheduledExecutor();
// protected static ScheduledExecutorService expireExecutor = ThreadExecutor.getInstance().getExpireExecutor();
private final Logger logger = Logger.getLogger(TableManager.class);
// protected static ScheduledExecutorService expireExecutor = ThreadExecutorImpl.getInstance().getExpireExecutor();
private final ManagerFactory managerFactory;
private final Logger logger = Logger.getLogger(TableManagerImpl.class);
private final DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
private final ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<>();
@ -55,10 +54,14 @@ public enum TableManager {
*/
private static final int EXPIRE_CHECK_PERIOD = 10;
TableManager() {
public TableManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
public void init() {
expireExecutor.scheduleAtFixedRate(() -> {
try {
ChatManager.instance.clearUserMessageStorage();
managerFactory.chatManager().clearUserMessageStorage();
checkTableHealthState();
} catch (Exception ex) {
logger.fatal("Check table health state job error:", ex);
@ -66,22 +69,25 @@ public enum TableManager {
}, EXPIRE_CHECK_PERIOD, EXPIRE_CHECK_PERIOD, TimeUnit.MINUTES);
}
@Override
public Table createTable(UUID roomId, UUID userId, MatchOptions options) {
TableController tableController = new TableController(roomId, userId, options);
TableController tableController = new TableController(managerFactory, roomId, userId, options);
putControllers(tableController.getTable().getId(), tableController);
putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable();
}
@Override
public Table createTable(UUID roomId, MatchOptions options) {
TableController tableController = new TableController(roomId, null, options);
TableController tableController = new TableController(managerFactory, roomId, null, options);
putControllers(tableController.getTable().getId(), tableController);
putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable();
}
@Override
public Table createTournamentTable(UUID roomId, UUID userId, TournamentOptions options) {
TableController tableController = new TableController(roomId, userId, options);
TableController tableController = new TableController(managerFactory, roomId, userId, options);
putControllers(tableController.getTable().getId(), tableController);
putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable();
@ -107,10 +113,12 @@ public enum TableManager {
}
}
@Override
public Table getTable(UUID tableId) {
return tables.get(tableId);
}
@Override
public Optional<Match> getMatch(UUID tableId) {
if (controllers.containsKey(tableId)) {
return Optional.of(controllers.get(tableId).getMatch());
@ -118,6 +126,7 @@ public enum TableManager {
return Optional.empty();
}
@Override
public Collection<Table> getTables() {
Collection<Table> newTables = new ArrayList<>();
final Lock r = tablesLock.readLock();
@ -130,6 +139,7 @@ public enum TableManager {
return newTables;
}
@Override
public Collection<TableController> getControllers() {
Collection<TableController> newControllers = new ArrayList<>();
final Lock r = controllersLock.readLock();
@ -142,6 +152,7 @@ public enum TableManager {
return newControllers;
}
@Override
public Optional<TableController> getController(UUID tableId) {
if (controllers.containsKey(tableId)) {
return Optional.of(controllers.get(tableId));
@ -149,6 +160,7 @@ public enum TableManager {
return Optional.empty();
}
@Override
public boolean joinTable(UUID userId, UUID tableId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws MageException {
if (controllers.containsKey(tableId)) {
return controllers.get(tableId).joinTable(userId, name, playerType, skill, deckList, password);
@ -156,6 +168,7 @@ public enum TableManager {
return false;
}
@Override
public boolean joinTournament(UUID userId, UUID tableId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws GameException {
if (controllers.containsKey(tableId)) {
return controllers.get(tableId).joinTournament(userId, name, playerType, skill, deckList, password);
@ -163,11 +176,12 @@ public enum TableManager {
return false;
}
@Override
public boolean submitDeck(UUID userId, UUID tableId, DeckCardLists deckList) throws MageException {
if (controllers.containsKey(tableId)) {
return controllers.get(tableId).submitDeck(userId, deckList);
}
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
user.removeSideboarding(tableId);
user.showUserMessage("Submit deck", "Table no longer active");
@ -176,6 +190,7 @@ public enum TableManager {
return true;
}
@Override
public void updateDeck(UUID userId, UUID tableId, DeckCardLists deckList) throws MageException {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).updateDeck(userId, deckList);
@ -183,6 +198,7 @@ public enum TableManager {
}
// removeUserFromAllTablesAndChat user from all tournament sub tables
@Override
public void userQuitTournamentSubTables(UUID userId) {
for (TableController controller : getControllers()) {
if (controller.getTable() != null) {
@ -190,12 +206,13 @@ public enum TableManager {
controller.leaveTable(userId);
}
} else {
logger.error("TableManager.userQuitTournamentSubTables table == null - userId " + userId);
logger.error("TableManagerImpl.userQuitTournamentSubTables table == null - userId " + userId);
}
}
}
// removeUserFromAllTablesAndChat user from all sub tables of a tournament
@Override
public void userQuitTournamentSubTables(UUID tournamentId, UUID userId) {
for (TableController controller : getControllers()) {
if (controller.getTable().isTournamentSubTable() && controller.getTable().getTournament().getId().equals(tournamentId)) {
@ -206,6 +223,7 @@ public enum TableManager {
}
}
@Override
public boolean isTableOwner(UUID tableId, UUID userId) {
if (controllers.containsKey(tableId)) {
return controllers.get(tableId).isOwner(userId);
@ -213,13 +231,14 @@ public enum TableManager {
return false;
}
@Override
public boolean removeTable(UUID userId, UUID tableId) {
if (isTableOwner(tableId, userId) || UserManager.instance.isAdmin(userId)) {
if (isTableOwner(tableId, userId) || managerFactory.userManager().isAdmin(userId)) {
logger.debug("Table remove request - userId: " + userId + " tableId: " + tableId);
TableController tableController = controllers.get(tableId);
if (tableController != null) {
tableController.leaveTableAll();
ChatManager.instance.destroyChatSession(tableController.getChatId());
managerFactory.chatManager().destroyChatSession(tableController.getChatId());
removeTable(tableId);
}
return true;
@ -227,6 +246,7 @@ public enum TableManager {
return false;
}
@Override
public void leaveTable(UUID userId, UUID tableId) {
TableController tableController = controllers.get(tableId);
if (tableController != null) {
@ -234,6 +254,7 @@ public enum TableManager {
}
}
@Override
public Optional<UUID> getChatId(UUID tableId) {
if (controllers.containsKey(tableId)) {
return Optional.of(controllers.get(tableId).getChatId());
@ -248,11 +269,12 @@ public enum TableManager {
* @param roomId
* @param tableId
*/
@Override
public void startMatch(UUID userId, UUID roomId, UUID tableId) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).startMatch(userId);
// chat of start dialog can be killed
ChatManager.instance.destroyChatSession(controllers.get(tableId).getChatId());
managerFactory.chatManager().destroyChatSession(controllers.get(tableId).getChatId());
}
}
@ -262,25 +284,29 @@ public enum TableManager {
* @param roomId
* @param tableId
*/
@Override
public void startTournamentSubMatch(UUID roomId, UUID tableId) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).startMatch();
}
}
@Override
public void startTournament(UUID userId, UUID roomId, UUID tableId) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).startTournament(userId);
ChatManager.instance.destroyChatSession(controllers.get(tableId).getChatId());
managerFactory.chatManager().destroyChatSession(controllers.get(tableId).getChatId());
}
}
@Override
public void startDraft(UUID tableId, Draft draft) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).startDraft(draft);
}
}
@Override
public boolean watchTable(UUID userId, UUID tableId) {
if (controllers.containsKey(tableId)) {
return controllers.get(tableId).watchTable(userId);
@ -288,6 +314,7 @@ public enum TableManager {
return false;
}
@Override
public void endGame(UUID tableId) {
if (controllers.containsKey(tableId)) {
if (controllers.get(tableId).endGameAndStartNextGame()) {
@ -296,42 +323,49 @@ public enum TableManager {
}
}
@Override
public void endDraft(UUID tableId, Draft draft) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).endDraft(draft);
}
}
@Override
public void endTournament(UUID tableId, Tournament tournament) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).endTournament(tournament);
}
}
@Override
public void swapSeats(UUID tableId, UUID userId, int seatNum1, int seatNum2) {
if (controllers.containsKey(tableId) && isTableOwner(tableId, userId)) {
controllers.get(tableId).swapSeats(seatNum1, seatNum2);
}
}
@Override
public void construct(UUID tableId) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).construct();
}
}
@Override
public void initTournament(UUID tableId) {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).initTournament();
}
}
@Override
public void addPlayer(UUID userId, UUID tableId, TournamentPlayer player) throws GameException {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).addPlayer(userId, player.getPlayer(), player.getPlayerType(), player.getDeck());
}
}
@Override
public void removeTable(UUID tableId) {
TableController tableController = controllers.get(tableId);
if (tableController != null) {
@ -365,21 +399,22 @@ public enum TableManager {
// If table is not finished, the table has to be removed completly because it's not a normal state (if finished it will be removed in GamesRoomImpl.Update())
if (table.getState() != TableState.FINISHED) {
if (game != null) {
GameManager.instance.removeGame(game.getId());
managerFactory.gameManager().removeGame(game.getId());
// something goes wrong, so don't add it to ended stats
}
GamesRoomManager.instance.removeTable(tableId);
managerFactory.gamesRoomManager().removeTable(tableId);
}
}
}
@Override
public void debugServerState() {
logger.debug("--- Server state ----------------------------------------------");
Collection<User> users = UserManager.instance.getUsers();
Collection<User> users = managerFactory.userManager().getUsers();
logger.debug("--------User: " + users.size() + " [userId | since | lock | name -----------------------");
for (User user : users) {
Optional<Session> session = SessionManager.instance.getSession(user.getSessionId());
Optional<Session> session = managerFactory.sessionManager().getSession(user.getSessionId());
String sessionState = "N";
if (session.isPresent()) {
if (session.get().isLocked()) {
@ -393,14 +428,14 @@ public enum TableManager {
+ " | " + sessionState
+ " | " + user.getName() + " (" + user.getUserState().toString() + " - " + user.getPingInfo() + ')');
}
List<ChatSession> chatSessions = ChatManager.instance.getChatSessions();
List<ChatSession> chatSessions = managerFactory.chatManager().getChatSessions();
logger.debug("------- ChatSessions: " + chatSessions.size() + " ----------------------------------");
for (ChatSession chatSession : chatSessions) {
logger.debug(chatSession.getChatId() + " " + formatter.format(chatSession.getCreateTime()) + ' ' + chatSession.getInfo() + ' ' + chatSession.getClients().values().toString());
}
logger.debug("------- Games: " + GameManager.instance.getNumberActiveGames() + " --------------------------------------------");
logger.debug(" Active Game Worker: " + ThreadExecutor.instance.getActiveThreads(ThreadExecutor.instance.getGameExecutor()));
for (Entry<UUID, GameController> entry : GameManager.instance.getGameController().entrySet()) {
logger.debug("------- Games: " + managerFactory.gameManager().getNumberActiveGames() + " --------------------------------------------");
logger.debug(" Active Game Worker: " + managerFactory.threadExecutor().getActiveThreads(managerFactory.threadExecutor().getGameExecutor()));
for (Entry<UUID, GameController> entry : managerFactory.gameManager().getGameController().entrySet()) {
logger.debug(entry.getKey() + entry.getValue().getPlayerNameList());
}
logger.debug("--- Server state END ------------------------------------------");

View file

@ -10,14 +10,13 @@ import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.players.net.UserData;
import mage.server.draft.DraftSession;
import mage.server.game.GameManager;
import mage.server.game.GameSessionPlayer;
import mage.server.managers.ManagerFactory;
import mage.server.rating.GlickoRating;
import mage.server.rating.GlickoRatingSystem;
import mage.server.record.UserStats;
import mage.server.record.UserStatsRepository;
import mage.server.tournament.TournamentController;
import mage.server.tournament.TournamentManager;
import mage.server.tournament.TournamentSession;
import mage.server.util.ServerMessagesUtil;
import mage.server.util.SystemUtil;
@ -43,6 +42,7 @@ public class User {
Offline // set if the user was disconnected and expired or regularly left XMage. Removed is the user later after some time
}
private final ManagerFactory managerFactory;
private final UUID userId;
private final String userName;
private final String host;
@ -68,7 +68,8 @@ public class User {
private String clientVersion;
private String userIdStr;
public User(String userName, String host, AuthorizedUser authorizedUser) {
public User(ManagerFactory managerFactory, String userName, String host, AuthorizedUser authorizedUser) {
this.managerFactory = managerFactory;
this.userId = UUID.randomUUID();
this.userName = userName;
this.host = host;
@ -181,7 +182,7 @@ public class User {
// Because watched games don't get restored after reconnection call stop watching
for (Iterator<UUID> iterator = watchedGames.iterator(); iterator.hasNext(); ) {
UUID gameId = iterator.next();
GameManager.instance.stopWatching(gameId, userId);
managerFactory.gameManager().stopWatching(gameId, userId);
iterator.remove();
}
ServerMessagesUtil.instance.incLostConnection();
@ -227,7 +228,7 @@ public class User {
public void fireCallback(final ClientCallback call) {
if (isConnected()) {
SessionManager.instance.getSession(sessionId).ifPresent(session
managerFactory.sessionManager().getSession(sessionId).ifPresent(session
-> session.fireCallback(call)
);
}
@ -288,27 +289,27 @@ public class User {
public void sendPlayerUUID(final UUID gameId, final UUID data) {
lastActivity = new Date();
GameManager.instance.sendPlayerUUID(gameId, userId, data);
managerFactory.gameManager().sendPlayerUUID(gameId, userId, data);
}
public void sendPlayerString(final UUID gameId, final String data) {
lastActivity = new Date();
GameManager.instance.sendPlayerString(gameId, userId, data);
managerFactory.gameManager().sendPlayerString(gameId, userId, data);
}
public void sendPlayerManaType(final UUID gameId, final UUID playerId, final ManaType data) {
lastActivity = new Date();
GameManager.instance.sendPlayerManaType(gameId, playerId, userId, data);
managerFactory.gameManager().sendPlayerManaType(gameId, playerId, userId, data);
}
public void sendPlayerBoolean(final UUID gameId, final Boolean data) {
lastActivity = new Date();
GameManager.instance.sendPlayerBoolean(gameId, userId, data);
managerFactory.gameManager().sendPlayerBoolean(gameId, userId, data);
}
public void sendPlayerInteger(final UUID gameId, final Integer data) {
lastActivity = new Date();
GameManager.instance.sendPlayerInteger(gameId, userId, data);
managerFactory.gameManager().sendPlayerInteger(gameId, userId, data);
}
public void updateLastActivity(String pingInfo) {
@ -338,7 +339,7 @@ public class User {
}
for (Iterator<Entry<UUID, UUID>> iterator = userTournaments.entrySet().iterator(); iterator.hasNext(); ) {
Entry<UUID, UUID> next = iterator.next();
Optional<TournamentController> tournamentController = TournamentManager.instance.getTournamentController(next.getValue());
Optional<TournamentController> tournamentController = managerFactory.tournamentManager().getTournamentController(next.getValue());
if (tournamentController.isPresent()) {
ccTournamentStarted(next.getValue(), next.getKey());
tournamentController.get().rejoin(next.getKey());
@ -349,7 +350,7 @@ public class User {
for (Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) {
ccGameStarted(entry.getValue().getGameId(), entry.getKey());
entry.getValue().init();
GameManager.instance.sendPlayerString(entry.getValue().getGameId(), userId, "");
managerFactory.gameManager().sendPlayerString(entry.getValue().getGameId(), userId, "");
}
for (Entry<UUID, DraftSession> entry : draftSessions.entrySet()) {
@ -362,7 +363,7 @@ public class User {
entry.getValue().construct(0); // TODO: Check if this is correct
}
for (Entry<UUID, Deck> entry : sideboarding.entrySet()) {
Optional<TableController> controller = TableManager.instance.getController(entry.getKey());
Optional<TableController> controller = managerFactory.tableManager().getController(entry.getKey());
if (controller.isPresent()) {
ccSideboard(entry.getValue(), entry.getKey(), controller.get().getRemainingTime(), controller.get().getOptions().isLimited());
} else {
@ -427,32 +428,32 @@ public class User {
draftSessions.clear();
logger.trace("REMOVE " + userName + " Tournament sessions " + userTournaments.size());
for (UUID tournamentId : userTournaments.values()) {
TournamentManager.instance.quit(tournamentId, userId);
managerFactory.tournamentManager().quit(tournamentId, userId);
}
userTournaments.clear();
constructing.clear();
logger.trace("REMOVE " + userName + " Tables " + tables.size());
for (Entry<UUID, Table> entry : tables.entrySet()) {
logger.debug("-- leave tableId: " + entry.getValue().getId());
TableManager.instance.leaveTable(userId, entry.getValue().getId());
managerFactory.tableManager().leaveTable(userId, entry.getValue().getId());
}
tables.clear();
sideboarding.clear();
logger.trace("REMOVE " + userName + " Game sessions: " + gameSessions.size());
for (GameSessionPlayer gameSessionPlayer : gameSessions.values()) {
logger.debug("-- kill game session of gameId: " + gameSessionPlayer.getGameId());
GameManager.instance.quitMatch(gameSessionPlayer.getGameId(), userId);
managerFactory.gameManager().quitMatch(gameSessionPlayer.getGameId(), userId);
gameSessionPlayer.quitGame();
}
gameSessions.clear();
logger.trace("REMOVE " + userName + " watched Games " + watchedGames.size());
for (Iterator<UUID> it = watchedGames.iterator(); it.hasNext(); ) { // Iterator to prevent ConcurrentModificationException
UUID gameId = it.next();
GameManager.instance.stopWatching(gameId, userId);
managerFactory.gameManager().stopWatching(gameId, userId);
}
watchedGames.clear();
logger.trace("REMOVE " + userName + " Chats ");
ChatManager.instance.removeUser(userId, reason);
managerFactory.chatManager().removeUser(userId, reason);
}
public void setUserData(UserData userData) {
@ -788,7 +789,7 @@ public class User {
if (table.getState() == TableState.FINISHED) {
number++;
} else {
Optional<TableController> tableController = TableManager.instance.getController(table.getId());
Optional<TableController> tableController = managerFactory.tableManager().getController(table.getId());
if (!tableController.isPresent()) {
logger.error("table not found : " + table.getId());
} else if (tableController.get().isUserStillActive(userId)) {

View file

@ -1,9 +1,10 @@
package mage.server;
import mage.server.User.UserState;
import mage.server.managers.UserManager;
import mage.server.managers.ManagerFactory;
import mage.server.record.UserStats;
import mage.server.record.UserStatsRepository;
import mage.server.util.ThreadExecutor;
import mage.view.UserView;
import org.apache.log4j.Logger;
@ -19,37 +20,43 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
*
* @author BetaSteward_at_googlemail.com
*/
public enum UserManager {
instance;
public class UserManagerImpl implements UserManager {
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
private static final Logger logger = Logger.getLogger(UserManager.class);
private static final Logger logger = Logger.getLogger(UserManagerImpl.class);
protected final ScheduledExecutorService expireExecutor = Executors.newSingleThreadScheduledExecutor();
protected final ScheduledExecutorService userListExecutor = Executors.newSingleThreadScheduledExecutor();
private List<UserView> userInfoList = new ArrayList<>();
private final ManagerFactory managerFactory;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, User> users = new ConcurrentHashMap<>();
private static final ExecutorService USER_EXECUTOR = ThreadExecutor.instance.getCallExecutor();
private ExecutorService USER_EXECUTOR;
UserManager() {
public UserManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
public void init() {
USER_EXECUTOR = managerFactory.threadExecutor().getCallExecutor();
expireExecutor.scheduleAtFixedRate(this::checkExpired, 60, 60, TimeUnit.SECONDS);
userListExecutor.scheduleAtFixedRate(this::updateUserInfoList, 4, 4, TimeUnit.SECONDS);
}
@Override
public Optional<User> createUser(String userName, String host, AuthorizedUser authorizedUser) {
if (getUserByName(userName).isPresent()) {
return Optional.empty(); //user already exists
}
User user = new User(userName, host, authorizedUser);
User user = new User(managerFactory, userName, host, authorizedUser);
final Lock w = lock.writeLock();
w.lock();
try {
@ -60,6 +67,7 @@ public enum UserManager {
return Optional.of(user);
}
@Override
public Optional<User> getUser(UUID userId) {
if (!users.containsKey(userId)) {
//logger.warn(String.format("User with id %s could not be found", userId), new Throwable()); // TODO: remove after session freezes fixed
@ -69,6 +77,7 @@ public enum UserManager {
}
}
@Override
public Optional<User> getUserByName(String userName) {
final Lock r = lock.readLock();
r.lock();
@ -81,6 +90,7 @@ public enum UserManager {
}
@Override
public Collection<User> getUsers() {
List<User> userList = new ArrayList<>();
final Lock r = lock.readLock();
@ -93,6 +103,7 @@ public enum UserManager {
return userList;
}
@Override
public boolean connectToSession(String sessionId, UUID userId) {
if (userId != null) {
User user = users.get(userId);
@ -104,19 +115,19 @@ public enum UserManager {
return false;
}
@Override
public void disconnect(UUID userId, DisconnectReason reason) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = getUser(userId);
if (user.isPresent()) {
user.get().setSessionId("");
if (reason == DisconnectReason.Disconnected) {
removeUserFromAllTablesAndChat(userId, reason);
user.get().setUserState(UserState.Offline);
} else {
// ChatManager.instance.sendLostConnectionMessage(userId, reason);
}
}
}
@Override
public boolean isAdmin(UUID userId) {
if (userId != null) {
User user = users.get(userId);
@ -127,6 +138,7 @@ public enum UserManager {
return false;
}
@Override
public void removeUserFromAllTablesAndChat(final UUID userId, final DisconnectReason reason) {
if (userId != null) {
getUser(userId).ifPresent(user
@ -135,7 +147,7 @@ public enum UserManager {
try {
logger.info("USER REMOVE - " + user.getName() + " (" + reason.toString() + ") userId: " + userId + " [" + user.getGameInfo() + ']');
user.removeUserFromAllTables(reason);
ChatManager.instance.removeUser(user.getId(), reason);
managerFactory.chatManager().removeUser(user.getId(), reason);
logger.debug("USER REMOVE END - " + user.getName());
} catch (Exception ex) {
handleException(ex);
@ -146,13 +158,14 @@ public enum UserManager {
}
}
@Override
public void informUserOpponents(final UUID userId, final String message) {
if (userId != null) {
getUser(userId).ifPresent(user
-> USER_EXECUTOR.execute(
() -> {
try {
ChatManager.instance.sendMessageToUserChats(user.getId(), message);
managerFactory.chatManager().sendMessageToUserChats(user.getId(), message);
} catch (Exception ex) {
handleException(ex);
}
@ -161,6 +174,7 @@ public enum UserManager {
}
}
@Override
public boolean extendUserSession(UUID userId, String pingInfo) {
if (userId != null) {
User user = users.get(userId);
@ -245,7 +259,7 @@ public enum UserManager {
private void updateUserInfoList() {
try {
List<UserView> newUserInfoList = new ArrayList<>();
for (User user : UserManager.instance.getUsers()) {
for (User user : getUsers()) {
newUserInfoList.add(new UserView(
user.getName(),
user.getHost(),
@ -266,10 +280,12 @@ public enum UserManager {
}
}
@Override
public List<UserView> getUserInfoList() {
return userInfoList;
}
@Override
public void handleException(Exception ex) {
if (ex != null) {
logger.fatal("User manager exception ", ex);
@ -281,6 +297,7 @@ public enum UserManager {
}
}
@Override
public String getUserHistory(String userName) {
Optional<User> user = getUserByName(userName);
if (user.isPresent()) {
@ -295,6 +312,7 @@ public enum UserManager {
return "User " + userName + " not found";
}
@Override
public void updateUserHistory() {
USER_EXECUTOR.execute(() -> {
for (String updatedUser : UserStatsRepository.instance.updateUserStats()) {

View file

@ -1,5 +1,3 @@
package mage.server.draft;
import mage.MageException;
@ -9,10 +7,8 @@ import mage.game.events.Listener;
import mage.game.events.PlayerQueryEvent;
import mage.game.events.TableEvent;
import mage.players.Player;
import mage.server.TableManager;
import mage.server.UserManager;
import mage.server.game.GameController;
import mage.server.util.ThreadExecutor;
import mage.server.managers.ManagerFactory;
import mage.view.DraftPickView;
import org.apache.log4j.Logger;
@ -25,7 +21,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class DraftController {
@ -33,6 +28,7 @@ public class DraftController {
private static final Logger logger = Logger.getLogger(GameController.class);
public static final String INIT_FILE_PATH = "config" + File.separator + "init.txt";
private final ManagerFactory managerFactory;
private final ConcurrentMap<UUID, DraftSession> draftSessions = new ConcurrentHashMap<>();
private final ConcurrentMap<UUID, UUID> userPlayerMap;
private final UUID draftSessionId;
@ -40,7 +36,8 @@ public class DraftController {
private final UUID tableId;
private final UUID markedCard;
public DraftController(Draft draft, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId) {
public DraftController(ManagerFactory managerFactory, Draft draft, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId) {
this.managerFactory = managerFactory;
draftSessionId = UUID.randomUUID();
this.userPlayerMap = userPlayerMap;
this.draft = draft;
@ -61,8 +58,7 @@ public class DraftController {
endDraft();
break;
}
}
catch (MageException ex) {
} catch (MageException ex) {
logger.fatal("Table event listener error", ex);
}
}
@ -75,13 +71,12 @@ public class DraftController {
pickCard(event.getPlayerId(), event.getMax());
break;
}
}
catch (MageException ex) {
} catch (MageException ex) {
logger.fatal("Table event listener error", ex);
}
}
);
for (DraftPlayer player: draft.getPlayers()) {
for (DraftPlayer player : draft.getPlayers()) {
if (!player.getPlayer().isHuman()) {
player.setJoined();
logger.debug("player " + player.getPlayer().getId() + " has joined draft " + draft.getId());
@ -96,13 +91,13 @@ public class DraftController {
public void join(UUID userId) {
UUID playerId = userPlayerMap.get(userId);
DraftSession draftSession = new DraftSession(draft, userId, playerId);
DraftSession draftSession = new DraftSession(managerFactory, draft, userId, playerId);
draftSessions.put(playerId, draftSession);
UserManager.instance.getUser(userId).ifPresent(user-> {
user.addDraft(playerId, draftSession);
logger.debug("User " + user.getName() + " has joined draft " + draft.getId());
draft.getPlayer(playerId).setJoined();
});
managerFactory.userManager().getUser(userId).ifPresent(user -> {
user.addDraft(playerId, draftSession);
logger.debug("User " + user.getName() + " has joined draft " + draft.getId());
draft.getPlayer(playerId).setJoined();
});
checkStart();
}
@ -114,7 +109,7 @@ public class DraftController {
}
public boolean replacePlayer(Player oldPlayer, Player newPlayer) {
if (draft.replacePlayer(oldPlayer, newPlayer)) {
if (draft.replacePlayer(oldPlayer, newPlayer)) {
DraftSession draftSession = draftSessions.get(oldPlayer.getId());
if (draftSession != null) {
draftSession.draftOver(); // closes the draft panel of the replaced player
@ -128,12 +123,12 @@ public class DraftController {
private synchronized void checkStart() {
if (!draft.isStarted() && allJoined()) {
draft.setStarted();
ThreadExecutor.instance.getCallExecutor().execute(this::startDraft);
managerFactory.threadExecutor().getCallExecutor().execute(this::startDraft);
}
}
private void startDraft() {
for (final Entry<UUID, DraftSession> entry: draftSessions.entrySet()) {
for (final Entry<UUID, DraftSession> entry : draftSessions.entrySet()) {
if (!entry.getValue().init()) {
logger.fatal("Unable to initialize client for playerId " + entry.getKey());
//TODO: generate client error message
@ -147,7 +142,7 @@ public class DraftController {
if (!draft.allJoined()) {
return false;
}
for (DraftPlayer player: draft.getPlayers()) {
for (DraftPlayer player : draft.getPlayers()) {
if (player.getPlayer().isHuman() && !draftSessions.containsKey(player.getPlayer().getId())) {
return false;
}
@ -160,12 +155,12 @@ public class DraftController {
}
private void endDraft() throws MageException {
for (final DraftSession draftSession: draftSessions.values()) {
for (final DraftSession draftSession : draftSessions.values()) {
draftSession.draftOver();
draftSession.removeDraft();
}
TableManager.instance.endDraft(tableId, draft);
DraftManager.instance.removeDraft(draft.getId());
managerFactory.tableManager().endDraft(tableId, draft);
managerFactory.draftManager().removeDraft(draft.getId());
}
public void kill(UUID userId) {
@ -210,7 +205,7 @@ public class DraftController {
}
private synchronized void updateDraft() throws MageException {
for (final Entry<UUID, DraftSession> entry: draftSessions.entrySet()) {
for (final Entry<UUID, DraftSession> entry : draftSessions.entrySet()) {
entry.getValue().update();
}
}
@ -229,7 +224,7 @@ public class DraftController {
draft.setAbort(true);
try {
endDraft();
} catch(MageException ex) {
} catch (MageException ex) {
}
}

View file

@ -1,69 +1,83 @@
package mage.server.draft;
import mage.game.draft.Draft;
import mage.server.managers.DraftManager;
import mage.server.managers.ManagerFactory;
import mage.view.DraftPickView;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import mage.game.draft.Draft;
import mage.view.DraftPickView;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public enum DraftManager {
instance;
public class DraftManagerImpl implements DraftManager {
private final ManagerFactory managerFactory;
private final ConcurrentMap<UUID, DraftController> draftControllers = new ConcurrentHashMap<>();
public DraftManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public UUID createDraftSession(Draft draft, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId) {
DraftController draftController = new DraftController(draft, userPlayerMap, tableId);
DraftController draftController = new DraftController(managerFactory, draft, userPlayerMap, tableId);
draftControllers.put(draft.getId(), draftController);
return draftController.getSessionId();
}
@Override
public void joinDraft(UUID draftId, UUID userId) {
draftControllers.get(draftId).join(userId);
}
@Override
public void destroyChatSession(UUID gameId) {
draftControllers.remove(gameId);
}
@Override
public DraftPickView sendCardPick(UUID draftId, UUID userId, UUID cardId, Set<UUID> hiddenCards) {
return draftControllers.get(draftId).sendCardPick(userId, cardId, hiddenCards);
}
@Override
public void sendCardMark(UUID draftId, UUID userId, UUID cardId) {
draftControllers.get(draftId).sendCardMark(userId, cardId);
}
@Override
public void removeSession(UUID userId) {
for (DraftController controller: draftControllers.values()) {
for (DraftController controller : draftControllers.values()) {
controller.kill(userId);
}
}
@Override
public void kill(UUID draftId, UUID userId) {
draftControllers.get(draftId).kill(userId);
}
@Override
public void timeout(UUID gameId, UUID userId) {
draftControllers.get(gameId).timeout(userId);
}
@Override
public void removeDraft(UUID draftId) {
draftControllers.remove(draftId);
}
@Override
public DraftController getControllerByDraftId(UUID draftId) {
return draftControllers.get(draftId);
}
@Override
public Optional<DraftController> getController(UUID tableId) {
return draftControllers.values().stream().filter(controller -> controller.getTableId().equals(tableId)).findFirst();
}

View file

@ -1,6 +1,15 @@
package mage.server.draft;
import mage.game.draft.Draft;
import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.server.User;
import mage.server.managers.ManagerFactory;
import mage.view.DraftClientMessage;
import mage.view.DraftPickView;
import mage.view.DraftView;
import org.apache.log4j.Logger;
import java.rmi.RemoteException;
import java.util.Optional;
import java.util.Set;
@ -8,16 +17,6 @@ import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import mage.game.draft.Draft;
import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.server.User;
import mage.server.UserManager;
import mage.server.util.ThreadExecutor;
import mage.view.DraftClientMessage;
import mage.view.DraftPickView;
import mage.view.DraftView;
import org.apache.log4j.Logger;
/**
* @author BetaSteward_at_googlemail.com
@ -26,6 +25,7 @@ public class DraftSession {
protected static final Logger logger = Logger.getLogger(DraftSession.class);
private final ManagerFactory managerFactory;
protected final UUID userId;
protected final UUID playerId;
protected final Draft draft;
@ -33,9 +33,11 @@ public class DraftSession {
protected UUID markedCard;
private ScheduledFuture<?> futureTimeout;
protected static final ScheduledExecutorService timeoutExecutor = ThreadExecutor.instance.getTimeoutExecutor();
protected final ScheduledExecutorService timeoutExecutor;
public DraftSession(Draft draft, UUID userId, UUID playerId) {
public DraftSession(ManagerFactory managerFactory, Draft draft, UUID userId, UUID playerId) {
this.managerFactory = managerFactory;
this.timeoutExecutor = managerFactory.threadExecutor().getTimeoutExecutor();
this.userId = userId;
this.draft = draft;
this.playerId = playerId;
@ -44,12 +46,12 @@ public class DraftSession {
public boolean init() {
if (!killed) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (user.isPresent()) {
if (futureTimeout != null && !futureTimeout.isDone()) {
int remaining = (int) futureTimeout.getDelay(TimeUnit.SECONDS);
user.get().fireCallback(new ClientCallback(ClientCallbackMethod.DRAFT_INIT, draft.getId(),
new DraftClientMessage(getDraftView(), getDraftPickView(remaining))));
new DraftClientMessage(getDraftView(), getDraftPickView(remaining))));
}
return true;
}
@ -59,16 +61,16 @@ public class DraftSession {
public void update() {
if (!killed) {
UserManager.instance
managerFactory.userManager()
.getUser(userId).
ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.DRAFT_UPDATE, draft.getId(),
new DraftClientMessage(getDraftView(), null))));
new DraftClientMessage(getDraftView(), null))));
}
}
public void draftOver() {
if (!killed) {
UserManager.instance
managerFactory.userManager()
.getUser(userId)
.ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.DRAFT_OVER, draft.getId())));
}
@ -77,10 +79,10 @@ public class DraftSession {
public void pickCard(int timeout) {
if (!killed) {
setupTimeout(timeout);
UserManager.instance
managerFactory.userManager()
.getUser(userId)
.ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.DRAFT_PICK, draft.getId(),
new DraftClientMessage(getDraftView(), getDraftPickView(timeout)))));
new DraftClientMessage(getDraftView(), getDraftPickView(timeout)))));
}
}
@ -89,7 +91,7 @@ public class DraftSession {
cancelTimeout();
if (seconds > 0) {
futureTimeout = timeoutExecutor.schedule(
() -> DraftManager.instance.timeout(draft.getId(), userId),
() -> managerFactory.draftManager().timeout(draft.getId(), userId),
seconds, TimeUnit.SECONDS
);
}
@ -103,7 +105,7 @@ public class DraftSession {
protected void handleRemoteException(RemoteException ex) {
logger.fatal("DraftSession error ", ex);
DraftManager.instance.kill(draft.getId(), userId);
managerFactory.draftManager().kill(draft.getId(), userId);
}
public void setKilled() {
@ -119,7 +121,7 @@ public class DraftSession {
}
public void removeDraft() {
UserManager.instance.getUser(userId).ifPresent(user -> user.removeDraft(playerId));
managerFactory.userManager().getUser(userId).ifPresent(user -> user.removeDraft(playerId));
}

View file

@ -23,11 +23,11 @@ import mage.game.permanent.Permanent;
import mage.game.turn.Phase;
import mage.interfaces.Action;
import mage.players.Player;
import mage.server.*;
import mage.server.util.ConfigSettings;
import mage.server.Main;
import mage.server.User;
import mage.server.managers.ManagerFactory;
import mage.server.util.Splitter;
import mage.server.util.SystemUtil;
import mage.server.util.ThreadExecutor;
import mage.utils.StreamUtils;
import mage.utils.timer.PriorityTimer;
import mage.view.*;
@ -53,13 +53,14 @@ public class GameController implements GameCallback {
private static final int GAME_TIMEOUTS_CHECK_JOINING_STATUS_EVERY_SECS = 10; // checks and inform players about joining status
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 final ExecutorService gameExecutor;
private static final Logger logger = Logger.getLogger(GameController.class);
protected final ScheduledExecutorService joinWaitingExecutor = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> futureTimeout;
protected static final ScheduledExecutorService timeoutIdleExecutor = ThreadExecutor.instance.getTimeoutIdleExecutor();
private final ManagerFactory managerFactory;
protected final ScheduledExecutorService timeoutIdleExecutor;
private final ConcurrentMap<UUID, GameSessionPlayer> gameSessions = new ConcurrentHashMap<>();
private final ReadWriteLock gameSessionsLock = new ReentrantReadWriteLock();
@ -83,13 +84,16 @@ public class GameController implements GameCallback {
private int turnsToRollback;
private int requestsOpen;
public GameController(Game game, ConcurrentMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) {
public GameController(ManagerFactory managerFactory, Game game, ConcurrentMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) {
this.managerFactory = managerFactory;
gameExecutor = managerFactory.threadExecutor().getGameExecutor();
timeoutIdleExecutor = managerFactory.threadExecutor().getTimeoutIdleExecutor();
gameSessionId = UUID.randomUUID();
this.userPlayerMap = userPlayerMap;
chatId = ChatManager.instance.createChatSession("Game " + game.getId());
chatId = managerFactory.chatManager().createChatSession("Game " + game.getId());
this.userReqestingRollback = null;
this.game = game;
this.game.setSaveGame(ConfigSettings.instance.isSaveGameActivated());
this.game.setSaveGame(managerFactory.configSettings().isSaveGameActivated());
this.tableId = tableId;
this.choosingPlayerId = choosingPlayerId;
this.gameOptions = gameOptions;
@ -103,7 +107,7 @@ public class GameController implements GameCallback {
for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
gameSessionPlayer.cleanUp();
}
ChatManager.instance.destroyChatSession(chatId);
managerFactory.chatManager().destroyChatSession(chatId);
for (PriorityTimer priorityTimer : timers.values()) {
priorityTimer.cancel();
}
@ -120,11 +124,11 @@ public class GameController implements GameCallback {
updateGame();
break;
case INFO:
ChatManager.instance.broadcast(chatId, "", event.getMessage(), MessageColor.BLACK, true, event.getGame(), MessageType.GAME, null);
managerFactory.chatManager().broadcast(chatId, "", event.getMessage(), MessageColor.BLACK, true, event.getGame(), MessageType.GAME, null);
logger.trace(game.getId() + " " + event.getMessage());
break;
case STATUS:
ChatManager.instance.broadcast(chatId, "", event.getMessage(), MessageColor.ORANGE, event.getWithTime(), event.getWithTurnInfo() ? event.getGame() : null, MessageType.GAME, null);
managerFactory.chatManager().broadcast(chatId, "", event.getMessage(), MessageColor.ORANGE, event.getWithTime(), event.getWithTurnInfo() ? event.getGame() : null, MessageType.GAME, null);
logger.trace(game.getId() + " " + event.getMessage());
break;
case ERROR:
@ -273,7 +277,7 @@ public class GameController implements GameCallback {
logger.fatal("- userId: " + userId);
return;
}
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (!user.isPresent()) {
logger.fatal("User not found : " + userId);
return;
@ -286,7 +290,7 @@ public class GameController implements GameCallback {
GameSessionPlayer gameSession = gameSessions.get(playerId);
String joinType;
if (gameSession == null) {
gameSession = new GameSessionPlayer(game, userId, playerId);
gameSession = new GameSessionPlayer(managerFactory, game, userId, playerId);
final Lock w = gameSessionsLock.writeLock();
w.lock();
try {
@ -300,7 +304,7 @@ public class GameController implements GameCallback {
}
user.get().addGame(playerId, gameSession);
logger.debug("Player " + player.getName() + ' ' + playerId + " has " + joinType + " gameId: " + game.getId());
ChatManager.instance.broadcast(chatId, "", game.getPlayer(playerId).getLogName() + " has " + joinType + " the game", MessageColor.ORANGE, true, game, MessageType.GAME, null);
managerFactory.chatManager().broadcast(chatId, "", game.getPlayer(playerId).getLogName() + " has " + joinType + " the game", MessageColor.ORANGE, true, game, MessageType.GAME, null);
checkStart();
}
@ -335,7 +339,7 @@ public class GameController implements GameCallback {
// join the game because player has not joined or was removed because of disconnect
String problemPlayerFixes;
user.removeConstructing(player.getId());
GameManager.instance.joinGame(game.getId(), user.getId());
managerFactory.gameManager().joinGame(game.getId(), user.getId());
logger.warn("Forced join of player " + player.getName() + " (" + user.getUserState() + ") to gameId: " + game.getId());
if (user.isConnected()) {
// init game session, see reconnect()
@ -345,7 +349,7 @@ public class GameController implements GameCallback {
logger.warn("Send forced game start event for player " + player.getName() + " in gameId: " + game.getId());
user.ccGameStarted(session.getGameId(), player.getId());
session.init();
GameManager.instance.sendPlayerString(session.getGameId(), user.getId(), "");
managerFactory.gameManager().sendPlayerString(session.getGameId(), user.getId(), "");
} else {
problemPlayerFixes = "leave on broken game session";
logger.error("Can't find game session for forced join, leave it: player " + player.getName() + " in gameId: " + game.getId());
@ -357,7 +361,7 @@ public class GameController implements GameCallback {
player.leave();
}
ChatManager.instance.broadcast(chatId, player.getName(), user.getPingInfo()
managerFactory.chatManager().broadcast(chatId, player.getName(), user.getPingInfo()
+ " is forced to join the game (waiting ends after "
+ GAME_TIMEOUTS_CANCEL_PLAYER_GAME_JOINING_AFTER_INACTIVE_SECS
+ " secs, applied fixes: " + problemPlayerFixes + ")",
@ -382,7 +386,7 @@ public class GameController implements GameCallback {
private Optional<User> getUserByPlayerId(UUID playerId) {
for (Map.Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
if (entry.getValue().equals(playerId)) {
return UserManager.instance.getUser(entry.getKey());
return managerFactory.userManager().getUser(entry.getKey());
}
}
return Optional.empty();
@ -391,7 +395,7 @@ public class GameController implements GameCallback {
private void checkStart() {
if (allJoined()) {
joinWaitingExecutor.shutdownNow();
ThreadExecutor.instance.getCallExecutor().execute(this::startGame);
managerFactory.threadExecutor().getCallExecutor().execute(this::startGame);
}
}
@ -423,14 +427,14 @@ public class GameController implements GameCallback {
}
if (!isAllowedToWatch(userId)) {
// Dont want people on our ignore list to stalk us
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
user.showUserMessage("Not allowed", "You are banned from watching this game");
ChatManager.instance.broadcast(chatId, user.getName(), " tried to join, but is banned", MessageColor.BLUE, true, game, ChatMessage.MessageType.STATUS, null);
managerFactory.chatManager().broadcast(chatId, user.getName(), " tried to join, but is banned", MessageColor.BLUE, true, game, ChatMessage.MessageType.STATUS, null);
});
return false;
}
UserManager.instance.getUser(userId).ifPresent(user -> {
GameSessionWatcher gameWatcher = new GameSessionWatcher(userId, game, false);
managerFactory.userManager().getUser(userId).ifPresent(user -> {
GameSessionWatcher gameWatcher = new GameSessionWatcher(managerFactory.userManager(), userId, game, false);
final Lock w = gameWatchersLock.writeLock();
w.lock();
try {
@ -440,7 +444,7 @@ public class GameController implements GameCallback {
}
gameWatcher.init();
user.addGameWatchInfo(game.getId());
ChatManager.instance.broadcast(chatId, user.getName(), " has started watching", MessageColor.BLUE, true, game, ChatMessage.MessageType.STATUS, null);
managerFactory.chatManager().broadcast(chatId, user.getName(), " has started watching", MessageColor.BLUE, true, game, ChatMessage.MessageType.STATUS, null);
});
return true;
}
@ -453,8 +457,8 @@ public class GameController implements GameCallback {
} finally {
w.unlock();
}
UserManager.instance.getUser(userId).ifPresent(user -> {
ChatManager.instance.broadcast(chatId, user.getName(), " has stopped watching", MessageColor.BLUE, true, game, ChatMessage.MessageType.STATUS, null);
managerFactory.userManager().getUser(userId).ifPresent(user -> {
managerFactory.chatManager().broadcast(chatId, user.getName(), " has stopped watching", MessageColor.BLUE, true, game, ChatMessage.MessageType.STATUS, null);
});
}
@ -628,7 +632,7 @@ public class GameController implements GameCallback {
gameSession.requestPermissionToSeeHandCards(userIdRequester);
} else {
// player does not allow the request
UserManager.instance.getUser(userIdRequester).ifPresent(requester -> {
managerFactory.userManager().getUser(userIdRequester).ifPresent(requester -> {
requester.showUserMessage("Request to show hand cards", "Player " + grantingPlayer.getName() + " does not allow to request to show hand cards!");
});
}
@ -640,7 +644,7 @@ public class GameController implements GameCallback {
}
} else {
// user can already see the cards
UserManager.instance.getUser(userIdRequester).ifPresent(requester -> {
managerFactory.userManager().getUser(userIdRequester).ifPresent(requester -> {
requester.showUserMessage("Request to show hand cards", "You can see already the hand cards of player " + grantingPlayer.getName() + '!');
});
@ -653,9 +657,9 @@ public class GameController implements GameCallback {
Player viewLimitedDeckPlayer = game.getPlayer(userIdRequester);
if (viewLimitedDeckPlayer != null) {
if (viewLimitedDeckPlayer.isHuman()) {
for (MatchPlayer p : TableManager.instance.getTable(tableId).getMatch().getPlayers()) {
for (MatchPlayer p : managerFactory.tableManager().getTable(tableId).getMatch().getPlayers()) {
if (p.getPlayer().getId().equals(userIdRequester)) {
Optional<User> u = UserManager.instance.getUser(origId);
Optional<User> u = managerFactory.userManager().getUser(origId);
if (u.isPresent() && p.getDeck() != null) {
u.get().ccViewLimitedDeck(p.getDeck(), tableId, requestsOpen, true);
}
@ -698,8 +702,8 @@ public class GameController implements GameCallback {
if (player != null) {
String sb = player.getLogName()
+ " has timed out (player had priority and was not active for "
+ ConfigSettings.instance.getMaxSecondsIdle() + " seconds ) - Auto concede.";
ChatManager.instance.broadcast(chatId, "", sb, MessageColor.BLACK, true, game, MessageType.STATUS, null);
+ managerFactory.configSettings().getMaxSecondsIdle() + " seconds ) - Auto concede.";
managerFactory.chatManager().broadcast(chatId, "", sb, MessageColor.BLACK, true, game, MessageType.STATUS, null);
game.idleTimeout(playerId);
}
}
@ -712,7 +716,7 @@ public class GameController implements GameCallback {
for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
gameWatcher.gameOver(message);
}
TableManager.instance.endGame(tableId);
managerFactory.tableManager().endGame(tableId);
}
public UUID getSessionId() {
@ -763,7 +767,7 @@ public class GameController implements GameCallback {
}
private synchronized void endGameInfo() {
Table table = TableManager.instance.getTable(tableId);
Table table = managerFactory.tableManager().getTable(tableId);
if (table != null) {
if (table.getMatch() != null) {
for (final GameSessionPlayer gameSession : getGameSessions()) {
@ -784,7 +788,7 @@ public class GameController implements GameCallback {
}
private synchronized void choosePile(UUID playerId, final String message, final List<? extends Card> pile1, final List<? extends Card> pile2) throws MageException {
perform(playerId, playerId1 -> getGameSession(playerId1).choosePile(message, new CardsView(pile1), new CardsView(pile2)));
perform(playerId, playerId1 -> getGameSession(playerId1).choosePile(message, new CardsView(game, pile1), new CardsView(game, pile2)));
}
private synchronized void chooseMode(UUID playerId, final Map<UUID, String> modes, final String message) throws MageException {
@ -1012,7 +1016,7 @@ public class GameController implements GameCallback {
cancelTimeout();
futureTimeout = timeoutIdleExecutor.schedule(
() -> idleTimeout(playerId),
Main.isTestMode() ? 3600 : ConfigSettings.instance.getMaxSecondsIdle(),
Main.isTestMode() ? 3600 : managerFactory.configSettings().getMaxSecondsIdle(),
TimeUnit.SECONDS
);
}
@ -1094,7 +1098,7 @@ public class GameController implements GameCallback {
}
public boolean isAllowedToWatch(UUID userId) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (user.isPresent()) {
return !gameOptions.bannedUsers.contains(user.get().getName());
}
@ -1208,7 +1212,7 @@ public class GameController implements GameCallback {
public String getPingsInfo() {
List<String> usersInfo = new ArrayList<>();
for (Map.Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
Optional<User> user = UserManager.instance.getUser(entry.getKey());
Optional<User> user = managerFactory.userManager().getUser(entry.getKey());
user.ifPresent(u -> usersInfo.add("* " + u.getName() + ": " + u.getPingInfo()));
}
Collections.sort(usersInfo);
@ -1216,7 +1220,7 @@ public class GameController implements GameCallback {
List<String> watchersinfo = new ArrayList<>();
for (Map.Entry<UUID, GameSessionWatcher> entry : watchers.entrySet()) {
Optional<User> user = UserManager.instance.getUser(entry.getValue().userId);
Optional<User> user = managerFactory.userManager().getUser(entry.getValue().userId);
user.ifPresent(u -> watchersinfo.add("* " + u.getName() + ": " + u.getPingInfo()));
}
Collections.sort(watchersinfo);

View file

@ -5,6 +5,8 @@ import mage.constants.ManaType;
import mage.constants.PlayerAction;
import mage.game.Game;
import mage.game.GameOptions;
import mage.server.managers.GameManager;
import mage.server.managers.ManagerFactory;
import mage.view.GameView;
import java.util.HashMap;
@ -20,14 +22,19 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author BetaSteward_at_googlemail.com
*/
public enum GameManager {
instance;
public class GameManagerImpl implements GameManager {
private final ManagerFactory managerFactory;
private final ConcurrentMap<UUID, GameController> gameControllers = new ConcurrentHashMap<>();
private final ReadWriteLock gameControllersLock = new ReentrantReadWriteLock();
public GameManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public UUID createGameSession(Game game, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) {
GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId, gameOptions);
GameController gameController = new GameController(managerFactory, game, userPlayerMap, tableId, choosingPlayerId, gameOptions);
final Lock w = gameControllersLock.writeLock();
w.lock();
try {
@ -48,6 +55,7 @@ public enum GameManager {
}
}
@Override
public void joinGame(UUID gameId, UUID userId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -55,6 +63,7 @@ public enum GameManager {
}
}
@Override
public Optional<UUID> getChatId(UUID gameId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -63,6 +72,7 @@ public enum GameManager {
return Optional.empty();
}
@Override
public void sendPlayerUUID(UUID gameId, UUID userId, UUID data) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -70,6 +80,7 @@ public enum GameManager {
}
}
@Override
public void sendPlayerString(UUID gameId, UUID userId, String data) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -77,6 +88,7 @@ public enum GameManager {
}
}
@Override
public void sendPlayerManaType(UUID gameId, UUID playerId, UUID userId, ManaType data) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -84,6 +96,7 @@ public enum GameManager {
}
}
@Override
public void sendPlayerBoolean(UUID gameId, UUID userId, Boolean data) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -91,6 +104,7 @@ public enum GameManager {
}
}
@Override
public void sendPlayerInteger(UUID gameId, UUID userId, Integer data) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -98,6 +112,7 @@ public enum GameManager {
}
}
@Override
public void quitMatch(UUID gameId, UUID userId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -105,6 +120,7 @@ public enum GameManager {
}
}
@Override
public void sendPlayerAction(PlayerAction playerAction, UUID gameId, UUID userId, Object data) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -112,6 +128,7 @@ public enum GameManager {
}
}
@Override
public boolean watchGame(UUID gameId, UUID userId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -120,6 +137,7 @@ public enum GameManager {
return false;
}
@Override
public void stopWatching(UUID gameId, UUID userId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -127,6 +145,7 @@ public enum GameManager {
}
}
@Override
public void cheat(UUID gameId, UUID userId, UUID playerId, DeckCardLists deckList) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -134,6 +153,7 @@ public enum GameManager {
}
}
@Override
public boolean cheat(UUID gameId, UUID userId, UUID playerId, String cardName) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -142,6 +162,7 @@ public enum GameManager {
return false;
}
@Override
public void removeGame(UUID gameId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -156,6 +177,7 @@ public enum GameManager {
}
}
@Override
public boolean saveGame(UUID gameId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -164,6 +186,7 @@ public enum GameManager {
return false;
}
@Override
public GameView getGameView(UUID gameId, UUID playerId) {
GameController gameController = getGameControllerSafe(gameId);
if (gameController != null) {
@ -172,10 +195,12 @@ public enum GameManager {
return null;
}
@Override
public int getNumberActiveGames() {
return getGameController().size();
}
@Override
public Map<UUID, GameController> getGameController() {
Map<UUID, GameController> newControllers = new HashMap<>();
final Lock r = gameControllersLock.readLock();

View file

@ -11,8 +11,8 @@ import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.players.Player;
import mage.server.User;
import mage.server.UserManager;
import mage.server.util.ThreadExecutor;
import mage.server.managers.UserManager;
import mage.server.managers.ManagerFactory;
import mage.view.*;
import org.apache.log4j.Logger;
@ -28,12 +28,15 @@ public class GameSessionPlayer extends GameSessionWatcher {
private static final Logger logger = Logger.getLogger(GameSessionPlayer.class);
private final UserManager userManager;
private final UUID playerId;
private static final ExecutorService callExecutor = ThreadExecutor.instance.getCallExecutor();
private final ExecutorService callExecutor;
public GameSessionPlayer(Game game, UUID userId, UUID playerId) {
super(userId, game, true);
public GameSessionPlayer(ManagerFactory managerFactory, Game game, UUID userId, UUID playerId) {
super(managerFactory.userManager(), userId, game, true);
this.userManager = managerFactory.userManager();
callExecutor = managerFactory.threadExecutor().getCallExecutor();
this.playerId = playerId;
}
@ -44,14 +47,14 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void ask(final String question, final Map<String, Serializable> options) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_ASK, game.getId(), new GameClientMessage(getGameView(), question, options)))
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_ASK, game.getId(), new GameClientMessage(getGameView(), question, options)))
);
}
}
public void target(final String question, final CardsView cardView, final Set<UUID> targets, final boolean required, final Map<String, Serializable> options) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> {
userManager.getUser(userId).ifPresent(user -> {
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_TARGET, game.getId(), new GameClientMessage(getGameView(), question, cardView, targets, required, options)));
});
@ -60,13 +63,13 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void select(final String message, final Map<String, Serializable> options) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_SELECT, game.getId(), new GameClientMessage(getGameView(), message, options))));
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_SELECT, game.getId(), new GameClientMessage(getGameView(), message, options))));
}
}
public void chooseAbility(final AbilityPickerView abilities) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user
userManager.getUser(userId).ifPresent(user
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_CHOOSE_ABILITY, game.getId(), abilities)));
}
@ -74,7 +77,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void choosePile(final String message, final CardsView pile1, final CardsView pile2) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user
userManager.getUser(userId).ifPresent(user
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_CHOOSE_PILE, game.getId(), new GameClientMessage(message, pile1, pile2))));
}
@ -82,7 +85,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void chooseChoice(final Choice choice) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user
userManager.getUser(userId).ifPresent(user
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_CHOOSE_CHOICE, game.getId(), new GameClientMessage(choice))));
}
@ -90,14 +93,14 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void playMana(final String message, final Map<String, Serializable> options) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user
userManager.getUser(userId).ifPresent(user
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_PLAY_MANA, game.getId(), new GameClientMessage(getGameView(), message, options))));
}
}
public void playXMana(final String message) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user
userManager.getUser(userId).ifPresent(user
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_PLAY_XMANA, game.getId(), new GameClientMessage(getGameView(), message))));
}
@ -105,7 +108,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void getAmount(final String message, final int min, final int max) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> {
userManager.getUser(userId).ifPresent(user -> {
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_GET_AMOUNT, game.getId(), new GameClientMessage(message, min, max)));
});
}
@ -113,15 +116,15 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void endGameInfo(Table table) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.END_GAME_INFO, game.getId(), getGameEndView(playerId, table))));
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.END_GAME_INFO, game.getId(), getGameEndView(playerId, table))));
}
}
public void requestPermissionToRollbackTurn(UUID requestingUserId, int numberTurns) {
if (!killed) {
Optional<User> requestingUser = UserManager.instance.getUser(requestingUserId);
Optional<User> requestedUser = UserManager.instance.getUser(userId);
Optional<User> requestingUser = userManager.getUser(requestingUserId);
Optional<User> requestedUser = userManager.getUser(userId);
if (requestedUser.isPresent() && requestingUser.isPresent()) {
String message;
switch (numberTurns) {
@ -147,8 +150,8 @@ public class GameSessionPlayer extends GameSessionWatcher {
public void requestPermissionToSeeHandCards(UUID watcherId) {
if (!killed) {
Optional<User> watcher = UserManager.instance.getUser(watcherId);
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> watcher = userManager.getUser(watcherId);
Optional<User> user = userManager.getUser(userId);
if (user.isPresent() && watcher.isPresent()) {
UserRequestMessage userRequestMessage = new UserRequestMessage(
"User request",
@ -217,7 +220,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
}
public void removeGame() {
UserManager.instance.getUser(userId).ifPresent(user -> user.removeGame(playerId));
userManager.getUser(userId).ifPresent(user -> user.removeGame(playerId));
}

View file

@ -6,7 +6,7 @@ import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.players.Player;
import mage.server.User;
import mage.server.UserManager;
import mage.server.managers.UserManager;
import mage.view.GameClientMessage;
import mage.view.GameEndView;
import mage.view.GameView;
@ -25,12 +25,14 @@ public class GameSessionWatcher {
protected static final Logger logger = Logger.getLogger(GameSessionWatcher.class);
private final UserManager userManager;
protected final UUID userId;
protected final Game game;
protected boolean killed = false;
protected final boolean isPlayer;
public GameSessionWatcher(UUID userId, Game game, boolean isPlayer) {
public GameSessionWatcher(UserManager userManager, UUID userId, Game game, boolean isPlayer) {
this.userManager = userManager;
this.userId = userId;
this.game = game;
this.isPlayer = isPlayer;
@ -38,7 +40,7 @@ public class GameSessionWatcher {
public boolean init() {
if (!killed) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = userManager.getUser(userId);
if (user.isPresent()) {
user.get().fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INIT, game.getId(), getGameView()));
return true;
@ -49,28 +51,28 @@ public class GameSessionWatcher {
public void update() {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_UPDATE, game.getId(), getGameView())));
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_UPDATE, game.getId(), getGameView())));
}
}
public void inform(final String message) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM, game.getId(), new GameClientMessage(getGameView(), message))));
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM, game.getId(), new GameClientMessage(getGameView(), message))));
}
}
public void informPersonal(final String message) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM_PERSONAL, game.getId(), new GameClientMessage(getGameView(), message))));
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM_PERSONAL, game.getId(), new GameClientMessage(getGameView(), message))));
}
}
public void gameOver(final String message) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> {
userManager.getUser(userId).ifPresent(user -> {
user.removeGameWatchInfo(game.getId());
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_OVER, game.getId(), message));
});
@ -86,7 +88,7 @@ public class GameSessionWatcher {
public void gameError(final String message) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_ERROR, game.getId(), message)));
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_ERROR, game.getId(), message)));
}
}

View file

@ -1,12 +1,5 @@
package mage.server.game;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import mage.MageException;
import mage.cards.decks.DeckCardLists;
import mage.constants.TableState;
@ -16,18 +9,21 @@ import mage.game.match.MatchOptions;
import mage.game.tournament.TournamentOptions;
import mage.players.PlayerType;
import mage.server.RoomImpl;
import mage.server.TableManager;
import mage.server.User;
import mage.server.UserManager;
import mage.server.tournament.TournamentManager;
import mage.server.util.ConfigSettings;
import mage.server.util.ThreadExecutor;
import mage.server.managers.ManagerFactory;
import mage.view.MatchView;
import mage.view.RoomUsersView;
import mage.view.TableView;
import mage.view.UsersView;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -40,9 +36,12 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
private static List<MatchView> matchView = new ArrayList<>();
private static List<RoomUsersView> roomUsersView = new ArrayList<>();
private final ManagerFactory managerFactory;
private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<>();
public GamesRoomImpl() {
public GamesRoomImpl(ManagerFactory managerFactory) {
super(managerFactory.chatManager());
this.managerFactory = managerFactory;
UPDATE_EXECUTOR.scheduleAtFixedRate(() -> {
try {
update();
@ -71,7 +70,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
} else {
// more since 50 matches finished since this match so removeUserFromAllTablesAndChat it
if (table.isTournament()) {
TournamentManager.instance.removeTournament(table.getTournament().getId());
managerFactory.tournamentManager().removeTournament(table.getTournament().getId());
}
this.removeTable(table.getId());
}
@ -79,7 +78,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
tableView = tableList;
matchView = matchList;
List<UsersView> users = new ArrayList<>();
for (User user : UserManager.instance.getUsers()) {
for (User user : managerFactory.userManager().getUsers()) {
if (user.getUserState() != User.UserState.Offline && !user.getName().equals("Admin")) {
try {
users.add(new UsersView(user.getUserData().getFlagName(), user.getName(),
@ -108,9 +107,9 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
users.sort((one, two) -> one.getUserName().compareToIgnoreCase(two.getUserName()));
List<RoomUsersView> roomUserInfo = new ArrayList<>();
roomUserInfo.add(new RoomUsersView(users,
GameManager.instance.getNumberActiveGames(),
ThreadExecutor.instance.getActiveThreads(ThreadExecutor.instance.getGameExecutor()),
ConfigSettings.instance.getMaxGameThreads()
managerFactory.gameManager().getNumberActiveGames(),
managerFactory.threadExecutor().getActiveThreads(managerFactory.threadExecutor().getGameExecutor()),
managerFactory.configSettings().getMaxGameThreads()
));
roomUsersView = roomUserInfo;
}
@ -123,7 +122,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
@Override
public boolean joinTable(UUID userId, UUID tableId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws MageException {
if (tables.containsKey(tableId)) {
return TableManager.instance.joinTable(userId, tableId, name, playerType, skill, deckList, password);
return managerFactory.tableManager().joinTable(userId, tableId, name, playerType, skill, deckList, password);
} else {
return false;
}
@ -131,7 +130,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
@Override
public TableView createTable(UUID userId, MatchOptions options) {
Table table = TableManager.instance.createTable(this.getRoomId(), userId, options);
Table table = managerFactory.tableManager().createTable(this.getRoomId(), userId, options);
tables.put(table.getId(), table);
return new TableView(table);
}
@ -139,7 +138,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
@Override
public boolean joinTournamentTable(UUID userId, UUID tableId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws GameException {
if (tables.containsKey(tableId)) {
return TableManager.instance.joinTournament(userId, tableId, name, playerType, skill, deckList, password);
return managerFactory.tableManager().joinTournament(userId, tableId, name, playerType, skill, deckList, password);
} else {
return false;
}
@ -147,7 +146,7 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
@Override
public TableView createTournamentTable(UUID userId, TournamentOptions options) {
Table table = TableManager.instance.createTournamentTable(this.getRoomId(), userId, options);
Table table = managerFactory.tableManager().createTournamentTable(this.getRoomId(), userId, options);
tables.put(table.getId(), table);
return new TableView(table);
}
@ -179,12 +178,12 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
@Override
public void leaveTable(UUID userId, UUID tableId) {
TableManager.instance.leaveTable(userId, tableId);
managerFactory.tableManager().leaveTable(userId, tableId);
}
@Override
public boolean watchTable(UUID userId, UUID tableId) throws MageException {
return TableManager.instance.watchTable(userId, tableId);
return managerFactory.tableManager().watchTable(userId, tableId);
}
@Override

View file

@ -1,5 +1,7 @@
package mage.server.game;
import mage.server.managers.GamesRoomManager;
import mage.server.managers.ManagerFactory;
import org.apache.log4j.Logger;
import java.util.Optional;
@ -9,36 +11,44 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* @author BetaSteward_at_googlemail.com
*/
public enum GamesRoomManager {
instance;
public class GamesRoomManagerImpl implements GamesRoomManager {
private final ManagerFactory managerFactory;
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);
private UUID mainRoomId;
private UUID mainChatId;
private static final Logger logger = Logger.getLogger(GamesRoomManagerImpl.class);
GamesRoomManager() {
GamesRoom mainRoom = new GamesRoomImpl();
public GamesRoomManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
public void init() {
GamesRoom mainRoom = new GamesRoomImpl(managerFactory);
mainRoomId = mainRoom.getRoomId();
mainChatId = mainRoom.getChatId();
rooms.put(mainRoomId, mainRoom);
}
@Override
public UUID createRoom() {
GamesRoom room = new GamesRoomImpl();
GamesRoom room = new GamesRoomImpl(managerFactory);
rooms.put(room.getRoomId(), room);
return room.getRoomId();
}
@Override
public UUID getMainRoomId() {
return mainRoomId;
}
@Override
public UUID getMainChatId() {
return mainChatId;
}
@Override
public Optional<GamesRoom> getRoom(UUID roomId) {
if (rooms.containsKey(roomId)) {
return Optional.of(rooms.get(roomId));
@ -48,6 +58,7 @@ public enum GamesRoomManager {
}
@Override
public void removeTable(UUID tableId) {
for (GamesRoom room : rooms.values()) {
room.removeTable(tableId);

View file

@ -1,46 +1,56 @@
package mage.server.game;
import mage.server.managers.ReplayManager;
import mage.server.managers.ManagerFactory;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import mage.server.UserManager;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public enum ReplayManager {
instance;
public class ReplayManagerImpl implements ReplayManager {
private final ConcurrentHashMap<String, ReplaySession> replaySessions = new ConcurrentHashMap<>();
private final ManagerFactory managerFactory;
public void replayGame(UUID gameId, UUID userId) {
ReplaySession replaySession = new ReplaySession(gameId, userId);
replaySessions.put(gameId.toString() + userId.toString(), replaySession);
UserManager.instance.getUser(userId).ifPresent(user->user.ccReplayGame(gameId));
public ReplayManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public void replayGame(UUID gameId, UUID userId) {
ReplaySession replaySession = new ReplaySession(managerFactory, gameId, userId);
replaySessions.put(gameId.toString() + userId.toString(), replaySession);
managerFactory.userManager().getUser(userId).ifPresent(user -> user.ccReplayGame(gameId));
}
@Override
public void startReplay(UUID gameId, UUID userId) {
replaySessions.get(gameId.toString() + userId.toString()).replay();
}
@Override
public void stopReplay(UUID gameId, UUID userId) {
replaySessions.get(gameId.toString() + userId.toString()).stop();
}
@Override
public void nextPlay(UUID gameId, UUID userId) {
replaySessions.get(gameId.toString() + userId.toString()).next();
}
@Override
public void previousPlay(UUID gameId, UUID userId) {
replaySessions.get(gameId.toString() + userId.toString()).previous();
}
@Override
public void skipForward(UUID gameId, UUID userId, int moves) {
replaySessions.get(gameId.toString() + userId.toString()).next(moves);
}
@Override
public void endReplay(UUID gameId, UUID userId) {
replaySessions.remove(gameId.toString() + userId.toString());
}

View file

@ -1,32 +1,32 @@
package mage.server.game;
import java.util.UUID;
import mage.game.Game;
import mage.game.GameState;
import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.server.UserManager;
import mage.server.managers.ManagerFactory;
import mage.view.GameView;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
public class ReplaySession implements GameCallback {
private final ManagerFactory managerFactory;
private final GameReplay replay;
protected final UUID userId;
ReplaySession(UUID gameId, UUID userId) {
ReplaySession(ManagerFactory managerFactory, UUID gameId, UUID userId) {
this.managerFactory = managerFactory;
this.replay = new GameReplay(gameId);
this.userId = userId;
}
public void replay() {
replay.start();
UserManager.instance.getUser(userId).ifPresent(user ->
managerFactory.userManager().getUser(userId).ifPresent(user ->
user.fireCallback(new ClientCallback(ClientCallbackMethod.REPLAY_INIT, replay.getGame().getId(), new GameView(replay.next(), replay.getGame(), null, null))));
}
@ -52,17 +52,17 @@ public class ReplaySession implements GameCallback {
@Override
public void gameResult(final String result) {
UserManager.instance.getUser(userId).ifPresent(user ->
managerFactory.userManager().getUser(userId).ifPresent(user ->
user.fireCallback(new ClientCallback(ClientCallbackMethod.REPLAY_DONE, replay.getGame().getId(), result)));
ReplayManager.instance.endReplay(replay.getGame().getId(), userId);
managerFactory.replayManager().endReplay(replay.getGame().getId(), userId);
}
private void updateGame(final GameState state, Game game) {
if (state == null) {
gameResult("game ended");
} else {
UserManager.instance.getUser(userId).ifPresent(user ->
managerFactory.userManager().getUser(userId).ifPresent(user ->
user.fireCallback(new ClientCallback(ClientCallbackMethod.REPLAY_UPDATE, replay.getGame().getId(), new GameView(state, game, null, null))));
}

View file

@ -0,0 +1,36 @@
package mage.server.managers;
import mage.game.Game;
import mage.server.ChatSession;
import mage.server.DisconnectReason;
import mage.server.exceptions.UserNotFoundException;
import mage.view.ChatMessage;
import java.util.List;
import java.util.UUID;
public interface ChatManager {
UUID createChatSession(String info);
void joinChat(UUID chatId, UUID userId);
void clearUserMessageStorage();
void leaveChat(UUID chatId, UUID userId);
void destroyChatSession(UUID chatId);
void broadcast(UUID chatId, String userName, String message, ChatMessage.MessageColor color, boolean withTime, Game game, ChatMessage.MessageType messageType, ChatMessage.SoundToPlay soundToPlay);
void broadcast(UUID userId, String message, ChatMessage.MessageColor color) throws UserNotFoundException;
void sendReconnectMessage(UUID userId);
void sendLostConnectionMessage(UUID userId, DisconnectReason reason);
void sendMessageToUserChats(UUID userId, String message);
void removeUser(UUID userId, DisconnectReason reason);
List<ChatSession> getChatSessions();
}

View file

@ -0,0 +1,72 @@
package mage.server.managers;
import mage.server.util.config.GamePlugin;
import mage.server.util.config.Plugin;
import java.util.List;
public interface ConfigSettings {
String getServerAddress();
String getServerName();
int getPort();
int getSecondaryBindPort();
int getLeasePeriod();
int getSocketWriteTimeout();
int getMaxPoolSize();
int getNumAcceptThreads();
int getBacklogSize();
int getMaxGameThreads();
int getMaxSecondsIdle();
int getMinUserNameLength();
int getMaxUserNameLength();
String getInvalidUserNamePattern();
int getMinPasswordLength();
int getMaxPasswordLength();
String getMaxAiOpponents();
Boolean isSaveGameActivated();
Boolean isAuthenticationActivated();
String getGoogleAccount();
String getMailgunApiKey();
String getMailgunDomain();
String getMailSmtpHost();
String getMailSmtpPort();
String getMailUser();
String getMailPassword();
String getMailFromAddress();
List<Plugin> getPlayerTypes();
List<GamePlugin> getGameTypes();
List<GamePlugin> getTournamentTypes();
List<Plugin> getDraftCubes();
List<Plugin> getDeckTypes();
}

View file

@ -0,0 +1,34 @@
package mage.server.managers;
import mage.game.draft.Draft;
import mage.server.draft.DraftController;
import mage.view.DraftPickView;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public interface DraftManager {
UUID createDraftSession(Draft draft, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId);
void joinDraft(UUID draftId, UUID userId);
void destroyChatSession(UUID gameId);
DraftPickView sendCardPick(UUID draftId, UUID userId, UUID cardId, Set<UUID> hiddenCards);
void sendCardMark(UUID draftId, UUID userId, UUID cardId);
void removeSession(UUID userId);
void kill(UUID draftId, UUID userId);
void timeout(UUID gameId, UUID userId);
void removeDraft(UUID draftId);
DraftController getControllerByDraftId(UUID draftId);
Optional<DraftController> getController(UUID tableId);
}

View file

@ -0,0 +1,54 @@
package mage.server.managers;
import mage.cards.decks.DeckCardLists;
import mage.constants.ManaType;
import mage.constants.PlayerAction;
import mage.game.Game;
import mage.game.GameOptions;
import mage.server.game.GameController;
import mage.view.GameView;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public interface GameManager {
UUID createGameSession(Game game, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions);
void joinGame(UUID gameId, UUID userId);
Optional<UUID> getChatId(UUID gameId);
void sendPlayerUUID(UUID gameId, UUID userId, UUID data);
void sendPlayerString(UUID gameId, UUID userId, String data);
void sendPlayerManaType(UUID gameId, UUID playerId, UUID userId, ManaType data);
void sendPlayerBoolean(UUID gameId, UUID userId, Boolean data);
void sendPlayerInteger(UUID gameId, UUID userId, Integer data);
void quitMatch(UUID gameId, UUID userId);
void sendPlayerAction(PlayerAction playerAction, UUID gameId, UUID userId, Object data);
boolean watchGame(UUID gameId, UUID userId);
void stopWatching(UUID gameId, UUID userId);
void cheat(UUID gameId, UUID userId, UUID playerId, DeckCardLists deckList);
boolean cheat(UUID gameId, UUID userId, UUID playerId, String cardName);
void removeGame(UUID gameId);
boolean saveGame(UUID gameId);
GameView getGameView(UUID gameId, UUID playerId);
int getNumberActiveGames();
Map<UUID, GameController> getGameController();
}

View file

@ -0,0 +1,18 @@
package mage.server.managers;
import mage.server.game.GamesRoom;
import java.util.Optional;
import java.util.UUID;
public interface GamesRoomManager {
UUID createRoom();
UUID getMainRoomId();
UUID getMainChatId();
Optional<GamesRoom> getRoom(UUID roomId);
void removeTable(UUID tableId);
}

View file

@ -0,0 +1,7 @@
package mage.server.managers;
public interface MailClient {
boolean sendMessage(String email, String subject, String text);
}

View file

@ -0,0 +1,29 @@
package mage.server.managers;
public interface ManagerFactory {
ChatManager chatManager();
DraftManager draftManager();
GameManager gameManager();
GamesRoomManager gamesRoomManager();
MailClient mailClient();
MailClient mailgunClient();
ReplayManager replayManager();
SessionManager sessionManager();
TableManager tableManager();
UserManager userManager();
ConfigSettings configSettings();
ThreadExecutor threadExecutor();
TournamentManager tournamentManager();
}

View file

@ -0,0 +1,19 @@
package mage.server.managers;
import java.util.UUID;
public interface ReplayManager {
void replayGame(UUID gameId, UUID userId);
void startReplay(UUID gameId, UUID userId);
void stopReplay(UUID gameId, UUID userId);
void nextPlay(UUID gameId, UUID userId);
void previousPlay(UUID gameId, UUID userId);
void skipForward(UUID gameId, UUID userId, int moves);
void endReplay(UUID gameId, UUID userId);
}

View file

@ -0,0 +1,43 @@
package mage.server.managers;
import mage.MageException;
import mage.players.net.UserData;
import mage.server.DisconnectReason;
import mage.server.Session;
import mage.server.User;
import org.jboss.remoting.callback.InvokerCallbackHandler;
import javax.annotation.Nonnull;
import java.util.Optional;
public interface SessionManager {
Optional<Session> getSession(@Nonnull String sessionId);
void createSession(String sessionId, InvokerCallbackHandler callbackHandler);
boolean registerUser(String sessionId, String userName, String password, String email) throws MageException;
boolean connectUser(String sessionId, String userName, String password, String userIdStr) throws MageException;
boolean connectAdmin(String sessionId);
boolean setUserData(String userName, String sessionId, UserData userData, String clientVersion, String userIdStr) throws MageException;
void disconnect(String sessionId, DisconnectReason reason);
void disconnect(String sessionId, DisconnectReason reason, Session directSession);
void disconnectUser(String sessionId, String userSessionId);
void endUserSession(String sessionId, String userSessionId);
boolean isAdmin(String sessionId);
boolean isValidSession(@Nonnull String sessionId);
Optional<User> getUser(@Nonnull String sessionId);
boolean extendUserSession(String sessionId, String pingInfo);
void sendErrorMessageToClient(String sessionId, String message);
}

View file

@ -0,0 +1,86 @@
package mage.server.managers;
import mage.MageException;
import mage.cards.decks.DeckCardLists;
import mage.game.GameException;
import mage.game.Table;
import mage.game.draft.Draft;
import mage.game.match.Match;
import mage.game.match.MatchOptions;
import mage.game.tournament.Tournament;
import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentPlayer;
import mage.players.PlayerType;
import mage.server.TableController;
import java.util.Collection;
import java.util.Optional;
import java.util.UUID;
public interface TableManager {
Table createTable(UUID roomId, UUID userId, MatchOptions options);
Table createTable(UUID roomId, MatchOptions options);
Table createTournamentTable(UUID roomId, UUID userId, TournamentOptions options);
Table getTable(UUID tableId);
Optional<Match> getMatch(UUID tableId);
Collection<Table> getTables();
Collection<TableController> getControllers();
Optional<TableController> getController(UUID tableId);
boolean joinTable(UUID userId, UUID tableId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws MageException;
boolean joinTournament(UUID userId, UUID tableId, String name, PlayerType playerType, int skill, DeckCardLists deckList, String password) throws GameException;
boolean submitDeck(UUID userId, UUID tableId, DeckCardLists deckList) throws MageException;
void updateDeck(UUID userId, UUID tableId, DeckCardLists deckList) throws MageException;
// removeUserFromAllTablesAndChat user from all tournament sub tables
void userQuitTournamentSubTables(UUID userId);
// removeUserFromAllTablesAndChat user from all sub tables of a tournament
void userQuitTournamentSubTables(UUID tournamentId, UUID userId);
boolean isTableOwner(UUID tableId, UUID userId);
boolean removeTable(UUID userId, UUID tableId);
void leaveTable(UUID userId, UUID tableId);
Optional<UUID> getChatId(UUID tableId);
void startMatch(UUID userId, UUID roomId, UUID tableId);
void startTournamentSubMatch(UUID roomId, UUID tableId);
void startTournament(UUID userId, UUID roomId, UUID tableId);
void startDraft(UUID tableId, Draft draft);
boolean watchTable(UUID userId, UUID tableId);
void endGame(UUID tableId);
void endDraft(UUID tableId, Draft draft);
void endTournament(UUID tableId, Tournament tournament);
void swapSeats(UUID tableId, UUID userId, int seatNum1, int seatNum2);
void construct(UUID tableId);
void initTournament(UUID tableId);
void addPlayer(UUID userId, UUID tableId, TournamentPlayer player) throws GameException;
void removeTable(UUID tableId);
void debugServerState();
}

View file

@ -0,0 +1,16 @@
package mage.server.managers;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
public interface ThreadExecutor {
int getActiveThreads(ExecutorService executerService);
ExecutorService getCallExecutor();
ExecutorService getGameExecutor();
ScheduledExecutorService getTimeoutExecutor();
ScheduledExecutorService getTimeoutIdleExecutor();
}

View file

@ -0,0 +1,32 @@
package mage.server.managers;
import mage.cards.decks.Deck;
import mage.game.tournament.Tournament;
import mage.server.tournament.TournamentController;
import mage.view.TournamentView;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public interface TournamentManager {
Optional<TournamentController> getTournamentController(UUID tournamentId);
void createTournamentSession(Tournament tournament, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId);
void joinTournament(UUID tournamentId, UUID userId);
void quit(UUID tournamentId, UUID userId);
void timeout(UUID tournamentId, UUID userId);
void submitDeck(UUID tournamentId, UUID playerId, Deck deck);
boolean updateDeck(UUID tournamentId, UUID playerId, Deck deck);
TournamentView getTournamentView(UUID tournamentId);
Optional<UUID> getChatId(UUID tournamentId);
void removeTournament(UUID tournamentId);
}

View file

@ -0,0 +1,41 @@
package mage.server.managers;
import mage.server.AuthorizedUser;
import mage.server.DisconnectReason;
import mage.server.User;
import mage.view.UserView;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface UserManager {
Optional<User> createUser(String userName, String host, AuthorizedUser authorizedUser);
Optional<User> getUser(UUID userId);
Optional<User> getUserByName(String userName);
Collection<User> getUsers();
boolean connectToSession(String sessionId, UUID userId);
void disconnect(UUID userId, DisconnectReason reason);
boolean isAdmin(UUID userId);
void removeUserFromAllTablesAndChat(UUID userId, DisconnectReason reason);
void informUserOpponents(UUID userId, String message);
boolean extendUserSession(UUID userId, String pingInfo);
List<UserView> getUserInfoList();
void handleException(Exception ex);
String getUserHistory(String userName);
void updateUserHistory();
}

View file

@ -3,16 +3,20 @@ package mage.server.record;
import mage.game.Table;
import mage.game.Table.TableRecorder;
import mage.game.result.ResultProtos.TableProto;
import mage.server.UserManager;
import mage.server.managers.UserManager;
public enum TableRecorderImpl implements TableRecorder {
public class TableRecorderImpl implements TableRecorder {
instance;
private final UserManager userManager;
public TableRecorderImpl(UserManager userManager) {
this.userManager = userManager;
}
@Override
public void record(Table table) {
TableProto proto = table.toProto();
TableRecordRepository.instance.add(new TableRecord(proto, proto.getEndTimeMs()));
UserManager.instance.updateUserHistory();
userManager.updateUserHistory();
}
}

View file

@ -17,14 +17,10 @@ import mage.game.tournament.Tournament;
import mage.game.tournament.TournamentPairing;
import mage.game.tournament.TournamentPlayer;
import mage.players.PlayerType;
import mage.server.ChatManager;
import mage.server.TableManager;
import mage.server.User;
import mage.server.UserManager;
import mage.server.draft.DraftController;
import mage.server.draft.DraftManager;
import mage.server.game.GamesRoomManager;
import mage.server.util.ThreadExecutor;
import mage.server.managers.TableManager;
import mage.server.managers.ManagerFactory;
import mage.view.ChatMessage.MessageColor;
import mage.view.ChatMessage.MessageType;
import mage.view.ChatMessage.SoundToPlay;
@ -44,6 +40,7 @@ public class TournamentController {
private static final Logger logger = Logger.getLogger(TournamentController.class);
private final ManagerFactory managerFactory;
private final UUID chatId;
private final UUID tableId;
private boolean started = false;
@ -51,9 +48,10 @@ public class TournamentController {
private ConcurrentMap<UUID, UUID> userPlayerMap = new ConcurrentHashMap<>();
private final ConcurrentMap<UUID, TournamentSession> tournamentSessions = new ConcurrentHashMap<>();
public TournamentController(Tournament tournament, ConcurrentMap<UUID, UUID> userPlayerMap, UUID tableId) {
public TournamentController(ManagerFactory managerFactory, Tournament tournament, ConcurrentMap<UUID, UUID> userPlayerMap, UUID tableId) {
this.managerFactory = managerFactory;
this.userPlayerMap = userPlayerMap;
chatId = ChatManager.instance.createChatSession("Tournament " + tournament.getId());
chatId = managerFactory.chatManager().createChatSession("Tournament " + tournament.getId());
this.tournament = tournament;
this.tableId = tableId;
init();
@ -67,7 +65,7 @@ public class TournamentController {
checkPlayersState();
break;
case INFO:
ChatManager.instance.broadcast(chatId, "", event.getMessage(), MessageColor.BLACK, true, null, MessageType.STATUS, null);
managerFactory.chatManager().broadcast(chatId, "", event.getMessage(), MessageColor.BLACK, true, null, MessageType.STATUS, null);
logger.debug(tournament.getId() + " " + event.getMessage());
break;
case START_DRAFT:
@ -122,7 +120,7 @@ public class TournamentController {
if (!player.getPlayer().isHuman()) {
player.setJoined();
logger.debug("player " + player.getPlayer().getId() + " has joined tournament " + tournament.getId());
ChatManager.instance.broadcast(chatId, "", player.getPlayer().getLogName() + " has joined the tournament", MessageColor.BLACK, true, null, MessageType.STATUS, null);
managerFactory.chatManager().broadcast(chatId, "", player.getPlayer().getLogName() + " has joined the tournament", MessageColor.BLACK, true, null, MessageType.STATUS, null);
}
}
checkStart();
@ -132,7 +130,7 @@ public class TournamentController {
UUID playerId = userPlayerMap.get(userId);
if (playerId == null) {
if (logger.isDebugEnabled()) {
UserManager.instance.getUser(userId).ifPresent(user
managerFactory.userManager().getUser(userId).ifPresent(user
-> logger.debug(user.getName() + " shows tournament panel tournamentId: " + tournament.getId()));
}
@ -143,16 +141,16 @@ public class TournamentController {
return;
}
// first join of player
TournamentSession tournamentSession = new TournamentSession(tournament, userId, tableId, playerId);
TournamentSession tournamentSession = new TournamentSession(managerFactory, tournament, userId, tableId, playerId);
tournamentSessions.put(playerId, tournamentSession);
Optional<User> _user = UserManager.instance.getUser(userId);
Optional<User> _user = managerFactory.userManager().getUser(userId);
if (_user.isPresent()) {
User user = _user.get();
user.addTournament(playerId, tournament.getId());
TournamentPlayer player = tournament.getPlayer(playerId);
player.setJoined();
logger.debug("player " + player.getPlayer().getName() + " - client has joined tournament " + tournament.getId());
ChatManager.instance.broadcast(chatId, "", player.getPlayer().getLogName() + " has joined the tournament", MessageColor.BLACK, true, null, MessageType.STATUS, null);
managerFactory.chatManager().broadcast(chatId, "", player.getPlayer().getLogName() + " has joined the tournament", MessageColor.BLACK, true, null, MessageType.STATUS, null);
checkStart();
} else {
logger.error("User not found userId: " + userId + " tournamentId: " + tournament.getId());
@ -174,7 +172,7 @@ public class TournamentController {
private void checkStart() {
if (!started && allJoined()) {
ThreadExecutor.instance.getCallExecutor().execute(this::startTournament);
managerFactory.threadExecutor().getCallExecutor().execute(this::startTournament);
}
}
@ -211,15 +209,15 @@ public class TournamentController {
tournamentSession.tournamentOver();
}
this.tournamentSessions.clear();
TableManager.instance.endTournament(tableId, tournament);
managerFactory.tableManager().endTournament(tableId, tournament);
tournament.cleanUpOnTournamentEnd();
}
private void startMatch(TournamentPairing pair, MatchOptions matchOptions) {
try {
TableManager tableManager = TableManager.instance;
Table table = tableManager.createTable(GamesRoomManager.instance.getMainRoomId(), matchOptions);
TableManager tableManager = managerFactory.tableManager();
Table table = tableManager.createTable(managerFactory.gamesRoomManager().getMainRoomId(), matchOptions);
table.setTournamentSubTable(true);
table.setTournament(tournament);
table.setState(TableState.WAITING);
@ -261,8 +259,8 @@ public class TournamentController {
private void startMultiplayerMatch(MultiplayerRound round, MatchOptions matchOptions) {
try {
TableManager tableManager = TableManager.instance;
Table table = tableManager.createTable(GamesRoomManager.instance.getMainRoomId(), matchOptions);
TableManager tableManager = managerFactory.tableManager();
Table table = tableManager.createTable(managerFactory.gamesRoomManager().getMainRoomId(), matchOptions);
table.setTournamentSubTable(true);
table.setTournament(tournament);
table.setState(TableState.WAITING);
@ -287,16 +285,16 @@ public class TournamentController {
}
private void startDraft(Draft draft) {
TableManager.instance.startDraft(tableId, draft);
managerFactory.tableManager().startDraft(tableId, draft);
}
private void construct() {
TableManager.instance.construct(tableId);
managerFactory.tableManager().construct(tableId);
}
private void initTournament() {
if (TableManager.instance.getTable(tableId).getState() != TableState.DUELING) {
TableManager.instance.initTournament(tableId);
if (managerFactory.tableManager().getTable(tableId).getState() != TableState.DUELING) {
managerFactory.tableManager().initTournament(tableId);
}
}
@ -305,7 +303,7 @@ public class TournamentController {
TournamentSession tournamentSession = tournamentSessions.get(playerId);
tournamentSession.construct(timeout);
getPlayerUserId(playerId).ifPresent(userId -> {
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
user.addConstructing(playerId, tournamentSession);
TournamentPlayer player = tournament.getPlayer(playerId);
player.setState(TournamentPlayerState.CONSTRUCTING);
@ -319,7 +317,7 @@ public class TournamentController {
TournamentPlayer player = tournament.getPlayer(playerId);
if (player != null && !player.hasQuit()) {
tournamentSessions.get(playerId).submitDeck(deck);
ChatManager.instance.broadcast(chatId, "", player.getPlayer().getLogName() + " has submitted their tournament deck", MessageColor.BLACK, true, null, MessageType.STATUS, SoundToPlay.PlayerSubmittedDeck);
managerFactory.chatManager().broadcast(chatId, "", player.getPlayer().getLogName() + " has submitted their tournament deck", MessageColor.BLACK, true, null, MessageType.STATUS, SoundToPlay.PlayerSubmittedDeck);
}
}
}
@ -338,7 +336,7 @@ public class TournamentController {
tournament.autoSubmit(userPlayerMap.get(userId), tournamentPlayer.generateDeck());
} else {
StringBuilder sb = new StringBuilder();
UserManager.instance.getUser(userId).ifPresent(user
managerFactory.userManager().getUser(userId).ifPresent(user
-> sb.append(user.getName()));
sb.append(" - no deck found for auto submit");
@ -380,16 +378,16 @@ public class TournamentController {
if (tournament.isDoneConstructing()) {
info = new StringBuilder("during round ").append(tournament.getRounds().size()).toString();
// quit active matches of that tournament
TableManager.instance.userQuitTournamentSubTables(tournament.getId(), userId);
managerFactory.tableManager().userQuitTournamentSubTables(tournament.getId(), userId);
status = TourneyQuitStatus.DURING_ROUND;
} else if (tournamentPlayer.getState() == TournamentPlayerState.DRAFTING) {
info = "during Draft phase";
if (!checkToReplaceDraftPlayerByAi(userId, tournamentPlayer)) {
this.abortDraftTournament();
} else {
DraftManager.instance.getController(tableId).ifPresent(draftController -> {
managerFactory.draftManager().getController(tableId).ifPresent(draftController -> {
draftController.getDraftSession(playerId).ifPresent(draftSession
-> DraftManager.instance.kill(draftSession.getDraftId(), userId));
-> managerFactory.draftManager().kill(draftSession.getDraftId(), userId));
});
}
@ -404,7 +402,7 @@ public class TournamentController {
tournamentPlayer.setQuit(info, status);
tournament.quit(playerId);
tournamentSession.quit();
ChatManager.instance.broadcast(chatId, "", tournamentPlayer.getPlayer().getLogName() + " has quit the tournament", MessageColor.BLACK, true, null, MessageType.STATUS, SoundToPlay.PlayerQuitTournament);
managerFactory.chatManager().broadcast(chatId, "", tournamentPlayer.getPlayer().getLogName() + " has quit the tournament", MessageColor.BLACK, true, null, MessageType.STATUS, SoundToPlay.PlayerQuitTournament);
}
}
@ -417,8 +415,8 @@ public class TournamentController {
}
// replace player that quits with draft bot
if (humans > 1) {
Optional<User> user = UserManager.instance.getUser(userId);
TableManager.instance.getController(tableId).ifPresent(tableController -> {
Optional<User> user = managerFactory.userManager().getUser(userId);
managerFactory.tableManager().getController(tableId).ifPresent(tableController -> {
String replacePlayerName = "Draftbot";
if (user.isPresent()) {
@ -430,7 +428,7 @@ public class TournamentController {
user.get().removeTable(leavingPlayer.getPlayer().getId());
user.get().removeTournament(leavingPlayer.getPlayer().getId());
}
ChatManager.instance.broadcast(chatId, "", leavingPlayer.getPlayer().getLogName() + " was replaced by draftbot", MessageColor.BLACK, true, null, MessageType.STATUS, null);
managerFactory.chatManager().broadcast(chatId, "", leavingPlayer.getPlayer().getLogName() + " was replaced by draftbot", MessageColor.BLACK, true, null, MessageType.STATUS, null);
});
return true;
}
@ -447,7 +445,7 @@ public class TournamentController {
private void abortDraftTournament() {
tournament.setAbort(true);
DraftManager.instance.getController(tableId).ifPresent(DraftController::abortDraft);
managerFactory.draftManager().getController(tableId).ifPresent(DraftController::abortDraft);
}
public boolean isAbort() {
@ -472,7 +470,7 @@ public class TournamentController {
}
public void cleanUpOnRemoveTournament() {
ChatManager.instance.destroyChatSession(chatId);
managerFactory.chatManager().destroyChatSession(chatId);
}
/**
@ -490,7 +488,7 @@ public class TournamentController {
if (tournamentPlayer != null) {
if (!tournamentPlayer.hasQuit()) {
if (tournamentPlayer.getPlayer().isHuman()) {
Optional<User> user = UserManager.instance.getUser(entry.getKey());
Optional<User> user = managerFactory.userManager().getUser(entry.getKey());
if (!user.isPresent()) {
logger.debug("Tournament user is missing but player active -> start quit - tournamentId: " + tournament.getId() + " state: " + tableState.toString());
// active tournament player but the user is no longer online

View file

@ -1,57 +1,71 @@
package mage.server.tournament;
import mage.cards.decks.Deck;
import mage.game.tournament.Tournament;
import mage.server.managers.TournamentManager;
import mage.server.managers.ManagerFactory;
import mage.view.TournamentView;
import org.apache.log4j.Logger;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import mage.cards.decks.Deck;
import mage.game.tournament.Tournament;
import mage.view.TournamentView;
import org.apache.log4j.Logger;
/**
* @author BetaSteward_at_googlemail.com
*/
public enum TournamentManager {
instance;
public class TournamentManagerImpl implements TournamentManager {
private final ManagerFactory managerFactory;
private final ConcurrentMap<UUID, TournamentController> controllers = new ConcurrentHashMap<>();
public TournamentManagerImpl(ManagerFactory managerFactory) {
this.managerFactory = managerFactory;
}
@Override
public Optional<TournamentController> getTournamentController(UUID tournamentId) {
return Optional.ofNullable(controllers.get(tournamentId));
}
@Override
public void createTournamentSession(Tournament tournament, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId) {
TournamentController tournamentController = new TournamentController(tournament, userPlayerMap, tableId);
TournamentController tournamentController = new TournamentController(managerFactory, tournament, userPlayerMap, tableId);
controllers.put(tournament.getId(), tournamentController);
}
@Override
public void joinTournament(UUID tournamentId, UUID userId) {
controllers.get(tournamentId).join(userId);
}
@Override
public void quit(UUID tournamentId, UUID userId) {
TournamentController tournamentController = controllers.get(tournamentId);
if (tournamentController != null) {
tournamentController.quit(userId);
} else {
Logger.getLogger(TournamentManager.class).error("Tournament controller missing tournamentid: " + tournamentId + " userId: " + userId);
Logger.getLogger(TournamentManagerImpl.class).error("Tournament controller missing tournamentid: " + tournamentId + " userId: " + userId);
}
}
@Override
public void timeout(UUID tournamentId, UUID userId) {
controllers.get(tournamentId).timeout(userId);
}
@Override
public void submitDeck(UUID tournamentId, UUID playerId, Deck deck) {
controllers.get(tournamentId).submitDeck(playerId, deck);
}
@Override
public boolean updateDeck(UUID tournamentId, UUID playerId, Deck deck) {
return controllers.get(tournamentId).updateDeck(playerId, deck);
}
@Override
public TournamentView getTournamentView(UUID tournamentId) {
TournamentController tournamentController = controllers.get(tournamentId);
if (tournamentController != null) {
@ -60,6 +74,7 @@ public enum TournamentManager {
return null;
}
@Override
public Optional<UUID> getChatId(UUID tournamentId) {
if (controllers.containsKey(tournamentId)) {
return Optional.of(controllers.get(tournamentId).getChatId());
@ -67,6 +82,7 @@ public enum TournamentManager {
return Optional.empty();
}
@Override
public void removeTournament(UUID tournamentId) {
TournamentController tournamentController = controllers.get(tournamentId);
if (tournamentController != null) {

View file

@ -1,20 +1,19 @@
package mage.server.tournament;
import mage.cards.decks.Deck;
import mage.game.tournament.Tournament;
import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.server.User;
import mage.server.managers.ManagerFactory;
import mage.view.TournamentView;
import org.apache.log4j.Logger;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import mage.cards.decks.Deck;
import mage.game.tournament.Tournament;
import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
import mage.server.User;
import mage.server.UserManager;
import mage.server.util.ThreadExecutor;
import mage.view.TournamentView;
import org.apache.log4j.Logger;
/**
* @author BetaSteward_at_googlemail.com
@ -23,6 +22,7 @@ public class TournamentSession {
protected static final Logger logger = Logger.getLogger(TournamentSession.class);
private final ManagerFactory managerFactory;
protected final UUID userId;
protected final UUID playerId;
protected final UUID tableId;
@ -30,9 +30,11 @@ public class TournamentSession {
protected boolean killed = false;
private ScheduledFuture<?> futureTimeout;
protected static final ScheduledExecutorService timeoutExecutor = ThreadExecutor.instance.getTimeoutExecutor();
protected final ScheduledExecutorService timeoutExecutor;
public TournamentSession(Tournament tournament, UUID userId, UUID tableId, UUID playerId) {
public TournamentSession(ManagerFactory managerFactory, Tournament tournament, UUID userId, UUID tableId, UUID playerId) {
this.managerFactory = managerFactory;
this.timeoutExecutor = managerFactory.threadExecutor().getTimeoutExecutor();
this.userId = userId;
this.tournament = tournament;
this.playerId = playerId;
@ -41,7 +43,7 @@ public class TournamentSession {
public boolean init() {
if (!killed) {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (user.isPresent()) {
user.get().fireCallback(new ClientCallback(ClientCallbackMethod.TOURNAMENT_INIT, tournament.getId(), getTournamentView()));
return true;
@ -52,7 +54,7 @@ public class TournamentSession {
public void update() {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user
managerFactory.userManager().getUser(userId).ifPresent(user
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.TOURNAMENT_UPDATE, tournament.getId(), getTournamentView())));
}
@ -60,7 +62,7 @@ public class TournamentSession {
public void gameOver(final String message) {
if (!killed) {
UserManager.instance.getUser(userId).ifPresent(user
managerFactory.userManager().getUser(userId).ifPresent(user
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.TOURNAMENT_OVER, tournament.getId(), message)));
}
@ -69,7 +71,7 @@ public class TournamentSession {
public void construct(int timeout) {
if (!killed) {
setupTimeout(timeout);
UserManager.instance.getUser(userId).ifPresent(user -> {
managerFactory.userManager().getUser(userId).ifPresent(user -> {
int remaining = (int) futureTimeout.getDelay(TimeUnit.SECONDS);
user.ccConstruct(tournament.getPlayer(playerId).getDeck(), tableId, remaining);
});
@ -102,7 +104,7 @@ public class TournamentSession {
futureTimeout = timeoutExecutor.schedule(
() -> {
try {
TournamentManager.instance.timeout(tournament.getId(), userId);
managerFactory.tournamentManager().timeout(tournament.getId(), userId);
} catch (Exception e) {
logger.fatal("TournamentSession error - userId " + userId + " tId " + tournament.getId(), e);
}
@ -144,7 +146,7 @@ public class TournamentSession {
}
private void removeTournamentForUser() {
Optional<User> user = UserManager.instance.getUser(userId);
Optional<User> user = managerFactory.userManager().getUser(userId);
if (user.isPresent()) {
user.get().removeTable(playerId);
user.get().removeTournament(playerId);

View file

@ -0,0 +1,20 @@
package mage.server.util;
import mage.server.util.config.Config;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.File;
public class ConfigFactory {
public static Config loadFromFile(final String filePath) {
try {
final JAXBContext jaxbContext = JAXBContext.newInstance("mage.server.util.config");
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
return (Config) unmarshaller.unmarshal(new File(filePath));
} catch (Exception e) {
throw new ConfigurationException(e);
}
}
}

View file

@ -1,33 +1,18 @@
package mage.server.util;
import java.io.File;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import mage.server.managers.ConfigSettings;
import mage.server.util.config.Config;
import mage.server.util.config.GamePlugin;
import mage.server.util.config.Plugin;
import org.apache.log4j.Logger;
/**
* @author BetaSteward_at_googlemail.com
*/
public enum ConfigSettings {
instance;
private final Logger logger = Logger.getLogger(ConfigSettings.class);
import java.util.List;
private Config config;
public class ConfigWrapper implements ConfigSettings {
ConfigSettings() {
try {
JAXBContext jaxbContext = JAXBContext.newInstance("mage.server.util.config");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
config = (Config) unmarshaller.unmarshal(new File("config/config.xml"));
} catch (JAXBException ex) {
logger.fatal("ConfigSettings error", ex);
}
private final Config config;
public ConfigWrapper(final Config config) {
this.config = config;
}
public String getServerAddress() {

View file

@ -0,0 +1,7 @@
package mage.server.util;
public class ConfigurationException extends RuntimeException {
public ConfigurationException(Throwable cause) {
super(cause);
}
}

View file

@ -1,24 +1,18 @@
package mage.server.util;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import mage.server.managers.ConfigSettings;
import mage.server.managers.ThreadExecutor;
import java.util.concurrent.*;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public enum ThreadExecutor {
instance;
private static final ExecutorService callExecutor = Executors.newCachedThreadPool();
private static final ExecutorService userExecutor = Executors.newCachedThreadPool();
private static final ExecutorService gameExecutor = Executors.newFixedThreadPool(ConfigSettings.instance.getMaxGameThreads());
private static final ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(4);
private static final ScheduledExecutorService timeoutIdleExecutor = Executors.newScheduledThreadPool(4);
public class ThreadExecutorImpl implements ThreadExecutor {
private final ExecutorService callExecutor;
private final ExecutorService gameExecutor;
private final ScheduledExecutorService timeoutExecutor;
private final ScheduledExecutorService timeoutIdleExecutor;
/**
* noxx: what the settings below do is setting the ability to keep OS
@ -26,17 +20,20 @@ instance;
* within this time period, the thread may be discarded. But anyway if new
* game is created later, new OS/java thread will be created for it taking
* MaxGameThreads limit into account.
*
* <p>
* This all is done for performance reasons as creating new OS threads is
* resource consuming process.
*/
static {
public ThreadExecutorImpl(ConfigSettings config) {
callExecutor = Executors.newCachedThreadPool();
gameExecutor = Executors.newFixedThreadPool(config.getMaxGameThreads());
timeoutExecutor = Executors.newScheduledThreadPool(4);
timeoutIdleExecutor = Executors.newScheduledThreadPool(4);
((ThreadPoolExecutor) callExecutor).setKeepAliveTime(60, TimeUnit.SECONDS);
((ThreadPoolExecutor) callExecutor).allowCoreThreadTimeOut(true);
((ThreadPoolExecutor) callExecutor).setThreadFactory(new XMageThreadFactory("CALL"));
((ThreadPoolExecutor) userExecutor).setKeepAliveTime(60, TimeUnit.SECONDS);
((ThreadPoolExecutor) userExecutor).allowCoreThreadTimeOut(true);
((ThreadPoolExecutor) userExecutor).setThreadFactory(new XMageThreadFactory("USER"));
((ThreadPoolExecutor) gameExecutor).setKeepAliveTime(60, TimeUnit.SECONDS);
((ThreadPoolExecutor) gameExecutor).allowCoreThreadTimeOut(true);
((ThreadPoolExecutor) gameExecutor).setThreadFactory(new XMageThreadFactory("GAME"));
@ -49,6 +46,7 @@ instance;
}
@Override
public int getActiveThreads(ExecutorService executerService) {
if (executerService instanceof ThreadPoolExecutor) {
return ((ThreadPoolExecutor) executerService).getActiveCount();
@ -56,18 +54,22 @@ instance;
return -1;
}
@Override
public ExecutorService getCallExecutor() {
return callExecutor;
}
@Override
public ExecutorService getGameExecutor() {
return gameExecutor;
}
@Override
public ScheduledExecutorService getTimeoutExecutor() {
return timeoutExecutor;
}
@Override
public ScheduledExecutorService getTimeoutIdleExecutor() {
return timeoutIdleExecutor;
}

View file

@ -0,0 +1,36 @@
package mage.server.util;
import mage.server.util.config.Config;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.nio.file.Paths;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
public class ConfigFactoryTest {
@Test
@DisplayName("should unmarshal configuration from file")
void loadConfig() {
final Config config = ConfigFactory.loadFromFile("config/config.xml");
assertThat(config.getServer().getServerName()).isEqualTo("mage-server");
assertThat(config.getServer().getPort()).isEqualTo(17171);
}
@Test
@DisplayName("should fail if config is malformed")
void failOnMalformed() {
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(() -> ConfigFactory.loadFromFile(Paths.get("src", "test", "resources", "config_error.xml").toString()));
}
@Test
@DisplayName("should fail if file does not exist")
void failOnNotFound() {
assertThatExceptionOfType(ConfigurationException.class)
.isThrownBy(() -> ConfigFactory.loadFromFile("does not exist"));
}
}

View file

@ -0,0 +1,294 @@
package mage.server.util;
import mage.server.util.config.*;
import mage.utils.FluentBuilder;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
public class ConfigWrapperTest {
static class ConfigBuilder extends FluentBuilder<Config, ConfigBuilder> {
public String serverAddress;
public String serverName;
public int port;
public int secondaryBindPort;
public int leasePeriod;
public int socketWriteTimeout;
public int maxPoolSize;
public int numAcceptThreads;
public int backlogSize;
public int maxGameThreads;
public int maxSecondsIdle;
public int minUsernameLength;
public int maxUsernameLength;
public String invalidUsernamePattern;
public int minPasswordLength;
public int maxPasswordLength;
public String maxAiOpponents;
public boolean saveGameActivated;
public boolean authenticationActivated;
public String googleAccount;
public String mailgunApiKey;
public String mailgunDomain;
public String mailSmtpHost;
public String mailSmtpPort;
public String mailUser;
public String mailPassword;
public String mailFromAddress;
public List<Plugin> playerTypes = Collections.emptyList();
public List<GamePlugin> gameTypes = Collections.emptyList();
public List<GamePlugin> tournamentTypes = Collections.emptyList();
public List<Plugin> draftCubes = Collections.emptyList();
public List<Plugin> deckTypes = Collections.emptyList();
private ConfigBuilder() {
super(ConfigBuilder::new);
}
@Override
protected Config makeValue() {
final Config result = new Config();
result.setServer(makeServer());
result.setPlayerTypes(makePlayerTypes());
result.setGameTypes(makeGameTypes());
result.setTournamentTypes(makeTournamentTypes());
result.setDraftCubes(makeDraftCubes());
result.setDeckTypes(makeDeckTypes());
return result;
}
private Server makeServer() {
final Server server = new Server();
server.setServerAddress(serverAddress);
server.setServerName(serverName);
server.setPort(BigInteger.valueOf(port));
server.setSecondaryBindPort(bi(secondaryBindPort));
server.setLeasePeriod(bi(leasePeriod));
server.setSocketWriteTimeout(bi(socketWriteTimeout));
server.setMaxPoolSize(bi(maxPoolSize));
server.setNumAcceptThreads(bi(numAcceptThreads));
server.setBacklogSize(bi(backlogSize));
server.setMaxGameThreads(bi(maxGameThreads));
server.setMaxSecondsIdle(bi(maxSecondsIdle));
server.setMinUserNameLength(bi(minUsernameLength));
server.setMaxUserNameLength(bi(maxUsernameLength));
server.setInvalidUserNamePattern(invalidUsernamePattern);
server.setMinPasswordLength(bi(minPasswordLength));
server.setMaxPasswordLength(bi(maxPasswordLength));
server.setMaxAiOpponents(maxAiOpponents);
server.setSaveGameActivated(saveGameActivated);
server.setAuthenticationActivated(authenticationActivated);
server.setGoogleAccount(googleAccount);
server.setMailgunApiKey(mailgunApiKey);
server.setMailgunDomain(mailgunDomain);
server.setMailSmtpHost(mailSmtpHost);
server.setMailSmtpPort(mailSmtpPort);
server.setMailUser(mailUser);
server.setMailPassword(mailPassword);
server.setMailFromAddress(mailFromAddress);
return server;
}
private PlayerTypes makePlayerTypes() {
final PlayerTypes playerTypes = new PlayerTypes();
this.playerTypes.forEach(p -> playerTypes.getPlayerType().add(p));
return playerTypes;
}
private GameTypes makeGameTypes() {
final GameTypes gameTypes = new GameTypes();
this.gameTypes.forEach(g -> gameTypes.getGameType().add(g));
return gameTypes;
}
private TournamentTypes makeTournamentTypes() {
final TournamentTypes tournamentTypes = new TournamentTypes();
this.tournamentTypes.forEach(t -> tournamentTypes.getTournamentType().add(t));
return tournamentTypes;
}
private DraftCubes makeDraftCubes() {
final DraftCubes draftCubes = new DraftCubes();
this.draftCubes.forEach(d -> draftCubes.getDraftCube().add(d));
return draftCubes;
}
private DeckTypes makeDeckTypes() {
final DeckTypes deckTypes = new DeckTypes();
this.deckTypes.forEach(d -> deckTypes.getDeckType().add(d));
return deckTypes;
}
private BigInteger bi(int value) {
return BigInteger.valueOf(value);
}
}
private ConfigBuilder baseConfigBuilder() {
return new ConfigBuilder();
}
private final String expectedString = RandomStringUtils.randomAlphanumeric(15);
private final int expectedPositiveInt = RandomUtils.nextInt(0, Integer.MAX_VALUE);
@TestFactory
@DisplayName("should return from server")
Stream<DynamicTest> assignmentFromServer() {
return Stream.of(
testString("server address", c -> c.serverAddress = expectedString, ConfigWrapper::getServerAddress),
testString("server name", c -> c.serverName = expectedString, ConfigWrapper::getServerName),
testInt("port", c -> c.port = expectedPositiveInt, ConfigWrapper::getPort),
testInt("secondary bind port", c -> c.secondaryBindPort = expectedPositiveInt, ConfigWrapper::getSecondaryBindPort),
testInt("lease period", c -> c.leasePeriod = expectedPositiveInt, ConfigWrapper::getLeasePeriod),
testInt("socket write timeout", c -> c.socketWriteTimeout = expectedPositiveInt, ConfigWrapper::getSocketWriteTimeout),
testInt("max pool size", c -> c.maxPoolSize = expectedPositiveInt, ConfigWrapper::getMaxPoolSize),
testInt("number of accept threads", c -> c.numAcceptThreads = expectedPositiveInt, ConfigWrapper::getNumAcceptThreads),
testInt("backlog size", c -> c.backlogSize = expectedPositiveInt, ConfigWrapper::getBacklogSize),
testInt("max game threads", c -> c.maxGameThreads = expectedPositiveInt, ConfigWrapper::getMaxGameThreads),
testInt("max seconds idle", c -> c.maxSecondsIdle = expectedPositiveInt, ConfigWrapper::getMaxSecondsIdle),
testInt("min username length", c -> c.minUsernameLength = expectedPositiveInt, ConfigWrapper::getMinUserNameLength),
testInt("max username length", c -> c.maxUsernameLength = expectedPositiveInt, ConfigWrapper::getMaxUserNameLength),
testString("invalid username pattern", c -> c.invalidUsernamePattern = expectedString, ConfigWrapper::getInvalidUserNamePattern),
testInt("min password length", c -> c.minPasswordLength = expectedPositiveInt, ConfigWrapper::getMinPasswordLength),
testInt("max password length", c -> c.maxPasswordLength = expectedPositiveInt, ConfigWrapper::getMaxPasswordLength),
testString("max AI opponents", c -> c.maxAiOpponents = expectedString, ConfigWrapper::getMaxAiOpponents),
testTrue("save game activated", c -> c.saveGameActivated = true, ConfigWrapper::isSaveGameActivated),
testTrue("authentication activated", c -> c.authenticationActivated = true, ConfigWrapper::isAuthenticationActivated),
testString("google account", c -> c.googleAccount = expectedString, ConfigWrapper::getGoogleAccount),
testString("mailgun api key", c -> c.mailgunApiKey = expectedString, ConfigWrapper::getMailgunApiKey),
testString("mailgun domain", c -> c.mailgunDomain = expectedString, ConfigWrapper::getMailgunDomain),
testString("mail smtp host", c -> c.mailSmtpHost = expectedString, ConfigWrapper::getMailSmtpHost),
testString("mail smtp port", c -> c.mailSmtpPort = expectedString, ConfigWrapper::getMailSmtpPort),
testString("mail from address", c -> c.mailFromAddress = expectedString, ConfigWrapper::getMailFromAddress),
testString("mail user", c -> c.mailUser = expectedString, ConfigWrapper::getMailUser),
testString("mail password", c -> c.mailPassword = expectedString, ConfigWrapper::getMailPassword)
);
}
private DynamicTest testString(String description, Consumer<ConfigBuilder> builderSetter, Function<ConfigWrapper, Object> valueExtractor) {
return testTemplate(description, builderSetter, valueExtractor, expectedString);
}
private DynamicTest testTemplate(String description, Consumer<ConfigBuilder> builderSetter, Function<ConfigWrapper, Object> valueExtractor, Object expectedValue) {
return DynamicTest.dynamicTest(description, () -> assertThat(valueExtractor.apply(makeTestee(baseConfigBuilder().with(builderSetter)))).isEqualTo(expectedValue));
}
private ConfigWrapper makeTestee(ConfigBuilder builder) {
return new ConfigWrapper(builder.build());
}
private DynamicTest testInt(String description, Consumer<ConfigBuilder> builderSetter, Function<ConfigWrapper, Object> valueExtractor) {
return testTemplate(description, builderSetter, valueExtractor, expectedPositiveInt);
}
private DynamicTest testTrue(String description, Consumer<ConfigBuilder> builderSetter, Function<ConfigWrapper, Object> valueExtractor) {
return testTemplate(description, builderSetter, valueExtractor, true);
}
private final Comparator<Plugin> pluginComparator = (p1, p2) -> {
if (Objects.equals(p1.getName(), p2.getName()) &&
Objects.equals(p1.getJar(), p2.getJar()) &&
Objects.equals(p1.getClassName(), p2.getClassName())) {
return 0;
} else {
return -1;
}
};
private final Comparator<GamePlugin> gamePluginComparator = (p1, p2) -> {
if (Objects.equals(p1.getName(), p2.getName()) &&
Objects.equals(p1.getJar(), p2.getJar()) &&
Objects.equals(p1.getClassName(), p2.getClassName()) &&
Objects.equals(p1.getTypeName(), p2.getTypeName())) {
return 0;
} else {
return -1;
}
};
private final List<Plugin> randomPlugins = IntStream.range(0, RandomUtils.nextInt(1, 10))
.mapToObj(i -> makePlugin(
RandomStringUtils.randomAlphanumeric(15),
RandomStringUtils.randomAlphanumeric(16),
RandomStringUtils.randomAlphanumeric(17))
).collect(Collectors.toList());
private final List<GamePlugin> randomGamePlugins = IntStream.range(0, RandomUtils.nextInt(1, 10))
.mapToObj(i -> makeGamePlugin(
RandomStringUtils.randomAlphanumeric(15),
RandomStringUtils.randomAlphanumeric(16),
RandomStringUtils.randomAlphanumeric(17),
RandomStringUtils.randomAlphanumeric(18))
).collect(Collectors.toList());
private Plugin makePlugin(String name, String jar, String className) {
final Plugin plugin = new Plugin();
plugin.setName(name);
plugin.setJar(jar);
plugin.setClassName(className);
return plugin;
}
private GamePlugin makeGamePlugin(String name, String jar, String className, String typeName) {
final GamePlugin plugin = new GamePlugin();
plugin.setName(name);
plugin.setJar(jar);
plugin.setClassName(className);
plugin.setTypeName(typeName);
return plugin;
}
@TestFactory
@DisplayName("should extract")
Stream<DynamicTest> pluginsExtraction() {
return Stream.of(
pluginTest("playerTypes from playerTypes", c -> c.playerTypes = randomPlugins, ConfigWrapper::getPlayerTypes),
gamePluginTest("gameTypes from gameTypes", c -> c.gameTypes = randomGamePlugins, ConfigWrapper::getGameTypes),
gamePluginTest("tournamentTypes from tournamentTypes", c -> c.tournamentTypes = randomGamePlugins, ConfigWrapper::getTournamentTypes),
pluginTest("draftCubes from draftCubes", c -> c.draftCubes = randomPlugins, ConfigWrapper::getDraftCubes),
pluginTest("deckTypes from deckTypes", c -> c.deckTypes = randomPlugins, ConfigWrapper::getDeckTypes)
);
}
private DynamicTest pluginTest(String description,
Consumer<ConfigBuilder> builderSetter,
Function<ConfigWrapper, List<Plugin>> listExtractor) {
return testTemplateForLists(description, builderSetter, listExtractor, randomPlugins, pluginComparator);
}
private DynamicTest gamePluginTest(String description,
Consumer<ConfigBuilder> builderSetter,
Function<ConfigWrapper, List<GamePlugin>> listExtractor) {
return testTemplateForLists(description, builderSetter, listExtractor, randomGamePlugins, gamePluginComparator);
}
private <T> DynamicTest testTemplateForLists(String description,
Consumer<ConfigBuilder> builderSetter,
Function<ConfigWrapper, List<T>> listExtractor,
List<T> expectedValue,
Comparator<T> comparator) {
return DynamicTest.dynamicTest(description, () ->
assertThat(listExtractor.apply(makeTestee(baseConfigBuilder().with(builderSetter))))
.usingElementComparator(comparator)
.containsExactlyElementsOf(expectedValue)
);
}
}

View file

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Configuration file with integers set as strings to cause unmarshalling errors -->
<config>
<server serverAddress="0.0.0.0"
serverName="mage-server"
port="NOT AN INTEGER"
secondaryBindPort="NOT AN INTEGER"
backlogSize="NOT AN INTEGER"
numAcceptThreads="NOT AN INTEGER"
maxPoolSize="NOT AN INTEGER"
leasePeriod="NOT AN INTEGER"
socketWriteTimeout="NOT AN INTEGER"
maxGameThreads="NOT AN INTEGER"
maxSecondsIdle="NOT AN INTEGER"
minUserNameLength="NOT AN INTEGER"
maxUserNameLength="NOT AN INTEGER"
invalidUserNamePattern="[^a-z0-9_]"
minPasswordLength="NOT AN INTEGER"
maxPasswordLength="NOT AN INTEGER"
maxAiOpponents="NOT AN INTEGER"
saveGameActivated="false"
authenticationActivated="false"
googleAccount=""
mailgunApiKey=""
mailgunDomain=""
mailSmtpHost=""
mailSmtpPort=""
mailUser=""
mailPassword=""
mailFromAddress=""
/>
<playerTypes>
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
<!--<playerType name="Computer - minimax" jar="mage-player-aiminimax.jar" className="mage.player.ai.ComputerPlayer3"/>-->
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayer7"/>
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
<playerType name="Computer - draftbot" jar="mage-player-ai-draft-bot.jar" className="mage.player.ai.ComputerDraftPlayer"/>
</playerTypes>
<gameTypes>
<gameType name="Two Player Duel" jar="mage-game-twoplayerduel.jar" className="mage.game.TwoPlayerMatch" typeName="mage.game.TwoPlayerDuelType"/>
<gameType name="Free For All" jar="mage-game-freeforall.jar" className="mage.game.FreeForAllMatch" typeName="mage.game.FreeForAllType"/>
<gameType name="Commander Two Player Duel" jar="mage-game-commanderduel.jar" className="mage.game.CommanderDuelMatch" typeName="mage.game.CommanderDuelType"/>
<gameType name="Commander Free For All" jar="mage-game-commanderfreeforall.jar" className="mage.game.CommanderFreeForAllMatch" typeName="mage.game.CommanderFreeForAllType"/>
<gameType name="Tiny Leaders Two Player Duel" jar="mage-game-tinyleadersduel.jar" className="mage.game.TinyLeadersDuelMatch" typeName="mage.game.TinyLeadersDuelType"/>
<gameType name="Canadian Highlander Two Player Duel" jar="mage-game-canadianhighlanderduel.jar" className="mage.game.CanadianHighlanderDuelMatch" typeName="mage.game.CanadianHighlanderDuelType"/>
<gameType name="Penny Dreadful Commander Free For All" jar="mage-game-pennydreadfulcommanderfreeforall.jar" className="mage.game.PennyDreadfulCommanderFreeForAllMatch" typeName="mage.game.PennyDreadfulCommanderFreeForAllType"/>
<gameType name="Freeform Commander Two Player Duel" jar="mage-game-freeformcommanderduel.jar" className="mage.game.FreeformCommanderDuelMatch" typeName="mage.game.FreeformCommanderDuelType"/>
<gameType name="Freeform Commander Free For All" jar="mage-game-freeformcommanderfreeforall.jar" className="mage.game.FreeformCommanderFreeForAllMatch" typeName="mage.game.FreeformCommanderFreeForAllType"/>
<gameType name="Freeform Unlimited Commander" jar="mage-game-freeformunlimitedcommander.jar" className="mage.game.FreeformUnlimitedCommanderMatch" typeName="mage.game.FreeformUnlimitedCommanderType"/>
<gameType name="Oathbreaker Two Player Duel" jar="mage-game-oathbreakerduel.jar" className="mage.game.OathbreakerDuelMatch" typeName="mage.game.OathbreakerDuelType"/>
<gameType name="Oathbreaker Free For All" jar="mage-game-oathbreakerfreeforall.jar" className="mage.game.OathbreakerFreeForAllMatch" typeName="mage.game.OathbreakerFreeForAllType"/>
<gameType name="Brawl Two Player Duel" jar="mage-game-brawlduel.jar" className="mage.game.BrawlDuelMatch" typeName="mage.game.BrawlDuelType"/>
<gameType name="Brawl Free For All" jar="mage-game-brawlfreeforall.jar" className="mage.game.BrawlFreeForAllMatch" typeName="mage.game.BrawlFreeForAllType"/>
<gameType name="Momir Basic Two Player Duel" jar="mage-game-momirduel.jar" className="mage.game.MomirDuelMatch" typeName="mage.game.MomirDuelType"/>
<gameType name="Momir Basic Free For All" jar="mage-game-momir.jar" className="mage.game.MomirFreeForAllMatch" typeName="mage.game.MomirFreeForAllType"/>
</gameTypes>
<tournamentTypes>
<tournamentType name="Constructed Elimination" jar="mage-tournament-constructed.jar" className="mage.tournament.ConstructedEliminationTournament" typeName="mage.tournament.ConstructedEliminationTournamentType"/>
<tournamentType name="Constructed Swiss" jar="mage-tournament-constructed.jar" className="mage.tournament.ConstructedSwissTournament" typeName="mage.tournament.ConstructedSwissTournamentType"/>
<tournamentType name="Booster Draft Elimination" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationTournamentType"/>
<tournamentType name="Booster Draft Elimination (Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftEliminationTournament" typeName="mage.tournament.BoosterDraftEliminationCubeTournamentType"/>
<tournamentType name="Booster Draft Elimination (Random)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RandomBoosterDraftEliminationTournament" typeName="mage.tournament.RandomBoosterDraftEliminationTournamentType"/>
<tournamentType name="Booster Draft Elimination (Rich Man)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RichManDraftEliminationTournament" typeName="mage.tournament.RichManDraftEliminationTournamentType"/>
<tournamentType name="Booster Draft Elimination (Rich Man Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RichManCubeDraftEliminationTournament" typeName="mage.tournament.RichManCubeDraftEliminationTournamentType"/>
<tournamentType name="Booster Draft Swiss" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissTournamentType"/>
<tournamentType name="Booster Draft Swiss (Cube)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.BoosterDraftSwissTournament" typeName="mage.tournament.BoosterDraftSwissCubeTournamentType"/>
<tournamentType name="Booster Draft Swiss (Random)" jar="mage-tournament-booster-draft.jar" className="mage.tournament.RandomBoosterDraftSwissTournament" typeName="mage.tournament.RandomBoosterDraftSwissTournamentType"/>
<tournamentType name="Sealed Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationTournamentType"/>
<tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
<tournamentType name="Sealed Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
<tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
<tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
<tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
</tournamentTypes>
<draftCubes>
<draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>
<draftCube name="Ben's Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.BensCube"/>
<draftCube name="Cube Tutor 360 Pauper" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.CubeTutor360Pauper"/>
<draftCube name="Cube Tutor 720" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.CubeTutor720"/>
<draftCube name="Eric Klug's Pro Tour Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.EricKlugsProTourCube"/>
<draftCube name="Guillaume Matignon's Jenny's/Johnny's Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.GuillaumeMatignonsJennysJohnnysCube"/>
<draftCube name="Jim Davis's Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.JimDavisCube"/>
<draftCube name="Joseph Vasoli's Peasant Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.JosephVasolisPeasantCube"/>
<draftCube name="Mono Blue Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.MonoBlueCube"/>
<draftCube name="Sam Black's No Search Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.SamBlacksCube"/>
<draftCube name="Timothee Simonot's Twisted Color Pie Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.TimotheeSimonotsTwistedColorPieCube"/>
<draftCube name="MTGA Cube 2020 April" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.MTGACube2020April"/>
<draftCube name="MTGO Cube March 2014" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.MTGOMarchCube2014"/>
<draftCube name="MTGO Legacy Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCube"/>
<draftCube name="MTGO Legacy Cube 2015 March" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCubeMarch2015"/>
<draftCube name="MTGO Legacy Cube 2015 September" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCubeSeptember2015"/>
<draftCube name="MTGO Legacy Cube 2016 January" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCubeJanuary2016"/>
<draftCube name="MTGO Legacy Cube 2016 September" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCubeSeptember2016"/>
<draftCube name="MTGO Legacy Cube 2017 January" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCubeJanuary2017"/>
<draftCube name="MTGO Legacy Cube 2017 April" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCubeApril2017"/>
<draftCube name="MTGO Legacy Cube 2018 February" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCube2018February"/>
<draftCube name="MTGO Legacy Cube 2019 July" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegacyCubeJuly2019"/>
<draftCube name="MTGO Legendary Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegendaryCube"/>
<draftCube name="MTGO Legendary Cube April 2016" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.LegendaryCubeApril2016"/>
<draftCube name="MTGO Modern Cube 2017" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.ModernCube2017"/>
<draftCube name="MTGO Vintage Cube 2013" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCube2013"/>
<draftCube name="MTGO Vintage Cube 2014" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCube2014"/>
<draftCube name="MTGO Vintage Cube 2015" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCube2015"/>
<draftCube name="MTGO Vintage Cube 2016" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCube2016"/>
<draftCube name="MTGO Vintage Cube June 2016" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJune2016"/>
<draftCube name="MTGO Vintage Cube November 2016" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeNovember2016"/>
<draftCube name="MTGO Vintage Cube June 2017" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJune2017"/>
<draftCube name="MTGO Vintage Cube December 2017" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeDecember2017"/>
<draftCube name="MTGO Vintage Cube June 2018" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJune2018"/>
<draftCube name="MTGO Vintage Cube December 2018" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeDecember2018"/>
<draftCube name="MTGO Vintage Cube June 2019" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJune2019"/>
<draftCube name="MTGO Vintage Cube December 2019" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeDecember2019"/>
<draftCube name="MTGO Vintage Cube April 2020" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeApril2020"/>
<draftCube name="MTGO Vintage Cube July 2020" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJuly2020"/>
<draftCube name="SCG Con Cube 2018 December" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.ScgConCube2018December"/>
<draftCube name="The Peasant's Toolbox" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.PeasantsToolboxCube"/>
<draftCube name="www.MTGCube.com" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.MTGCube"/>
<draftCube name="Cube From Deck" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.CubeFromDeck"/>
</draftCubes>
<deckTypes>
<deckType name="Constructed - Standard" jar="mage-deck-constructed.jar" className="mage.deck.Standard"/>
<deckType name="Constructed - Extended" jar="mage-deck-constructed.jar" className="mage.deck.Extended"/>
<deckType name="Constructed - Frontier" jar="mage-deck-constructed.jar" className="mage.deck.Frontier"/>
<deckType name="Constructed - Pioneer" jar="mage-deck-constructed.jar" className="mage.deck.Pioneer"/>
<deckType name="Constructed - Modern" jar="mage-deck-constructed.jar" className="mage.deck.Modern"/>
<deckType name="Constructed - Modern - No Banned List" jar="mage-deck-constructed.jar" className="mage.deck.ModernNoBannedList"/>
<deckType name="Constructed - Eternal" jar="mage-deck-constructed.jar" className="mage.deck.Eternal"/>
<deckType name="Constructed - Legacy" jar="mage-deck-constructed.jar" className="mage.deck.Legacy"/>
<deckType name="Constructed - Vintage" jar="mage-deck-constructed.jar" className="mage.deck.Vintage"/>
<deckType name="Constructed - Pauper" jar="mage-deck-constructed.jar" className="mage.deck.Pauper"/>
<deckType name="Constructed - Historic" jar="mage-deck-constructed.jar" className="mage.deck.Historic"/>
<deckType name="Constructed - Historical Type 2" jar="mage-deck-constructed.jar" className="mage.deck.HistoricalType2"/>
<deckType name="Constructed - Super Type 2" jar="mage-deck-constructed.jar" className="mage.deck.SuperType2"/>
<deckType name="Constructed - Australian Highlander" jar="mage-deck-constructed.jar" className="mage.deck.AusHighlander"/>
<deckType name="Constructed - Canadian Highlander" jar="mage-deck-constructed.jar" className="mage.deck.CanadianHighlander"/>
<deckType name="Constructed - Old School 93/94" jar="mage-deck-constructed.jar" className="mage.deck.OldSchool9394"/>
<deckType name="Constructed - Old School 93/94 - Italian Rules" jar="mage-deck-constructed.jar" className="mage.deck.OldSchool9394Italian"/>
<deckType name="Constructed - Old School 93/94 - Channel Fireball Rules" jar="mage-deck-constructed.jar" className="mage.deck.OldSchool9394CFB"/>
<deckType name="Constructed - Old School 93/94 - EudoGames Rules" jar="mage-deck-constructed.jar" className="mage.deck.OldSchool9394EG"/>
<deckType name="Constructed - Old School 93/94 - EC Rules" jar="mage-deck-constructed.jar" className="mage.deck.OldSchool9394EC"/>
<deckType name="Constructed - Premodern" jar="mage-deck-constructed.jar" className="mage.deck.Premodern"/>
<deckType name="Constructed - Freeform" jar="mage-deck-constructed.jar" className="mage.deck.Freeform"/>
<deckType name="Variant Magic - Commander" jar="mage-deck-constructed.jar" className="mage.deck.Commander"/>
<deckType name="Variant Magic - Duel Commander" jar="mage-deck-constructed.jar" className="mage.deck.DuelCommander"/>
<deckType name="Variant Magic - MTGO 1v1 Commander" jar="mage-deck-constructed.jar" className="mage.deck.MTGO1v1Commander"/>
<deckType name="Variant Magic - Centurion Commander" jar="mage-deck-constructed.jar" className="mage.deck.CenturionCommander"/>
<deckType name="Variant Magic - Tiny Leaders" jar="mage-deck-constructed.jar" className="mage.deck.TinyLeaders"/>
<deckType name="Variant Magic - Momir Basic" jar="mage-deck-constructed.jar" className="mage.deck.Momir"/>
<deckType name="Variant Magic - Penny Dreadful Commander" jar="mage-deck-constructed.jar" className="mage.deck.PennyDreadfulCommander"/>
<deckType name="Variant Magic - Freeform Commander" jar="mage-deck-constructed.jar" className="mage.deck.FreeformCommander"/>
<deckType name="Variant Magic - Freeform Unlimited Commander" jar="mage-deck-constructed.jar" className="mage.deck.FreeformUnlimitedCommander"/>
<deckType name="Variant Magic - Brawl" jar="mage-deck-constructed.jar" className="mage.deck.Brawl"/>
<deckType name="Variant Magic - Oathbreaker" jar="mage-deck-constructed.jar" className="mage.deck.Oathbreaker"/>
<deckType name="Block Constructed - Amonkhet" jar="mage-deck-constructed.jar" className="mage.deck.AmonkhetBlock"/>
<deckType name="Block Constructed - Battle for Zendikar" jar="mage-deck-constructed.jar" className="mage.deck.BattleForZendikarBlock"/>
<deckType name="Block Constructed - Innistrad" jar="mage-deck-constructed.jar" className="mage.deck.InnistradBlock"/>
<deckType name="Block Constructed - Ixalan" jar="mage-deck-constructed.jar" className="mage.deck.IxalanBlock"/>
<deckType name="Block Constructed - Kaladesh" jar="mage-deck-constructed.jar" className="mage.deck.KaladeshBlock"/>
<deckType name="Block Constructed - Kamigawa" jar="mage-deck-constructed.jar" className="mage.deck.KamigawaBlock"/>
<deckType name="Block Constructed - Khans of Tarkir" jar="mage-deck-constructed.jar" className="mage.deck.KhansOfTarkirBlock"/>
<deckType name="Block Constructed - Lorwyn" jar="mage-deck-constructed.jar" className="mage.deck.LorwynBlock"/>
<deckType name="Block Constructed - Return to Ravnica" jar="mage-deck-constructed.jar" className="mage.deck.ReturnToRavnicaBlock"/>
<deckType name="Block Constructed - Scars of Mirrodin" jar="mage-deck-constructed.jar" className="mage.deck.ScarsOfMirrodinBlock"/>
<deckType name="Block Constructed - Shadowmoor" jar="mage-deck-constructed.jar" className="mage.deck.ShadowmoorBlock"/>
<deckType name="Block Constructed - Shadows over Innistrad" jar="mage-deck-constructed.jar" className="mage.deck.ShadowsOverInnistradBlock"/>
<deckType name="Block Constructed - Shards of Alara" jar="mage-deck-constructed.jar" className="mage.deck.ShardsOfAlaraBlock"/>
<deckType name="Block Constructed - Theros" jar="mage-deck-constructed.jar" className="mage.deck.TherosBlock"/>
<deckType name="Block Constructed - Zendikar" jar="mage-deck-constructed.jar" className="mage.deck.ZendikarBlock"/>
<deckType name="Block Constructed Custom - Star Wars" jar="mage-deck-constructed.jar" className="mage.deck.StarWarsBlock"/>
<deckType name="Limited" jar="mage-deck-limited.jar" className="mage.deck.Limited"/>
</deckTypes>
</config>

View file

@ -32,7 +32,7 @@ public final class AgonyWarp extends CardImpl {
// Target creature gets -0/-3 until end of turn.
Effect effect2 = new BoostTargetEffect(-0,-3, Duration.EndOfTurn);
effect2.setText("<br></br>Target creature gets -0/-3 until end of turn");
effect2.setText("<br><br>Target creature gets -0/-3 until end of turn");
effect2.setTargetPointer(SecondTargetPointer.getInstance());
this.getSpellAbility().addEffect(effect2);
target = new TargetCreaturePermanent(new FilterCreaturePermanent("second creature (can be the same as the first)"));

View file

@ -78,7 +78,18 @@ class ArchelosLagoonMysticEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return !source.getSourceId().equals(((EntersTheBattlefieldEvent) event).getTarget().getId());
Permanent sourceObject = game.getPermanent(source.getSourceId());
if (sourceObject == null) {
return false;
}
Permanent targetObject = ((EntersTheBattlefieldEvent) event).getTarget();
if (targetObject == null) {
return false;
}
return !sourceObject.getId().equals(targetObject.getId())
&& sourceObject.isTapped() == this.tapped;
}
@Override

View file

@ -68,6 +68,6 @@ enum ArcticFoxesCondition implements Condition {
if (defenderId == null) {
return false;
}
return game.getBattlefield().contains(filter, defenderId, 1, game);
return game.getBattlefield().contains(filter, source.getSourceId(), defenderId, game, 1);
}
}

View file

@ -33,7 +33,7 @@ public final class AzoriusPloy extends CardImpl {
// Prevent all combat damage that would be dealt to target creature this turn.
Effect effect2 = new PreventDamageToTargetEffect(Duration.EndOfTurn, true);
effect2.setText("<br></br>Prevent all combat damage that would be dealt to target creature this turn.");
effect2.setText("<br><br>Prevent all combat damage that would be dealt to target creature this turn.");
effect2.setTargetPointer(SecondTargetPointer.getInstance());
this.getSpellAbility().addEffect(effect2);
target = new TargetCreaturePermanent(new FilterCreaturePermanent("second creature (can be the same as the first)"));

View file

@ -63,16 +63,15 @@ class BacklashEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
boolean applied = false;
Permanent targetCreature = game.getPermanent(targetPointer.getFirst(game, source));
if (targetCreature != null) {
applied = targetCreature.tap(source, game);
targetCreature.tap(source, game);
Player controller = game.getPlayer(targetCreature.getControllerId());
if (controller != null) {
controller.damage(targetCreature.getPower().getValue(), source.getSourceId(), source, game);
applied = true;
controller.damage(targetCreature.getPower().getValue(), targetCreature.getId(), source, game);
return true;
}
}
return applied;
return false;
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.b;
import java.util.UUID;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.counter.DistributeCountersEffect;
import mage.abilities.keyword.MiracleAbility;
@ -11,15 +9,15 @@ import mage.constants.CardType;
import mage.counters.CounterType;
import mage.target.common.TargetCreaturePermanentAmount;
import java.util.UUID;
/**
*
* @author North
*/
public final class BlessingsOfNature extends CardImpl {
public BlessingsOfNature(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{G}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}");
// Distribute four +1/+1 counters among any number of target creatures.
this.getSpellAbility().addEffect(new DistributeCountersEffect(CounterType.P1P1, 4, false, "any number of target creatures"));

View file

@ -1,8 +1,5 @@
package mage.cards.c;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.StateTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
@ -11,7 +8,6 @@ import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import static mage.cards.c.CityInABottle.getArabianNightsNamePredicates;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
@ -23,11 +19,15 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static mage.cards.c.CityInABottle.getArabianNightsNamePredicates;
/**
*
* @author emerald000
*/
public final class CityInABottle extends CardImpl {
@ -158,7 +158,7 @@ class CityInABottleStateTriggeredAbility extends StateTriggeredAbility {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return game.getBattlefield().contains(filter, this.getControllerId(), game, 1);
return game.getBattlefield().contains(filter, this.getSourceId(), this.getControllerId(), game, 1);
}
@Override

View file

@ -1,7 +1,5 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
@ -18,14 +16,15 @@ import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class ConcertedEffort extends CardImpl {
public ConcertedEffort(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}");
// At the beginning of each upkeep, creatures you control gain flying until end of turn if a creature you control has flying. The same is true for fear, first strike, double strike, landwalk, protection, trample, and vigilance.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConcertedEffortEffect(), TargetController.ANY, false));
@ -81,22 +80,22 @@ class ConcertedEffortEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterFlying, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Fear
if (game.getBattlefield().contains(filterFear, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterFear, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FearAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterFirstStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterDoubleStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
@ -119,12 +118,12 @@ class ConcertedEffortEffect extends OneShotEffect {
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterTrample, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
if (game.getBattlefield().contains(filterVigilance, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
return true;

View file

@ -70,18 +70,19 @@ class CourtOfAmbitionEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
int discardCount = source.isControlledBy(game.getMonarchId()) ? 2 : 1;
String message = "Discard " + CardUtil.numberToText(discardCount, "a")
+ "card" + (discardCount > 1 ? 's' : "") + "? If not you lose " + (discardCount * 3) + " life";
+ " card" + (discardCount > 1 ? 's' : "") + "? If not you lose " + (discardCount * 3) + " life";
Map<UUID, Cards> discardMap = new HashMap<>();
for (UUID playerId : game.getOpponents(source.getControllerId())) {
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
if (player.getHand().size() < discardCount || !player.chooseUse(outcome, message, source, game)) {
if (player.getHand().size() < discardCount || !player.chooseUse(Outcome.LoseLife, message, source, game)) {
player.loseLife(discardCount * 3, game, source, false);
continue;
}
TargetDiscard target = new TargetDiscard(discardCount, StaticFilters.FILTER_CARD, playerId);
player.choose(outcome, target, source.getSourceId(), game);
player.choose(Outcome.Discard, target, source.getSourceId(), game);
discardMap.put(playerId, new CardsImpl(target.getTargets()));
}
for (Map.Entry<UUID, Cards> entry : discardMap.entrySet()) {

View file

@ -1,7 +1,5 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.common.DestroyTargetEffect;
@ -12,27 +10,30 @@ import mage.constants.ComparisonType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import java.util.UUID;
/**
*
* @author cg5
*/
public final class CullingScales extends CardImpl {
private static final FilterPermanent filterNonlandPermanentWithLowestCmc = new FilterNonlandPermanent(
"nonland permanent with the lowest converted mana cost (<i>If two or more permanents are tied for lowest cost, target any one of them.</i>)"
"nonland permanent with the lowest converted mana cost (<i>If two or more permanents are tied for lowest cost, target any one of them.</i>)"
);
static {
filterNonlandPermanentWithLowestCmc.add(new HasLowestCMCAmongstNonlandPermanentsPredicate());
}
public CullingScales(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// At the beginning of your upkeep, destroy target nonland permanent with the lowest converted mana cost.
Ability ability = new BeginningOfUpkeepTriggeredAbility(new DestroyTargetEffect(), TargetController.YOU, false);
@ -48,16 +49,16 @@ public final class CullingScales extends CardImpl {
public CullingScales copy() {
return new CullingScales(this);
}
}
class HasLowestCMCAmongstNonlandPermanentsPredicate implements Predicate<Permanent> {
class HasLowestCMCAmongstNonlandPermanentsPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<Permanent>> {
@Override
public boolean apply(Permanent input, Game game) {
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
FilterPermanent filter = new FilterNonlandPermanent();
filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, input.getConvertedManaCost()));
return !game.getBattlefield().contains(filter, 1, game);
filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, input.getObject().getConvertedManaCost()));
return !game.getBattlefield().contains(filter, input.getSourceId(), input.getPlayerId(), game, 1);
}
}

View file

@ -84,7 +84,7 @@ class DoomForetoldEffect extends OneShotEffect {
}
FilterPermanent filter2 = filter.copy();
filter2.add(new ControllerIdPredicate(player.getId()));
if (game.getBattlefield().contains(filter2, 1, game)) {
if (game.getBattlefield().contains(filter2, source, game, 1)) {
TargetPermanent target = new TargetPermanent(filter2);
target.setNotTarget(true);
if (player.choose(Outcome.Sacrifice, target, source.getSourceId(), game)) {

View file

@ -1,7 +1,5 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
@ -24,14 +22,15 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class HedronAlignment extends CardImpl {
public HedronAlignment(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
// Hexproof
this.addAbility(HexproofAbility.getInstance());
@ -85,7 +84,7 @@ class HedronAlignmentEffect extends OneShotEffect {
Cards cardsToReveal = new CardsImpl();
controller.revealCards(sourceObject.getIdName(), cardsToReveal, game);
// Check battlefield
if (!game.getBattlefield().contains(filterPermanent, source.getControllerId(), game, 1)) {
if (!game.getBattlefield().contains(filterPermanent, source, game, 1)) {
return true;
}
if (controller.getHand().getCards(filterCard, source.getSourceId(), controller.getId(), game).isEmpty()) {

View file

@ -1,7 +1,5 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
@ -17,12 +15,7 @@ import mage.abilities.keyword.CumulativeUpkeepAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledLandPermanent;
import mage.filter.common.FilterLandPermanent;
@ -34,13 +27,15 @@ import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class HeraldOfLeshrac extends CardImpl {
private static final FilterPermanent filter = new FilterControlledLandPermanent("land you control but don't own");
static {
filter.add(TargetController.NOT_YOU.getOwnerPredicate());
}
@ -78,6 +73,7 @@ public final class HeraldOfLeshrac extends CardImpl {
class HeraldOfLeshracCumulativeCost extends CostImpl {
private static final FilterPermanent filter = new FilterLandPermanent("land you don't control");
static {
filter.add(TargetController.NOT_YOU.getControllerPredicate());
}
@ -105,7 +101,7 @@ class HeraldOfLeshracCumulativeCost extends CostImpl {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return game.getBattlefield().contains(filter, controllerId, game, 1);
return game.getBattlefield().contains(filter, source.getSourceId(), controllerId, game, 1);
}
@Override

View file

@ -1,24 +1,18 @@
package mage.cards.i;
import java.util.UUID;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.PreventDamageToAttachedEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.constants.*;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class Inviolability extends CardImpl {
@ -34,7 +28,9 @@ public final class Inviolability extends CardImpl {
this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
// Prevent all damage that would be dealt to enchanted creature.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PreventDamageToAttachedEffect(Duration.WhileOnBattlefield, AttachmentType.EQUIPMENT, false)));
this.addAbility(new SimpleStaticAbility(new PreventDamageToAttachedEffect(
Duration.WhileOnBattlefield, AttachmentType.AURA, false
)));
}
public Inviolability(final Inviolability card) {

View file

@ -103,62 +103,62 @@ class MajesticMyriarchEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFlying, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), source);
}
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFirstStrike, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDoubleStrike, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), source);
}
// Deathtouch
if (game.getBattlefield().contains(filterDeathtouch, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDeathtouch, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn), source);
}
// Haste
if (game.getBattlefield().contains(filterHaste, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHaste, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), source);
}
// Hexproof
if (game.getBattlefield().contains(filterHexproof, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHexproof, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(HexproofAbility.getInstance(), Duration.EndOfTurn), source);
}
// Indestructible
if (game.getBattlefield().contains(filterIndestructible, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterIndestructible, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), source);
}
// Lifelink
if (game.getBattlefield().contains(filterLifelink, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterLifelink, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn), source);
}
// Menace
if (game.getBattlefield().contains(filterMenace, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterMenace, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn), source);
}
// Reach
if (game.getBattlefield().contains(filterReach, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterReach, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(ReachAbility.getInstance(), Duration.EndOfTurn), source);
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterTrample, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterVigilance, source, game, 1)) {
game.addEffect(new GainAbilitySourceEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn), source);
}
return true;

View file

@ -32,7 +32,7 @@ public final class MartialGlory extends CardImpl {
// Target creature gets +0/+3 until end of turn.
Effect effect2 = new BoostTargetEffect(0,3, Duration.EndOfTurn);
effect2.setText("<br></br>Target creature gets +0/+3 until end of turn");
effect2.setText("<br><br>Target creature gets +0/+3 until end of turn");
effect2.setTargetPointer(SecondTargetPointer.getInstance());
target = new TargetCreaturePermanent(new FilterCreaturePermanent("second creature (can be the same as the first)"));
this.getSpellAbility().addEffect(effect2);

View file

@ -1,4 +1,3 @@
package mage.cards.o;
import mage.MageInt;
@ -94,67 +93,67 @@ class OdricLunarchMarshalEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFirstStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterFlying, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Deathtouch
if (game.getBattlefield().contains(filterDeathtouch, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDeathtouch, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Double strike
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterDoubleStrike, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Haste
if (game.getBattlefield().contains(filterHaste, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHaste, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Hexproof
if (game.getBattlefield().contains(filterHexproof, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterHexproof, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Indestructible
if (game.getBattlefield().contains(filterIndestructible, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterIndestructible, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Lifelink
if (game.getBattlefield().contains(filterLifelink, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterLifelink, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Menace
if (game.getBattlefield().contains(filterMenace, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterMenace, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn, filterCreatures), source);
}
// Reach
if (game.getBattlefield().contains(filterReach, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterReach, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(ReachAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Skulk
if (game.getBattlefield().contains(filterSkulk, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterSkulk, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(new SkulkAbility(), Duration.EndOfTurn, filterCreatures), source);
}
// Trample
if (game.getBattlefield().contains(filterTrample, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterTrample, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Vigilance
if (game.getBattlefield().contains(filterVigilance, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filterVigilance, source, game, 1)) {
game.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
return true;

View file

@ -0,0 +1,59 @@
package mage.cards.r;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect;
import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
import mage.abilities.keyword.ChangelingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
/**
*
* @author weirddan455
*/
public final class Realmwalker extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard("cast creature spells of the chosen type");
static {
filter.add(ChosenSubtypePredicate.instance);
}
public Realmwalker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
this.subtype.add(SubType.SHAPESHIFTER);
this.power = new MageInt(2);
this.toughness = new MageInt(3);
// Changeling
this.setIsAllCreatureTypes(true);
this.addAbility(ChangelingAbility.getInstance());
// As Realmwalker enters the battlefield, choose a creature type.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.Benefit)));
// You may look at the top card of your library any time.
this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect()));
// You may cast creature spells of the chosen type from the top of your library.
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(filter)));
}
private Realmwalker(final Realmwalker card) {
super(card);
}
@Override
public Realmwalker copy() {
return new Realmwalker(this);
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.r;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
@ -16,14 +14,15 @@ import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class RemorselessPunishment extends CardImpl {
public RemorselessPunishment(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
// Target opponent loses 5 life unless that player discards two cards or sacrifices a creature or planeswalker. Repeat this process once.
getSpellAbility().addEffect(new RemorselessPunishmentEffect());
@ -80,7 +79,7 @@ class RemorselessPunishmentEffect extends OneShotEffect {
return;
}
}
if (game.getBattlefield().contains(filter, opponent.getId(), 1, game)) {
if (game.getBattlefield().containsControlled(filter, source.getSourceId(), opponent.getId(), game, 1)) {
if (opponent.chooseUse(outcome, "Choose your " + iteration + " punishment.", null, "Sacrifice a creature or planeswalker", "Lose 5 life", source, game)) {
TargetPermanent target = new TargetPermanent(1, 1, filter, true);
if (target.choose(Outcome.Sacrifice, opponent.getId(), source.getId(), game)) {

View file

@ -1,7 +1,5 @@
package mage.cards.s;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@ -11,8 +9,8 @@ import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
@ -22,8 +20,9 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class ShelteringAncient extends CardImpl {
@ -82,7 +81,7 @@ class ShelteringAncientCost extends CostImpl {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return game.getBattlefield().contains(filter, source.getSourceId(), game, 1);
return game.getBattlefield().contains(filter, source, game, 1);
}
@Override

View file

@ -0,0 +1,98 @@
package mage.cards.s;
import java.util.UUID;
import mage.ApprovingObject;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.costs.OrCost;
import mage.abilities.costs.common.RevealTargetFromHandCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
/**
*
* @author weirddan455
*/
public final class SurtlandElementalist extends CardImpl {
private static final FilterCard filter = new FilterCard("a Giant card from your hand");
static {
filter.add(SubType.GIANT.getPredicate());
}
public SurtlandElementalist(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}");
this.subtype.add(SubType.GIANT);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(8);
this.toughness = new MageInt(8);
// As an additional cost to cast this spell, reveal a Giant card from your hand or pay {2}.
this.getSpellAbility().addCost(new OrCost(
new RevealTargetFromHandCost(new TargetCardInHand(filter)),
new GenericManaCost(2),
"reveal a Giant card from your hand or pay {2}"));
// Whenever Surtland Elementalist attacks, you may cast an instant or sorcery spell from your hand without paying its mana cost.
this.addAbility(new AttacksTriggeredAbility(new SurtlandElementalistEffect(), true));
}
private SurtlandElementalist(final SurtlandElementalist card) {
super(card);
}
@Override
public SurtlandElementalist copy() {
return new SurtlandElementalist(this);
}
}
class SurtlandElementalistEffect extends OneShotEffect {
public SurtlandElementalistEffect () {
super(Outcome.PlayForFree);
this.staticText = "cast an instant or sorcery spell from your hand without paying its mana cost";
}
private SurtlandElementalistEffect(final SurtlandElementalistEffect effect) {
super(effect);
}
@Override
public SurtlandElementalistEffect copy() {
return new SurtlandElementalistEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
TargetCardInHand target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY);
if (player.chooseTarget(Outcome.PlayForFree, target, source, game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE);
boolean cardWasCast = player.cast(player.chooseAbilityForCast(card, game, true),
game, true, new ApprovingObject(source, game));
game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null);
return cardWasCast;
}
}
}
return false;
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.t;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
@ -18,8 +16,9 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author spjspj
*/
public final class TheCheeseStandsAlone extends CardImpl {
@ -47,6 +46,7 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp
private static final FilterControlledPermanent filter = new FilterControlledPermanent();
private boolean wonAlready = false;
static {
filter.add(new NamePredicate("The Cheese Stands Alone"));
}
@ -57,7 +57,7 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp
}
public CheeseStandsAloneContinuousEffect(final CheeseStandsAloneContinuousEffect effect) {
super(effect);
super(effect);
}
@Override
@ -72,7 +72,7 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp
if (controller.getHand().isEmpty()) {
int numberPerms = new PermanentsOnBattlefieldCount(new FilterControlledPermanent()).calculate(game, source, this);
if (numberPerms == 1) {
if (game.getBattlefield().contains(filter, source.getControllerId(), 1, game)) {
if (game.getBattlefield().containsControlled(filter, source, game, 1)) {
if (!wonAlready) {
wonAlready = true;
controller.won(game);

View file

@ -1,7 +1,5 @@
package mage.cards.t;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.StateTriggeredAbility;
@ -31,8 +29,9 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import java.util.UUID;
/**
*
* @author LoneFox
*/
public final class TidalInfluence extends CardImpl {
@ -44,26 +43,26 @@ public final class TidalInfluence extends CardImpl {
}
public TidalInfluence(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
// Cast Tidal Influence only if no permanents named Tidal Influence are on the battlefield.
this.getSpellAbility().addCost(new TidalInfluenceCost());
// Tidal Influence enters the battlefield with a tide counter on it.
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()),
"with a tide counter on it."));
"with a tide counter on it."));
// At the beginning of your upkeep, put a tide counter on Tidal Influence.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()),
TargetController.YOU, false));
TargetController.YOU, false));
// As long as there is exactly one tide counter on Tidal Influence, all blue creatures get -2/-0.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostAllEffect(-2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 1, 1),
"As long as there is exactly one tide counter on {this}, all blue creatures get -2/-0.")));
new BoostAllEffect(-2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 1, 1),
"As long as there is exactly one tide counter on {this}, all blue creatures get -2/-0.")));
// As long as there are exactly three tide counters on Tidal Influence, all blue creatures get +2/+0.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(
new BoostAllEffect(2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 3, 3),
"As long as there are exactly three tide counter on {this}, all blue creatures get +2/-0.")));
new BoostAllEffect(2, -0, Duration.WhileOnBattlefield, filter, false),
new SourceHasCounterCondition(CounterType.TIDE, 3, 3),
"As long as there are exactly three tide counter on {this}, all blue creatures get +2/-0.")));
// Whenever there are four tide counters on Tidal Influence, remove all tide counters from it.
this.addAbility(new TidalInfluenceTriggeredAbility(new RemoveAllCountersSourceEffect(CounterType.TIDE)));
}
@ -97,7 +96,7 @@ class TidalInfluenceCost extends CostImpl {
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return !game.getBattlefield().contains(filter, 1, game);
return !game.getBattlefield().contains(filter, source, game, 1);
}
@Override

View file

@ -1,13 +1,13 @@
package mage.cards.t;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
import mage.abilities.effects.common.ChooseLandTypeEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
@ -15,24 +15,24 @@ import mage.abilities.keyword.EnchantAbility;
import mage.abilities.keyword.LandwalkAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class TravelersCloak extends CardImpl {
public TravelersCloak(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
this.subtype.add(SubType.AURA);
// Enchant creature
@ -41,18 +41,17 @@ public final class TravelersCloak extends CardImpl {
this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// As Traveler's Cloak enters the battlefield, choose a land type.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseLandTypeEffect(Outcome.AddAbility)));
// When Traveler's Cloak enters the battlefield, draw a card.
this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false));
// Enchanted creature has landwalk of the chosen type.
FilterLandPermanent filter = new FilterLandPermanent("Landwalk of the chosen type");
filter.add(ChosenSubtypePredicate.instance);
Ability landwalkAbility = new LandwalkAbility(filter);
Effect effect = new GainAbilityAttachedEffect(landwalkAbility, AttachmentType.AURA);
filter.add(TravelersCloakChosenSubtypePredicate.instance);
Effect effect = new TravelersCloakGainAbilityAttachedEffect(filter);
effect.setText("Enchanted creature has landwalk of the chosen type");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
}
@ -66,3 +65,35 @@ public final class TravelersCloak extends CardImpl {
return new TravelersCloak(this);
}
}
class TravelersCloakGainAbilityAttachedEffect extends GainAbilityAttachedEffect {
public TravelersCloakGainAbilityAttachedEffect(FilterLandPermanent filter) {
super(new LandwalkAbility(filter), AttachmentType.AURA);
}
@Override
public void afterGain(Game game, Ability source, Permanent permanent, Ability addedAbility) {
super.afterGain(game, source, permanent, addedAbility);
// ChooseLandTypeEffect keep settings in original source, but we must transfer it to real permanent
Object val = game.getState().getValue(source.getSourceId() + "_type");
game.getState().setValue(permanent.getId() + "_landwalk_type", val);
}
}
enum TravelersCloakChosenSubtypePredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<MageObject>> {
instance;
@Override
public boolean apply(ObjectSourcePlayer<MageObject> input, Game game) {
SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(input.getSourceId(), game, "_landwalk_type");
return input.getObject().hasSubtype(subType, game);
}
@Override
public String toString() {
return "Chosen subtype";
}
}

View file

@ -107,7 +107,8 @@ class VivienMonstersAdvocateTokenEffect extends OneShotEffect {
if (permanent == null) {
continue;
}
Choice choice = new ChoiceImpl();
Choice choice = new ChoiceImpl(true);
choice.setMessage("Choose vigilance, reach, or trample counter");
choice.setChoices(choices);
player.choose(outcome, choice, game);
String chosen = choice.getChoice();

View file

@ -1,9 +1,5 @@
package mage.cards.w;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
@ -25,8 +21,12 @@ import mage.target.TargetPermanent;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class WeightOfConscience extends CardImpl {
@ -84,8 +84,8 @@ class WeightOfConscienceEffect extends OneShotEffect {
// It was not blinked, use the standard method
enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
}
if (controller != null
&& enchantment != null
if (controller != null
&& enchantment != null
&& enchantment.getAttachedTo() != null) {
Permanent creature = game.getPermanent(enchantment.getAttachedTo());
if (creature != null) {
@ -119,10 +119,10 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
if (player != null) {
// Choosing first target
if (this.getTargets().isEmpty()) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
for (SubType subtype : permanent.getSubtype(game)) {
if (subtype.getSubTypeSet() == SubTypeSet.CreatureType) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), sourceControllerId, game, 2)) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), sourceId, sourceControllerId, game, 2)) {
possibleTargets.add(permanent.getId());
}
}
@ -133,7 +133,7 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
UUID firstTargetId = this.getTargets().get(0);
Permanent firstTargetCreature = game.getPermanent(firstTargetId);
if (firstTargetCreature != null) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
if (!permanent.getId().equals(firstTargetId) && firstTargetCreature.shareCreatureTypes(permanent, game)) {
possibleTargets.add(permanent.getId());
}
@ -146,8 +146,8 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
@Override
public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
for (Permanent permanent1 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent2 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, game)) {
for (Permanent permanent1 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
for (Permanent permanent2 : game.getBattlefield().getActivePermanents(filterUntapped, sourceControllerId, sourceId, game)) {
if (!Objects.equals(permanent1, permanent2) && permanent1.shareCreatureTypes(permanent2, game)) {
return true;
}
@ -162,10 +162,10 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
Permanent targetPermanent = game.getPermanent(id);
if (targetPermanent != null) {
if (this.getTargets().isEmpty()) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, source.getControllerId(), game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterUntapped, source.getControllerId(), source.getSourceId(), game)) {
for (SubType subtype : permanent.getSubtype(game)) {
if (subtype.getSubTypeSet() == SubTypeSet.CreatureType) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), source.getControllerId(), game, 2)) {
if (game.getBattlefield().contains(new FilterControlledCreaturePermanent(subtype, subtype.toString()), source, game, 2)) {
return true;
}
}
@ -173,9 +173,7 @@ class WeightOfConscienceTarget extends TargetControlledCreaturePermanent {
}
} else {
Permanent firstTarget = game.getPermanent(this.getTargets().get(0));
if (firstTarget != null && firstTarget.shareCreatureTypes(targetPermanent, game)) {
return true;
}
return firstTarget != null && firstTarget.shareCreatureTypes(targetPermanent, game);
}
}
}

View file

@ -69,7 +69,7 @@ public final class Wirecat extends CardImpl {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent.getId().equals(source.getSourceId())) {
return game.getBattlefield().contains(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, 1, game);
return game.getBattlefield().contains(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, source, game, 1);
}
return false;
}

View file

@ -44,9 +44,11 @@ public final class Kaldheim extends ExpansionSet {
cards.add(new SetCardInfo("Gladewalker Ritualist", 392, Rarity.UNCOMMON, mage.cards.g.GladewalkerRitualist.class));
cards.add(new SetCardInfo("Hengegate Pathway", 260, Rarity.RARE, mage.cards.h.HengegatePathway.class));
cards.add(new SetCardInfo("Rampage of the Valkyries", 393, Rarity.UNCOMMON, mage.cards.r.RampageOfTheValkyries.class));
cards.add(new SetCardInfo("Realmwalker", 188, Rarity.RARE, mage.cards.r.Realmwalker.class));
cards.add(new SetCardInfo("Renegade Reaper", 386, Rarity.UNCOMMON, mage.cards.r.RenegadeReaper.class));
cards.add(new SetCardInfo("Showdown of the Skalds", 229, Rarity.RARE, mage.cards.s.ShowdownOfTheSkalds.class));
cards.add(new SetCardInfo("Starnheim Aspirant", 380, Rarity.UNCOMMON, mage.cards.s.StarnheimAspirant.class));
cards.add(new SetCardInfo("Surtland Elementalist", 375, Rarity.RARE, mage.cards.s.SurtlandElementalist.class));
cards.add(new SetCardInfo("Surtland Flinger", 377, Rarity.RARE, mage.cards.s.SurtlandFlinger.class));
}
}

View file

@ -0,0 +1,53 @@
package org.mage.test.AI.basic;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
/**
* @author JayDi85
*/
public class TargetAmountAITest extends CardTestPlayerBaseWithAIHelps {
@Test
public void test_AI_ChooseTargets() {
// Distribute four +1/+1 counters among any number of target creatures.
addCard(Zone.HAND, playerA, "Blessings of Nature", 1); // {4}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
//
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4); // 2/2
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 4); // 2/2
// ai must choose by special dialog, not full simulation
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blessings of Nature");
setStopAt(1, PhaseStep.END_TURN);
//setStrictChooseMode(true); // ai must choose
execute();
assertAllCommandsUsed();
assertPowerToughness(playerA, "Balduvian Bears", 2 + 4, 2 + 4); // boost one creature (it's just a choose code, so can be different from simulation results)
}
@Test
public void test_AI_SimulateTargets() {
// Distribute four +1/+1 counters among any number of target creatures.
addCard(Zone.HAND, playerA, "Blessings of Nature", 1); // {4}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
//
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 4); // 2/2
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 4); // 2/2
// AI must put creatures on own permanents (all in one creature to boost it)
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.END_TURN);
setStrictChooseMode(true);
execute();
assertAllCommandsUsed();
assertPowerToughness(playerA, "Balduvian Bears", 2 + 1, 2 + 1); // boost each possible creatures
assertPowerToughness(playerB, "Balduvian Bears", 2, 2); // no boost for enemy
}
}

View file

@ -1,4 +1,3 @@
package org.mage.test.cards.conditional;
import mage.constants.PhaseStep;
@ -7,7 +6,6 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class LegendarySorceryTest extends CardTestPlayerBase {
@ -55,10 +53,13 @@ public class LegendarySorceryTest extends CardTestPlayerBase {
// Flying, first strike, vigilance, trample, haste, protection from black and from red
addCard(Zone.BATTLEFIELD, playerB, "Akroma, Angel of Wrath", 1); // Legendary
// can't cast cause you don't have a legendary creature (only opponent have)
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Urza's Ruinous Blast");
//setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
//assertAllCommandsUsed();
assertGraveyardCount(playerA, "Urza's Ruinous Blast", 0);

View file

@ -0,0 +1,46 @@
package org.mage.test.cards.single.cmr;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class ArchelosLagoonMysticTest extends CardTestPlayerBase {
@Test
public void test_Playable() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2 + 2); // add lands before Archelos, Lagoon Mystic to ignore ETB effects
// As long as Archelos, Lagoon Mystic is tapped, other permanents enter the battlefield tapped.
// As long as Archelos, Lagoon Mystic is untapped, other permanents enter the battlefield untapped.
addCard(Zone.BATTLEFIELD, playerA, "Archelos, Lagoon Mystic", 1);
//
addCard(Zone.HAND, playerA, "Grizzly Bears", 1); // {1}{G}
addCard(Zone.HAND, playerA, "Deranged Outcast", 1); // {1}{G}
// first - untapped
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkPermanentTapped("untapped", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Archelos, Lagoon Mystic", false, 1);
checkPermanentTapped("untapped", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", false, 1);
// prepare tapped mystic
attack(1, playerA, "Archelos, Lagoon Mystic", playerB);
checkPermanentTapped("tapped", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Archelos, Lagoon Mystic", true, 1);
// second - tapped
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Deranged Outcast");
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN);
checkPermanentTapped("tapped", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Archelos, Lagoon Mystic", true, 1);
checkPermanentTapped("tapped", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Deranged Outcast", true, 1);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
}

View file

@ -0,0 +1,54 @@
package org.mage.test.cards.single.inv;
import mage.abilities.keyword.LandwalkAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class TravelersCloakTest extends CardTestPlayerBase {
@Test
public void test_MustHaveLandWalkOfTheChosenType() {
// Enchant creature
// As Traveler's Cloak enters the battlefield, choose a land type.
// When Traveler's Cloak enters the battlefield, draw a card.
// Enchanted creature has landwalk of the chosen type.
addCard(Zone.HAND, playerA, "Traveler's Cloak"); // {2}{U}
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
//
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
addCard(Zone.BATTLEFIELD, playerB, "Kitesail Corsair", 1);
// cast and assign landwalk ability to creature
checkAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", LandwalkAbility.class, false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Traveler's Cloak", "Grizzly Bears");
setChoice(playerA, "Swamp"); // land type for landwalk
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", LandwalkAbility.class, true);
// check that it can't be blocked
attack(1, playerA, "Grizzly Bears");
runCode("check blocking", 1, PhaseStep.DECLARE_BLOCKERS, playerB, (info, player, game) -> {
Permanent blocker = game.getBattlefield().getAllActivePermanents()
.stream()
.filter(p -> p.getName().equals("Kitesail Corsair"))
.findFirst()
.get();
Assert.assertFalse("Grizzly Bears must be protected from blocking by Kitesail Corsair",
game.getCombat().getGroups().get(0).canBlock(blocker, game));
});
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
}

View file

@ -9,7 +9,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
public class ConspicuousSnoopTest extends CardTestPlayerBase {
@Test
public void testTopCardLibraryRevealed(){
public void test_TopCardLibraryRevealed() {
// Play with the top card of your library revealed.
addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop");
addCard(Zone.LIBRARY, playerA, "Swamp");
@ -23,43 +23,49 @@ public class ConspicuousSnoopTest extends CardTestPlayerBase {
}
@Test
public void castGoblinSpellsFromLibrary(){
public void test_castGoblinSpellsFromLibrary() {
skipInitShuffling();
removeAllCardsFromLibrary(playerA);
// You may cast Goblin spells from the top of your library.
addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop");
addCard(Zone.LIBRARY, playerA, "Goblin Lackey");
skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
// Whenever Goblin Lackey deals damage to a player, you may put a Goblin permanent card from your hand onto the battlefield.
addCard(Zone.LIBRARY, playerA, "Atog", 1); // second from top
addCard(Zone.LIBRARY, playerA, "Goblin Lackey", 1); // first from top
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 * 2);
// Whenever Goblin Lackey deals damage to a player, you may put a Goblin permanent card from your hand onto the battlefield.
checkPlayableAbility("can play goblin", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Goblin Lackey", true);
checkPlayableAbility("can't play non goblin before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Atog", false);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Goblin Lackey");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkPlayableAbility("can't play non goblin after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Atog", false);
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
setStrictChooseMode(true);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Goblin Lackey", 1);
assertPermanentCount(playerA, "Goblin Lackey", 1);
}
@Test
public void hasActivatedAbilities(){
public void test_hasActivatedAbilities() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
// Play with the top card of your library revealed.
// You may cast Goblin spells from the top of your library.
// As long as the top card of your library is a Goblin card, Conspicuous Snoop has all activated abilities of that card.
addCard(Zone.BATTLEFIELD, playerA, "Conspicuous Snoop");
// {R}: Goblin Balloon Brigade gains flying until end of turn.
addCard(Zone.LIBRARY, playerA, "Goblin Balloon Brigade");
addCard(Zone.LIBRARY, playerA, "Goblin Balloon Brigade");
skipInitShuffling();
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
setStrictChooseMode(true);
execute();
assertAllCommandsUsed();
assertAbilityCount(playerA, "Conspicuous Snoop", ActivatedAbility.class, 3); // (2 X casts + gains flying )
}
}

View file

@ -79,8 +79,6 @@ public class CastSplitCardsWithSpliceTest extends CardTestPlayerBase {
addTarget(playerA, "Bow of Nylea"); // target right
// must used all mana
//showAvaileableMana("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();

View file

@ -1956,7 +1956,7 @@ public class TestPlayer implements Player {
int index = 0;
for (Map.Entry<String, String> entry : rEffects.entrySet()) {
if (entry.getValue().startsWith(choice)) {
choices.remove(choice);
choices.remove(0);
return index;
}
index++;

Some files were not shown because too many files have changed in this diff Show more