mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 13:02:06 -08:00
Merge pull request #2379 from Lymia/master
Add basic support for custom sets.
This commit is contained in:
commit
652672ee45
13 changed files with 394 additions and 77 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<>();
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue