Merge pull request #2379 from Lymia/master

Add basic support for custom sets.
This commit is contained in:
LevelX2 2016-09-26 14:26:35 +02:00 committed by GitHub
commit 652672ee45
13 changed files with 394 additions and 77 deletions

View file

@ -39,6 +39,7 @@ import mage.abilities.Ability;
import mage.abilities.PlayLandAbility;
import mage.abilities.SpellAbility;
import mage.abilities.mana.ManaAbility;
import mage.cards.repository.PluginClassloaderRegistery;
import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
import mage.constants.Rarity;
@ -160,6 +161,11 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
try {
return createCard(Class.forName(name));
} catch (ClassNotFoundException ex) {
try {
return createCard(PluginClassloaderRegistery.forName(name));
} catch (ClassNotFoundException ex2) {
// ignored
}
logger.fatal("Error loading card: " + name, ex);
return null;
}

View file

@ -374,6 +374,10 @@ public abstract class ExpansionSet implements Serializable {
return null;
}
public boolean isCustomSet() {
return setType == SetType.CUSTOM_SET;
}
public void removeSavedCards() {
savedCards.clear();
}

View file

@ -30,11 +30,7 @@ package mage.cards;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.*;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
@ -61,10 +57,12 @@ public class Sets extends HashMap<String, ExpansionSet> {
return fINSTANCE;
}
private Set<String> customSets = new HashSet<>();
private Sets() {
ArrayList<String> packages = new ArrayList<>();
packages.add("mage.sets");
for (Class c : ClassScanner.findClasses(packages, ExpansionSet.class)) {
for (Class c : ClassScanner.findClasses(null, packages, ExpansionSet.class)) {
try {
addSet((ExpansionSet) c.getMethod("getInstance").invoke(null));
} catch (Exception ex) {
@ -72,8 +70,14 @@ public class Sets extends HashMap<String, ExpansionSet> {
}
}
private void addSet(ExpansionSet set) {
public void addSet(ExpansionSet set) {
if(containsKey(set.getCode())) throw new IllegalArgumentException("Set code "+set.getCode()+" already exists.");
this.put(set.getCode(), set);
if(set.isCustomSet()) customSets.add(set.getCode());
}
public static boolean isCustomSet(String setCode) {
return getInstance().customSets.contains(setCode);
}
/**

View file

@ -27,13 +27,10 @@
*/
package mage.cards.decks;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import mage.cards.Card;
import mage.cards.Sets;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
@ -52,6 +49,9 @@ public class Constructed extends DeckValidator {
protected List<String> setCodes = new ArrayList<>();
protected List<Rarity> rarities = new ArrayList<>();
protected boolean allowAllCustomSets = false;
protected Set<String> allowedCustomSetCodes = new HashSet<>();
public Constructed() {
super("Constructed");
}
@ -126,22 +126,21 @@ public class Constructed extends DeckValidator {
}
}
if (!setCodes.isEmpty()) {
for (Card card : deck.getCards()) {
if (!setCodes.contains(card.getExpansionSetCode())) {
if( !legalSets(card) ){
valid = false;
}
}
}
for (Card card : deck.getSideboard()) {
if (!setCodes.contains(card.getExpansionSetCode())) {
if( !legalSets(card) ){
valid = false;
}
for (Card card : deck.getCards()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if( !legalSets(card) ){
valid = false;
}
}
}
for (Card card : deck.getSideboard()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if( !legalSets(card) ){
valid = false;
}
}
}
logger.debug("DECK validate end: " + name + " deckname: " + deck.getName() + " invalids:" + invalid.size());
return valid;
}
@ -167,6 +166,16 @@ public class Constructed extends DeckValidator {
return legal;
}
/**
* Checks if a given set is legal in this format.
* @param code - the set code to check
* @return Whether the set is legal in this format.
*/
protected boolean isSetAllowed(String code) {
if(Sets.isCustomSet(code)) return allowAllCustomSets || allowedCustomSetCodes.contains(code);
else return setCodes.isEmpty() || setCodes.contains(code);
}
/**
* Checks if the given card is legal in any of the given sets
* @param card - the card to check
@ -177,7 +186,7 @@ public class Constructed extends DeckValidator {
boolean legal = false;
List<CardInfo> cardInfos = CardRepository.instance.findCards(card.getName());
for (CardInfo cardInfo : cardInfos) {
if (setCodes.contains(cardInfo.getSetCode())) {
if (isSetAllowed(cardInfo.getSetCode())) {
legal = true;
break;
}

View file

@ -29,7 +29,10 @@
package mage.cards.repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.ExpansionSet;
@ -55,23 +58,28 @@ public class CardScanner {
scanned = true;
List<CardInfo> cardsToAdd = new ArrayList<>();
List<String> packages = new ArrayList<>();
Map<ClassLoader, List<String>> packageMap = new HashMap<>();
for (ExpansionSet set : Sets.getInstance().values()) {
ClassLoader cl = set.getClass().getClassLoader();
if(!packageMap.containsKey(cl)) packageMap.put(cl, new ArrayList<String>());
List<String> packages = packageMap.get(cl);
packages.add(set.getPackageName());
ExpansionRepository.instance.add(new ExpansionInfo(set));
}
ExpansionRepository.instance.setContentVersion(ExpansionRepository.instance.getContentVersionConstant());
for (Class c : ClassScanner.findClasses(packages, CardImpl.class)) {
if (!CardRepository.instance.cardExists(c.getCanonicalName())) {
Card card = CardImpl.createCard(c);
if (card != null) {
cardsToAdd.add(new CardInfo(card));
if (card instanceof SplitCard) {
SplitCard splitCard = (SplitCard) card;
cardsToAdd.add(new CardInfo(splitCard.getLeftHalfCard()));
cardsToAdd.add(new CardInfo(splitCard.getRightHalfCard()));
for (ClassLoader cl : packageMap.keySet()) {
for (Class c : ClassScanner.findClasses(cl, packageMap.get(cl), CardImpl.class)) {
if (!CardRepository.instance.cardExists(c.getCanonicalName())) {
Card card = CardImpl.createCard(c);
if (card != null) {
cardsToAdd.add(new CardInfo(card));
if (card instanceof SplitCard) {
SplitCard splitCard = (SplitCard) card;
cardsToAdd.add(new CardInfo(splitCard.getLeftHalfCard()));
cardsToAdd.add(new CardInfo(splitCard.getRightHalfCard()));
}
}
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2016 Lymia <lymia@lymiahugs.com>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.repository;
import java.util.ArrayList;
import java.util.List;
/**
* Stores a list of classloaders for plugins that define custom sets.
*
* @author Lymia
*/
public class PluginClassloaderRegistery {
static List<ClassLoader> pluginClassloaders = new ArrayList<>();
public static void registerPluginClassloader(ClassLoader cl) {
pluginClassloaders.add(cl);
}
public static Class<?> forName(String className) throws ClassNotFoundException {
for(ClassLoader cl : pluginClassloaders) try {
return Class.forName(className, true, cl);
} catch (ClassNotFoundException c) {
// ignored
}
throw new ClassNotFoundException();
}
}

View file

@ -11,7 +11,8 @@ public enum SetType {
SUPPLEMENTAL("Supplemental"),
SUPPLEMENTAL_STANDARD_LEGAL("Standard Legal Supplemental"),
PROMOTIONAL("Promotional"),
JOKESET("Joke Set");
JOKESET("Joke Set"),
CUSTOM_SET("Unofficial Set");
private final String text;

View file

@ -47,6 +47,7 @@ public class GameEvent implements Serializable {
protected String data;
protected Zone zone;
protected ArrayList<UUID> appliedEffects = new ArrayList<>();
protected UUID customEventType = null;
public enum EventType {
@ -269,15 +270,15 @@ public class GameEvent implements Serializable {
NUMBER_OF_TRIGGERS,
//combat events
COMBAT_DAMAGE_APPLIED,
SELECTED_ATTACKER, SELECTED_BLOCKER;
SELECTED_ATTACKER, SELECTED_BLOCKER,
//custom events
CUSTOM_EVENT;
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId) {
this(type, targetId, sourceId, playerId, 0, false);
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
private GameEvent(EventType type, UUID customEventType,
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this.type = type;
this.customEventType = customEventType;
this.targetId = targetId;
this.sourceId = sourceId;
this.amount = amount;
@ -285,6 +286,22 @@ public class GameEvent implements Serializable {
this.flag = flag;
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId) {
this(type, null, targetId, sourceId, playerId, 0, false);
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this(type, null, targetId, sourceId, playerId, amount, flag);
}
public GameEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId) {
this(EventType.CUSTOM_EVENT, customEventType, targetId, sourceId, playerId, 0, false);
}
public GameEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this(EventType.CUSTOM_EVENT, customEventType, targetId, sourceId, playerId, amount, flag);
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount) {
return new GameEvent(type, targetId, sourceId, playerId, amount, false);
}
@ -304,10 +321,33 @@ public class GameEvent implements Serializable {
return event;
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId, int amount) {
return new GameEvent(customEventType, targetId, sourceId, playerId, amount, false);
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId) {
return new GameEvent(customEventType, targetId, sourceId, playerId);
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID playerId) {
return new GameEvent(customEventType, targetId, null, playerId);
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID playerId, String data, int amount) {
GameEvent event = getEvent(customEventType, targetId, playerId);
event.setAmount(amount);
event.setData(data);
return event;
}
public EventType getType() {
return type;
}
public UUID getCustomEventType() {
return customEventType;
}
public UUID getTargetId() {
return targetId;
}
@ -374,6 +414,10 @@ public class GameEvent implements Serializable {
return appliedEffects;
}
public boolean isCustomEvent(UUID customEventType) {
return type == EventType.CUSTOM_EVENT && this.customEventType.equals(customEventType);
}
public void setAppliedEffects(ArrayList<UUID> appliedEffects) {
if (this.appliedEffects == null) {
this.appliedEffects = new ArrayList<>();

View file

@ -46,10 +46,21 @@ import java.util.jar.JarInputStream;
*/
public class ClassScanner {
public static List<Class> findClasses(List<String> packages, Class<?> type) {
private static void checkClassForInclusion(List<Class> cards, Class type, String name, ClassLoader cl) {
try {
Class clazz = Class.forName(name, true, cl);
if (clazz.getEnclosingClass() == null && type.isAssignableFrom(clazz)) {
cards.add(clazz);
}
} catch (ClassNotFoundException ex) {
// ignored
}
}
public static List<Class> findClasses(ClassLoader classLoader, List<String> packages, Class<?> type) {
List<Class> cards = new ArrayList<>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
assert classLoader != null;
HashMap<String, String> dirs = new HashMap<>();
@ -71,43 +82,35 @@ public class ClassScanner {
}
for (String filePath : dirs.keySet()) {
cards.addAll(findClasses(new File(filePath), dirs.get(filePath), type));
cards.addAll(findClasses(classLoader, new File(filePath), dirs.get(filePath), type));
}
for (String filePath : jars) {
File file = new File(URLDecoder.decode(filePath, "UTF-8"));
cards.addAll(findClassesInJar(file, packages, type));
cards.addAll(findClassesInJar(classLoader, file, packages, type));
}
} catch (IOException ex) {
}
return cards;
}
private static List<Class> findClasses(File directory, String packageName, Class<?> type) {
private static List<Class> findClasses(ClassLoader classLoader, File directory, String packageName, Class<?> type) {
List<Class> cards = new ArrayList<>();
if (!directory.exists()) {
return cards;
}
if (!directory.exists()) return cards;
for (File file : directory.listFiles()) {
if (file.getName().endsWith(".class")) {
try {
Class<?> clazz = Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6));
if (type.isAssignableFrom(clazz)) {
cards.add(clazz);
}
} catch (ClassNotFoundException ex) {
}
String name = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
checkClassForInclusion(cards, type, name, classLoader);
}
}
return cards;
}
private static List<Class> findClassesInJar(File file, List<String> packages, Class<?> type) {
private static List<Class> findClassesInJar(ClassLoader classLoader, File file, List<String> packages, Class<?> type) {
List<Class> cards = new ArrayList<>();
if (!file.exists()) {
return cards;
}
if (!file.exists()) return cards;
JarInputStream jarFile = null;
try {
jarFile = new JarInputStream(new FileInputStream(file));
@ -120,22 +123,13 @@ public class ClassScanner {
String className = jarEntry.getName().replace(".class", "").replace('/', '.');
int packageNameEnd = className.lastIndexOf('.');
String packageName = packageNameEnd != -1 ? className.substring(0, packageNameEnd) : "";
if (packages.contains(packageName)) {
Class<?> clazz;
try {
clazz = Class.forName(className);
if (type.isAssignableFrom(clazz)) {
cards.add(clazz);
}
} catch (ClassNotFoundException ex) {
}
}
if (packages.contains(packageName)) checkClassForInclusion(cards, type, className, classLoader);
}
}
} catch (IOException ex) {
} finally {
try {
jarFile.close();
if(jarFile != null) jarFile.close();
} catch (IOException ex) {
}
}