From 3d45a2495914a7ab8bae9a84cd87489b4ab22b8a Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 31 May 2025 20:15:31 +0400 Subject: [PATCH] Draft Cubes rework (better cube from deck, dynamic data, better errors processing, actual MTGO Vintage Cube) (#13705) - GUI, table: added dynamic data support for Cube Types (no more depends on server's config names, part of #12050); - server: replace multiple MTGO Vintage Cubes by single cube, updated to April 2025 (part of #12050); - server: fixed table freeze on starting error (related to #11285); - GUI, table: added better support of Cube From Deck (client/server side errors, additional info about loaded cards, etc); --- .../client/dialog/NewTournamentDialog.java | 19 +- .../java/mage/client/draft/DraftPanel.java | 8 +- .../src/main/java/mage/view/DraftView.java | 43 +- .../mage/tournament/cubes/CubeFromDeck.java | 18 +- .../tournament/cubes/MTGOVintageCube.java | 559 ++++++++++++++++++ Mage.Server/config/config.xml | 4 + Mage.Server/release/config/config.xml | 4 + .../main/java/mage/server/MageServerImpl.java | 2 +- .../java/mage/server/draft/CubeFactory.java | 31 +- .../server/tournament/TournamentFactory.java | 35 +- Mage.Server/src/test/data/config_error.xml | 4 + .../java/mage/verify/VerifyCardDataTest.java | 24 +- .../main/java/mage/game/draft/DraftCube.java | 65 +- 13 files changed, 755 insertions(+), 61 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MTGOVintageCube.java diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index a22adb61fb9..b83408d981a 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -43,6 +43,8 @@ public class NewTournamentDialog extends MageDialog { // it's ok to have 4 players at the screen, 6 is fine for big screens too private static final int MAX_WORKABLE_PLAYERS_PER_GAME = 6; + private static final String CUBE_FROM_DECK_NAME = "Cube From Deck"; + // temp settings on loading players list private final List prefPlayerTypes = new ArrayList<>(); private final List prefPlayerSkills = new ArrayList<>(); @@ -712,6 +714,21 @@ public class NewTournamentDialog extends MageDialog { } } + // cube from deck uses weird choose logic from combobox select, so players can forget or cancel it + if (tournamentType.isDraft() + && tOptions.getLimitedOptions().getDraftCubeName() != null + && tOptions.getLimitedOptions().getDraftCubeName().contains(CUBE_FROM_DECK_NAME)) { + if (tOptions.getLimitedOptions().getCubeFromDeck() == null || tOptions.getLimitedOptions().getCubeFromDeck().getCards().isEmpty()) { + JOptionPane.showMessageDialog( + MageFrame.getDesktop(), + "Found empty cube. You must choose Cube From Deck again and select existing deck file.", + "Warning", + JOptionPane.WARNING_MESSAGE + ); + return; + } + } + // save last settings onSaveSettings(0, tOptions); @@ -826,7 +843,7 @@ public class NewTournamentDialog extends MageDialog { private void cbDraftCubeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDraftCubeActionPerformed cubeFromDeckFilename = ""; - if (cbDraftCube.getSelectedItem().toString().equals("Cube From Deck")) { + if (cbDraftCube.getSelectedItem().toString().startsWith(CUBE_FROM_DECK_NAME)) { cubeFromDeckFilename = playerLoadDeck(); } }//GEN-LAST:event_cbDraftCubeActionPerformed diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index 1118801bf5f..41b4904fadb 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -191,15 +191,15 @@ } public void updateDraft(DraftView draftView) { - if (draftView.getSets().size() != 3) { + if (draftView.getSetNames().size() != 3) { // Random draft - TODO: can we access the type of draft here? this.editPack1.setText("Random Boosters"); this.editPack2.setText("Random Boosters"); this.editPack3.setText("Random Boosters"); } else { - this.editPack1.setText(String.format("%s - %s", draftView.getSetCodes().get(0), draftView.getSets().get(0))); - this.editPack2.setText(String.format("%s - %s", draftView.getSetCodes().get(1), draftView.getSets().get(1))); - this.editPack3.setText(String.format("%s - %s", draftView.getSetCodes().get(2), draftView.getSets().get(2))); + this.editPack1.setText(draftView.getBoosterInfo(0)); + this.editPack2.setText(draftView.getBoosterInfo(1)); + this.editPack3.setText(draftView.getBoosterInfo(2)); } // scroll too long text to the start diff --git a/Mage.Common/src/main/java/mage/view/DraftView.java b/Mage.Common/src/main/java/mage/view/DraftView.java index 9b0100c74d5..8e9c84868c6 100644 --- a/Mage.Common/src/main/java/mage/view/DraftView.java +++ b/Mage.Common/src/main/java/mage/view/DraftView.java @@ -1,50 +1,63 @@ - - package mage.view; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; import mage.cards.ExpansionSet; import mage.game.draft.Draft; import mage.game.draft.DraftCube; import mage.game.draft.DraftPlayer; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + /** - * - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class DraftView implements Serializable { private static final long serialVersionUID = 1L; - private final List sets = new ArrayList<>(); + private final List setNames = new ArrayList<>(); private final List setCodes = new ArrayList<>(); private final int boosterNum; // starts with 1 private final int cardNum; // starts with 1 + private final boolean isCube; + private final List players = new ArrayList<>(); public DraftView(Draft draft) { - if (draft.getDraftCube() != null) { + this.isCube = draft.getDraftCube() != null; + if (this.isCube) { for (int i = 0; i < draft.getNumberBoosters(); i++) { DraftCube cube = draft.getDraftCube(); - sets.add(cube.getName()); + setNames.add(cube.getName()); setCodes.add(cube.getCode()); } } else { - for (ExpansionSet set: draft.getSets()) { - sets.add(set.getName()); + for (ExpansionSet set : draft.getSets()) { + setNames.add(set.getName()); setCodes.add(set.getCode()); } } this.boosterNum = draft.getBoosterNum(); this.cardNum = draft.getCardNum(); - for(DraftPlayer draftPlayer :draft.getPlayers()) { + for (DraftPlayer draftPlayer : draft.getPlayers()) { players.add(draftPlayer.getPlayer().getName()); } } - public List getSets() { - return sets; + public String getBoosterInfo(int index) { + if (index >= this.setCodes.size() || this.setCodes.size() != this.setNames.size()) { + return "error"; + } + + if (this.isCube) { + return this.setNames.get(index); + } else { + return String.join(" - ", this.setCodes.get(index), this.setNames.get(index)); + } + } + + public List getSetNames() { + return setNames; } public List getSetCodes() { diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java index 796ad4557de..4d307ace43f 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java @@ -6,12 +6,15 @@ import mage.cards.decks.DeckCardLists; import mage.game.draft.DraftCube; /** - * @author spjspj + * @author spjspj, JayDi85 */ public class CubeFromDeck extends DraftCube { + /** + * Calls on table create, can use any names + */ public CubeFromDeck(Deck cubeFromDeck) { - super("Cube From Deck"); + this(); if (cubeFromDeck == null) { return; @@ -21,5 +24,16 @@ public class CubeFromDeck extends DraftCube { for (DeckCardInfo card : cards.getCards()) { cubeCards.add(new CardIdentity(card.getCardName(), card.getSetCode(), card.getCardNumber())); } + + // add useful info about cubes, but don't print user defined data like deck name due security reasons + this.setUpdateInfo(String.format("%d cards", cubeCards.size())); + } + + /** + * Calls on server's startng, must use default name - that's name will see all users after connection + */ + public CubeFromDeck() { + // don't change default name - new tourney dialog use it to choose a cube's deck + super("Cube From Deck", "your custom cube", 0, 0, 0); } } diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MTGOVintageCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MTGOVintageCube.java new file mode 100644 index 00000000000..f649050409f --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/MTGOVintageCube.java @@ -0,0 +1,559 @@ +package mage.tournament.cubes; + +import mage.game.draft.DraftCube; + +/** + * MTGO Vintage Cube, latest version + *

+ * data source + * + * @author JayDi85 + */ +public class MTGOVintageCube extends DraftCube { + + public MTGOVintageCube() { + super("MTGO Vintage Cube", "", 2025, 4, 23); + + cubeCards.add(new CardIdentity("\"Name Sticker\" Goblin", "")); + cubeCards.add(new CardIdentity("Abrade", "")); + cubeCards.add(new CardIdentity("Abrupt Decay", "")); + cubeCards.add(new CardIdentity("Adanto Vanguard", "")); + cubeCards.add(new CardIdentity("Adeline, Resplendent Cathar", "")); + cubeCards.add(new CardIdentity("Aether Spellbomb", "")); + cubeCards.add(new CardIdentity("Aetherflux Reservoir", "")); + cubeCards.add(new CardIdentity("Agatha's Soul Cauldron", "")); + cubeCards.add(new CardIdentity("Akal Pakal, First Among Equals", "")); + cubeCards.add(new CardIdentity("Ancestral Recall", "")); + cubeCards.add(new CardIdentity("Ancient Tomb", "")); + cubeCards.add(new CardIdentity("Animate Dead", "")); + cubeCards.add(new CardIdentity("Arbor Elf", "")); + cubeCards.add(new CardIdentity("Arc Trail", "")); + cubeCards.add(new CardIdentity("Archon of Cruelty", "")); + cubeCards.add(new CardIdentity("Arid Mesa", "")); + cubeCards.add(new CardIdentity("Arwen, Mortal Queen", "")); + cubeCards.add(new CardIdentity("Ashen Rider", "")); + cubeCards.add(new CardIdentity("Assimilation Aegis", "")); + cubeCards.add(new CardIdentity("Atraxa, Grand Unifier", "")); + cubeCards.add(new CardIdentity("Augur of Autumn", "")); + cubeCards.add(new CardIdentity("Avacyn's Pilgrim", "")); + cubeCards.add(new CardIdentity("Aven Interrupter", "")); + cubeCards.add(new CardIdentity("Badlands", "")); + cubeCards.add(new CardIdentity("Balance", "")); + cubeCards.add(new CardIdentity("Baleful Mastery", "")); + cubeCards.add(new CardIdentity("Baleful Strix", "")); + cubeCards.add(new CardIdentity("Basalt Monolith", "")); + cubeCards.add(new CardIdentity("Batterskull", "")); + cubeCards.add(new CardIdentity("Bayou", "")); + cubeCards.add(new CardIdentity("Benevolent Bodyguard", "")); + cubeCards.add(new CardIdentity("Beseech the Mirror", "")); + cubeCards.add(new CardIdentity("Birds of Paradise", "")); + cubeCards.add(new CardIdentity("Birgi, God of Storytelling", "")); + cubeCards.add(new CardIdentity("Bitter Triumph", "")); + cubeCards.add(new CardIdentity("Black Lotus", "")); + cubeCards.add(new CardIdentity("Blackcleave Cliffs", "")); + cubeCards.add(new CardIdentity("Blade Splicer", "")); + cubeCards.add(new CardIdentity("Blightsteel Colossus", "")); + cubeCards.add(new CardIdentity("Blood Crypt", "")); + cubeCards.add(new CardIdentity("Bloodstained Mire", "")); + cubeCards.add(new CardIdentity("Bloodtithe Harvester", "")); + cubeCards.add(new CardIdentity("Blooming Marsh", "")); + cubeCards.add(new CardIdentity("Bolas's Citadel", "")); + cubeCards.add(new CardIdentity("Bone Shards", "")); + cubeCards.add(new CardIdentity("Bonecrusher Giant", "")); + cubeCards.add(new CardIdentity("Bonehoard Dracosaur", "")); + cubeCards.add(new CardIdentity("Boseiju, Who Endures", "")); + cubeCards.add(new CardIdentity("Botanical Sanctum", "")); + cubeCards.add(new CardIdentity("Brain Freeze", "")); + cubeCards.add(new CardIdentity("Brainstorm", "")); + cubeCards.add(new CardIdentity("Brazen Borrower", "")); + cubeCards.add(new CardIdentity("Breeding Pool", "")); + cubeCards.add(new CardIdentity("Bring to Light", "")); + cubeCards.add(new CardIdentity("Bristly Bill, Spine Sower", "")); + cubeCards.add(new CardIdentity("Broadside Bombardiers", "")); + cubeCards.add(new CardIdentity("Bubbling Muck", "")); + cubeCards.add(new CardIdentity("Burst Lightning", "")); + cubeCards.add(new CardIdentity("Cabal Ritual", "")); + cubeCards.add(new CardIdentity("Candelabra of Tawnos", "")); + cubeCards.add(new CardIdentity("Carnage Interpreter", "")); + cubeCards.add(new CardIdentity("Cathar Commando", "")); + cubeCards.add(new CardIdentity("Caustic Bronco", "")); + cubeCards.add(new CardIdentity("Celestial Colonnade", "")); + cubeCards.add(new CardIdentity("Chain Lightning", "")); + cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", "")); + cubeCards.add(new CardIdentity("Channel", "")); + cubeCards.add(new CardIdentity("Chart a Course", "")); + cubeCards.add(new CardIdentity("Chromatic Star", "")); + cubeCards.add(new CardIdentity("Chrome Host Seedshark", "")); + cubeCards.add(new CardIdentity("Chrome Mox", "")); + cubeCards.add(new CardIdentity("City of Traitors", "")); + cubeCards.add(new CardIdentity("Coalition Relic", "")); + cubeCards.add(new CardIdentity("Collective Brutality", "")); + cubeCards.add(new CardIdentity("Collector's Cage", "")); + cubeCards.add(new CardIdentity("Commercial District", "")); + cubeCards.add(new CardIdentity("Concealed Courtyard", "")); + cubeCards.add(new CardIdentity("Concealing Curtains", "")); + cubeCards.add(new CardIdentity("Consider", "")); + cubeCards.add(new CardIdentity("Containment Priest", "")); + cubeCards.add(new CardIdentity("Copperline Gorge", "")); + cubeCards.add(new CardIdentity("Corpse Dance", "")); + cubeCards.add(new CardIdentity("Council's Judgment", "")); + cubeCards.add(new CardIdentity("Counterspell", "")); + cubeCards.add(new CardIdentity("Courser of Kruphix", "")); + cubeCards.add(new CardIdentity("Coveted Jewel", "")); + cubeCards.add(new CardIdentity("Craterhoof Behemoth", "")); + cubeCards.add(new CardIdentity("Creeping Tar Pit", "")); + cubeCards.add(new CardIdentity("Crucible of Worlds", "")); + cubeCards.add(new CardIdentity("Cruel Ultimatum", "")); + cubeCards.add(new CardIdentity("Cryptic Coat", "")); + cubeCards.add(new CardIdentity("Cryptic Command", "")); + cubeCards.add(new CardIdentity("Currency Converter", "")); + cubeCards.add(new CardIdentity("Cut Down", "")); + cubeCards.add(new CardIdentity("Dack Fayden", "")); + cubeCards.add(new CardIdentity("Damn", "")); + cubeCards.add(new CardIdentity("Dark Confidant", "")); + cubeCards.add(new CardIdentity("Dark Ritual", "")); + cubeCards.add(new CardIdentity("Darkslick Shores", "")); + cubeCards.add(new CardIdentity("Dauthi Voidwalker", "")); + cubeCards.add(new CardIdentity("Daze", "")); + cubeCards.add(new CardIdentity("Death-Greeter's Champion", "")); + cubeCards.add(new CardIdentity("Deep-Cavern Bat", "")); + cubeCards.add(new CardIdentity("Delayed Blast Fireball", "")); + cubeCards.add(new CardIdentity("Delighted Halfling", "")); + cubeCards.add(new CardIdentity("Demonic Tutor", "")); + cubeCards.add(new CardIdentity("Dig Through Time", "")); + cubeCards.add(new CardIdentity("Dismember", "")); + cubeCards.add(new CardIdentity("Displacer Kitten", "")); + cubeCards.add(new CardIdentity("Dragon's Rage Channeler", "")); + cubeCards.add(new CardIdentity("Dreadhorde Arcanist", "")); + cubeCards.add(new CardIdentity("Dream Halls", "")); + cubeCards.add(new CardIdentity("Duelist of the Mind", "")); + cubeCards.add(new CardIdentity("Duress", "")); + cubeCards.add(new CardIdentity("Echo of Eons", "")); + cubeCards.add(new CardIdentity("Elegant Parlor", "")); + cubeCards.add(new CardIdentity("Elemental Eruption", "")); + cubeCards.add(new CardIdentity("Elite Spellbinder", "")); + cubeCards.add(new CardIdentity("Elspeth, Knight-Errant", "")); + cubeCards.add(new CardIdentity("Elvish Mystic", "")); + cubeCards.add(new CardIdentity("Embereth Shieldbreaker", "")); + cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", "")); + cubeCards.add(new CardIdentity("Endurance", "")); + cubeCards.add(new CardIdentity("Enlightened Tutor", "")); + cubeCards.add(new CardIdentity("Entomb", "")); + cubeCards.add(new CardIdentity("Ephemerate", "")); + cubeCards.add(new CardIdentity("Escape to the Wilds", "")); + cubeCards.add(new CardIdentity("Esika's Chariot", "")); + cubeCards.add(new CardIdentity("Esper Sentinel", "")); + cubeCards.add(new CardIdentity("Etali, Primal Conqueror", "")); + cubeCards.add(new CardIdentity("Eternal Witness", "")); + cubeCards.add(new CardIdentity("Eureka", "")); + cubeCards.add(new CardIdentity("Everflowing Chalice", "")); + cubeCards.add(new CardIdentity("Evolved Sleeper", "")); + cubeCards.add(new CardIdentity("Exhume", "")); + cubeCards.add(new CardIdentity("Exploration", "")); + cubeCards.add(new CardIdentity("Expressive Iteration", "")); + cubeCards.add(new CardIdentity("Fable of the Mirror-Breaker", "")); + cubeCards.add(new CardIdentity("Faerie Mastermind", "")); + cubeCards.add(new CardIdentity("Faithless Looting", "")); + cubeCards.add(new CardIdentity("Fallen Shinobi", "")); + cubeCards.add(new CardIdentity("Fastbond", "")); + cubeCards.add(new CardIdentity("Fatal Push", "")); + cubeCards.add(new CardIdentity("Fiery Confluence", "")); + cubeCards.add(new CardIdentity("Fiery Islet", "")); + cubeCards.add(new CardIdentity("Figure of Destiny", "")); + cubeCards.add(new CardIdentity("Fire Covenant", "")); + cubeCards.add(new CardIdentity("Fireblast", "")); + cubeCards.add(new CardIdentity("Firebolt", "")); + cubeCards.add(new CardIdentity("Flame Slash", "")); + cubeCards.add(new CardIdentity("Flametongue Kavu", "")); + cubeCards.add(new CardIdentity("Flash", "")); + cubeCards.add(new CardIdentity("Flickerwisp", "")); + cubeCards.add(new CardIdentity("Flooded Strand", "")); + cubeCards.add(new CardIdentity("Force of Negation", "")); + cubeCards.add(new CardIdentity("Force of Will", "")); + cubeCards.add(new CardIdentity("Forensic Gadgeteer", "")); + cubeCards.add(new CardIdentity("Forth Eorlingas!", "")); + cubeCards.add(new CardIdentity("Fractured Identity", "")); + cubeCards.add(new CardIdentity("Frantic Search", "")); + cubeCards.add(new CardIdentity("Fury", "")); + cubeCards.add(new CardIdentity("Gaea's Cradle", "")); + cubeCards.add(new CardIdentity("Garruk Wildspeaker", "")); + cubeCards.add(new CardIdentity("Generous Ent", "")); + cubeCards.add(new CardIdentity("Generous Plunderer", "")); + cubeCards.add(new CardIdentity("Gitaxian Probe", "")); + cubeCards.add(new CardIdentity("Giver of Runes", "")); + cubeCards.add(new CardIdentity("Gix, Yawgmoth Praetor", "")); + cubeCards.add(new CardIdentity("Glimmer Lens", "")); + cubeCards.add(new CardIdentity("Goblin Rabblemaster", "")); + cubeCards.add(new CardIdentity("Godless Shrine", "")); + cubeCards.add(new CardIdentity("Goldspan Dragon", "")); + cubeCards.add(new CardIdentity("Goldvein Hydra", "")); + cubeCards.add(new CardIdentity("Goryo's Vengeance", "")); + cubeCards.add(new CardIdentity("Grave Titan", "")); + cubeCards.add(new CardIdentity("Graveyard Trespasser", "")); + cubeCards.add(new CardIdentity("Green Sun's Zenith", "")); + cubeCards.add(new CardIdentity("Grief", "")); + cubeCards.add(new CardIdentity("Grim Lavamancer", "")); + cubeCards.add(new CardIdentity("Grim Monolith", "")); + cubeCards.add(new CardIdentity("Griselbrand", "")); + cubeCards.add(new CardIdentity("Grist, the Hunger Tide", "")); + cubeCards.add(new CardIdentity("Gruff Triplets", "")); + cubeCards.add(new CardIdentity("Guardian Scalelord", "")); + cubeCards.add(new CardIdentity("Gut, True Soul Zealot", "")); + cubeCards.add(new CardIdentity("Hallowed Fountain", "")); + cubeCards.add(new CardIdentity("Hard Evidence", "")); + cubeCards.add(new CardIdentity("Harvester of Misery", "")); + cubeCards.add(new CardIdentity("Haywire Mite", "")); + cubeCards.add(new CardIdentity("Headliner Scarlett", "")); + cubeCards.add(new CardIdentity("Hedge Maze", "")); + cubeCards.add(new CardIdentity("Helm of Awakening", "")); + cubeCards.add(new CardIdentity("Hero of Bladehold", "")); + cubeCards.add(new CardIdentity("Hexdrinker", "")); + cubeCards.add(new CardIdentity("High Tide", "")); + cubeCards.add(new CardIdentity("Highway Robbery", "")); + cubeCards.add(new CardIdentity("Horizon Canopy", "")); + cubeCards.add(new CardIdentity("Hostile Investigator", "")); + cubeCards.add(new CardIdentity("Huatli, Poet of Unity", "")); + cubeCards.add(new CardIdentity("Hullbreacher", "")); + cubeCards.add(new CardIdentity("Hymn to Tourach", "")); + cubeCards.add(new CardIdentity("Ignoble Hierarch", "")); + cubeCards.add(new CardIdentity("Imperial Seal", "")); + cubeCards.add(new CardIdentity("Indatha Triome", "")); + cubeCards.add(new CardIdentity("Infernal Grasp", "")); + cubeCards.add(new CardIdentity("Inquisition of Kozilek", "")); + cubeCards.add(new CardIdentity("Inspiring Vantage", "")); + cubeCards.add(new CardIdentity("Inti, Seneschal of the Sun", "")); + cubeCards.add(new CardIdentity("Intrepid Adversary", "")); + cubeCards.add(new CardIdentity("Invigorate", "")); + cubeCards.add(new CardIdentity("Jace Reawakened", "")); + cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", "")); + cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", "")); + cubeCards.add(new CardIdentity("Jetmir's Garden", "")); + cubeCards.add(new CardIdentity("Kaervek, the Punisher", "")); + cubeCards.add(new CardIdentity("Kaito Shizuki", "")); + cubeCards.add(new CardIdentity("Kaldra Compleat", "")); + cubeCards.add(new CardIdentity("Kappa Cannoneer", "")); + cubeCards.add(new CardIdentity("Karakas", "")); + cubeCards.add(new CardIdentity("Karn, Scion of Urza", "")); + cubeCards.add(new CardIdentity("Ketria Triome", "")); + cubeCards.add(new CardIdentity("Kinnan, Bonder Prodigy", "")); + cubeCards.add(new CardIdentity("Kitesail Larcenist", "")); + cubeCards.add(new CardIdentity("Kolaghan's Command", "")); + cubeCards.add(new CardIdentity("Kozilek, Butcher of Truth", "")); + cubeCards.add(new CardIdentity("Kutzil, Malamet Exemplar", "")); + cubeCards.add(new CardIdentity("Laelia, the Blade Reforged", "")); + cubeCards.add(new CardIdentity("Lava Dart", "")); + cubeCards.add(new CardIdentity("Lazav, Wearer of Faces", "")); + cubeCards.add(new CardIdentity("Ledger Shredder", "")); + cubeCards.add(new CardIdentity("Legion Extruder", "")); + cubeCards.add(new CardIdentity("Legolas's Quick Reflexes", "")); + cubeCards.add(new CardIdentity("Leonin Relic-Warder", "")); + cubeCards.add(new CardIdentity("Leovold, Emissary of Trest", "")); + cubeCards.add(new CardIdentity("Leyline Binding", "")); + cubeCards.add(new CardIdentity("Library of Alexandria", "")); + cubeCards.add(new CardIdentity("Life // Death", "")); + cubeCards.add(new CardIdentity("Lightning Bolt", "")); + cubeCards.add(new CardIdentity("Liliana of the Veil", "")); + cubeCards.add(new CardIdentity("Lim-Dul's Vault", "")); + cubeCards.add(new CardIdentity("Lingering Souls", "")); + cubeCards.add(new CardIdentity("Lion Sash", "")); + cubeCards.add(new CardIdentity("Lion's Eye Diamond", "")); + cubeCards.add(new CardIdentity("Llanowar Elves", "")); + cubeCards.add(new CardIdentity("Long Goodbye", "")); + cubeCards.add(new CardIdentity("Loran of the Third Path", "")); + cubeCards.add(new CardIdentity("Lotus Cobra", "")); + cubeCards.add(new CardIdentity("Lotus Field", "")); + cubeCards.add(new CardIdentity("Lotus Petal", "")); + cubeCards.add(new CardIdentity("Luminarch Aspirant", "")); + cubeCards.add(new CardIdentity("Lurrus of the Dream-Den", "")); + cubeCards.add(new CardIdentity("Lush Portico", "")); + cubeCards.add(new CardIdentity("Lorien Revealed", "")); + cubeCards.add(new CardIdentity("Magda, Brazen Outlaw", "")); + cubeCards.add(new CardIdentity("Magda, the Hoardmaster", "")); + cubeCards.add(new CardIdentity("Magma Opus", "")); + cubeCards.add(new CardIdentity("Malcolm, Alluring Scoundrel", "")); + cubeCards.add(new CardIdentity("Mana Confluence", "")); + cubeCards.add(new CardIdentity("Mana Crypt", "")); + cubeCards.add(new CardIdentity("Mana Drain", "")); + cubeCards.add(new CardIdentity("Mana Leak", "")); + cubeCards.add(new CardIdentity("Mana Tithe", "")); + cubeCards.add(new CardIdentity("Mana Vault", "")); + cubeCards.add(new CardIdentity("Manamorphose", "")); + cubeCards.add(new CardIdentity("Manifold Key", "")); + cubeCards.add(new CardIdentity("Marsh Flats", "")); + cubeCards.add(new CardIdentity("Memory Jar", "")); + cubeCards.add(new CardIdentity("Memory Lapse", "")); + cubeCards.add(new CardIdentity("Meticulous Archive", "")); + cubeCards.add(new CardIdentity("Mind Stone", "")); + cubeCards.add(new CardIdentity("Mind Twist", "")); + cubeCards.add(new CardIdentity("Mind's Desire", "")); + cubeCards.add(new CardIdentity("Mine Collapse", "")); + cubeCards.add(new CardIdentity("Minsc & Boo, Timeless Heroes", "")); + cubeCards.add(new CardIdentity("Miscalculation", "")); + cubeCards.add(new CardIdentity("Mishra's Bauble", "")); + cubeCards.add(new CardIdentity("Mishra's Research Desk", "")); + cubeCards.add(new CardIdentity("Mishra's Workshop", "")); + cubeCards.add(new CardIdentity("Misty Rainforest", "")); + cubeCards.add(new CardIdentity("Mizzix's Mastery", "")); + cubeCards.add(new CardIdentity("Monastery Mentor", "")); + cubeCards.add(new CardIdentity("Monastery Swiftspear", "")); + cubeCards.add(new CardIdentity("Mother of Runes", "")); + cubeCards.add(new CardIdentity("Mox Diamond", "")); + cubeCards.add(new CardIdentity("Mox Emerald", "")); + cubeCards.add(new CardIdentity("Mox Jet", "")); + cubeCards.add(new CardIdentity("Mox Opal", "")); + cubeCards.add(new CardIdentity("Mox Pearl", "")); + cubeCards.add(new CardIdentity("Mox Ruby", "")); + cubeCards.add(new CardIdentity("Mox Sapphire", "")); + cubeCards.add(new CardIdentity("Mutagenic Growth", "")); + cubeCards.add(new CardIdentity("Myr Battlesphere", "")); + cubeCards.add(new CardIdentity("Mystic Confluence", "")); + cubeCards.add(new CardIdentity("Mystic Forge", "")); + cubeCards.add(new CardIdentity("Mystical Tutor", "")); + cubeCards.add(new CardIdentity("Narset, Parter of Veils", "")); + cubeCards.add(new CardIdentity("Natural Order", "")); + cubeCards.add(new CardIdentity("Nature's Claim", "")); + cubeCards.add(new CardIdentity("Necromancy", "")); + cubeCards.add(new CardIdentity("Nettlecyst", "")); + cubeCards.add(new CardIdentity("Nexus of Becoming", "")); + cubeCards.add(new CardIdentity("Night's Whisper", "")); + cubeCards.add(new CardIdentity("Nishoba Brawler", "")); + cubeCards.add(new CardIdentity("Nissa, Ascended Animist", "")); + cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", "")); + cubeCards.add(new CardIdentity("Noble Hierarch", "")); + cubeCards.add(new CardIdentity("Nurturing Peatland", "")); + cubeCards.add(new CardIdentity("Oath of Druids", "")); + cubeCards.add(new CardIdentity("Oko, the Ringleader", "")); + cubeCards.add(new CardIdentity("Oko, Thief of Crowns", "")); + cubeCards.add(new CardIdentity("Oliphaunt", "")); + cubeCards.add(new CardIdentity("Omnath, Locus of Creation", "")); + cubeCards.add(new CardIdentity("Once Upon a Time", "")); + cubeCards.add(new CardIdentity("Orcish Bowmasters", "")); + cubeCards.add(new CardIdentity("Orcish Lumberjack", "")); + cubeCards.add(new CardIdentity("Ornery Tumblewagg", "")); + cubeCards.add(new CardIdentity("Otawara, Soaring City", "")); + cubeCards.add(new CardIdentity("Otharri, Suns' Glory", "")); + cubeCards.add(new CardIdentity("Oust", "")); + cubeCards.add(new CardIdentity("Outland Liberator", "")); + cubeCards.add(new CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new CardIdentity("Palace Jailer", "")); + cubeCards.add(new CardIdentity("Palantir of Orthanc", "")); + cubeCards.add(new CardIdentity("Paradise Druid", "")); + cubeCards.add(new CardIdentity("Parallax Wave", "")); + cubeCards.add(new CardIdentity("Path to Exile", "")); + cubeCards.add(new CardIdentity("Pentad Prism", "")); + cubeCards.add(new CardIdentity("Pest Infestation", "")); + cubeCards.add(new CardIdentity("Phantasmal Image", "")); + cubeCards.add(new CardIdentity("Phantom Interference", "")); + cubeCards.add(new CardIdentity("Phyrexian Fleshgorger", "")); + cubeCards.add(new CardIdentity("Phyrexian Metamorph", "")); + cubeCards.add(new CardIdentity("Phyrexian Revoker", "")); + cubeCards.add(new CardIdentity("Pick Your Poison", "")); + cubeCards.add(new CardIdentity("Pillage the Bog", "")); + cubeCards.add(new CardIdentity("Plateau", "")); + cubeCards.add(new CardIdentity("Polluted Delta", "")); + cubeCards.add(new CardIdentity("Ponder", "")); + cubeCards.add(new CardIdentity("Portable Hole", "")); + cubeCards.add(new CardIdentity("Portal to Phyrexia", "")); + cubeCards.add(new CardIdentity("Portent", "")); + cubeCards.add(new CardIdentity("Preacher of the Schism", "")); + cubeCards.add(new CardIdentity("Preordain", "")); + cubeCards.add(new CardIdentity("Prismatic Ending", "")); + cubeCards.add(new CardIdentity("Prismatic Vista", "")); + cubeCards.add(new CardIdentity("Proft's Eidetic Memory", "")); + cubeCards.add(new CardIdentity("Pyrite Spellbomb", "")); + cubeCards.add(new CardIdentity("Pyrokinesis", "")); + cubeCards.add(new CardIdentity("Qasali Pridemage", "")); + cubeCards.add(new CardIdentity("Questing Beast", "")); + cubeCards.add(new CardIdentity("Raffine's Tower", "")); + cubeCards.add(new CardIdentity("Ragavan, Nimble Pilferer", "")); + cubeCards.add(new CardIdentity("Raging Ravine", "")); + cubeCards.add(new CardIdentity("Railway Brawler", "")); + cubeCards.add(new CardIdentity("Rain of Filth", "")); + cubeCards.add(new CardIdentity("Rampaging Raptor", "")); + cubeCards.add(new CardIdentity("Ramunap Excavator", "")); + cubeCards.add(new CardIdentity("Ranger Class", "")); + cubeCards.add(new CardIdentity("Raucous Theater", "")); + cubeCards.add(new CardIdentity("Raugrin Triome", "")); + cubeCards.add(new CardIdentity("Razorverge Thicket", "")); + cubeCards.add(new CardIdentity("Reanimate", "")); + cubeCards.add(new CardIdentity("Recurring Nightmare", "")); + cubeCards.add(new CardIdentity("Regrowth", "")); + cubeCards.add(new CardIdentity("Relic of Progenitus", "")); + cubeCards.add(new CardIdentity("Relic of Sauron", "")); + cubeCards.add(new CardIdentity("Remand", "")); + cubeCards.add(new CardIdentity("Reprieve", "")); + cubeCards.add(new CardIdentity("Restless Vents", "")); + cubeCards.add(new CardIdentity("Retrofitter Foundry", "")); + cubeCards.add(new CardIdentity("Rite of Flame", "")); + cubeCards.add(new CardIdentity("Robber of the Rich", "")); + cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", "")); + cubeCards.add(new CardIdentity("Runaway Steam-Kin", "")); + cubeCards.add(new CardIdentity("Sacred Foundry", "")); + cubeCards.add(new CardIdentity("Saheeli, Sublime Artificer", "")); + cubeCards.add(new CardIdentity("Sail into the West", "")); + cubeCards.add(new CardIdentity("Samwise the Stouthearted", "")); + cubeCards.add(new CardIdentity("Sandstorm Salvager", "")); + cubeCards.add(new CardIdentity("Sanguine Evangelist", "")); + cubeCards.add(new CardIdentity("Savai Triome", "")); + cubeCards.add(new CardIdentity("Savannah", "")); + cubeCards.add(new CardIdentity("Scalding Tarn", "")); + cubeCards.add(new CardIdentity("Scavenging Ooze", "")); + cubeCards.add(new CardIdentity("Scrapwork Mutt", "")); + cubeCards.add(new CardIdentity("Scrubland", "")); + cubeCards.add(new CardIdentity("Seachrome Coast", "")); + cubeCards.add(new CardIdentity("Seasoned Pyromancer", "")); + cubeCards.add(new CardIdentity("Seething Song", "")); + cubeCards.add(new CardIdentity("Selfless Spirit", "")); + cubeCards.add(new CardIdentity("Sensei's Divining Top", "")); + cubeCards.add(new CardIdentity("Sentinel of the Nameless City", "")); + cubeCards.add(new CardIdentity("Serra Paragon", "")); + cubeCards.add(new CardIdentity("Serum Visions", "")); + cubeCards.add(new CardIdentity("Sevinne's Reclamation", "")); + cubeCards.add(new CardIdentity("Shadowspear", "")); + cubeCards.add(new CardIdentity("Shadowy Backstreet", "")); + cubeCards.add(new CardIdentity("Shallow Grave", "")); + cubeCards.add(new CardIdentity("Shelldock Isle", "")); + cubeCards.add(new CardIdentity("Sheoldred's Edict", "")); + cubeCards.add(new CardIdentity("Sheoldred, the Apocalypse", "")); + cubeCards.add(new CardIdentity("Shorikai, Genesis Engine", "")); + cubeCards.add(new CardIdentity("Show and Tell", "")); + cubeCards.add(new CardIdentity("Showdown of the Skalds", "")); + cubeCards.add(new CardIdentity("Silent Clearing", "")); + cubeCards.add(new CardIdentity("Skullclamp", "")); + cubeCards.add(new CardIdentity("Skyclave Apparition", "")); + cubeCards.add(new CardIdentity("Slickshot Show-Off", "")); + cubeCards.add(new CardIdentity("Smuggler's Copter", "")); + cubeCards.add(new CardIdentity("Smuggler's Surprise", "")); + cubeCards.add(new CardIdentity("Snap", "")); + cubeCards.add(new CardIdentity("Snapcaster Mage", "")); + cubeCards.add(new CardIdentity("Sneak Attack", "")); + cubeCards.add(new CardIdentity("Snuff Out", "")); + cubeCards.add(new CardIdentity("Sol Ring", "")); + cubeCards.add(new CardIdentity("Solitude", "")); + cubeCards.add(new CardIdentity("Soul-Guide Lantern", "")); + cubeCards.add(new CardIdentity("Soul-Scar Mage", "")); + cubeCards.add(new CardIdentity("Spara's Headquarters", "")); + cubeCards.add(new CardIdentity("Spell Pierce", "")); + cubeCards.add(new CardIdentity("Spellseeker", "")); + cubeCards.add(new CardIdentity("Spirebluff Canal", "")); + cubeCards.add(new CardIdentity("Staff of the Storyteller", "")); + cubeCards.add(new CardIdentity("Steam Vents", "")); + cubeCards.add(new CardIdentity("Steel Seraph", "")); + cubeCards.add(new CardIdentity("Stern Scolding", "")); + cubeCards.add(new CardIdentity("Stomping Ground", "")); + cubeCards.add(new CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new CardIdentity("Strip Mine", "")); + cubeCards.add(new CardIdentity("Student of Warfare", "")); + cubeCards.add(new CardIdentity("Subtlety", "")); + cubeCards.add(new CardIdentity("Sunbaked Canyon", "")); + cubeCards.add(new CardIdentity("Sunfall", "")); + cubeCards.add(new CardIdentity("Sword of the Meek", "")); + cubeCards.add(new CardIdentity("Swords to Plowshares", "")); + cubeCards.add(new CardIdentity("Sylvan Caryatid", "")); + cubeCards.add(new CardIdentity("Sylvan Library", "")); + cubeCards.add(new CardIdentity("Sylvan Safekeeper", "")); + cubeCards.add(new CardIdentity("Taiga", "")); + cubeCards.add(new CardIdentity("Talisman of Conviction", "")); + cubeCards.add(new CardIdentity("Talisman of Creativity", "")); + cubeCards.add(new CardIdentity("Talisman of Curiosity", "")); + cubeCards.add(new CardIdentity("Talisman of Dominance", "")); + cubeCards.add(new CardIdentity("Talisman of Indulgence", "")); + cubeCards.add(new CardIdentity("Talisman of Progress", "")); + cubeCards.add(new CardIdentity("Tamiyo, Collector of Tales", "")); + cubeCards.add(new CardIdentity("Tarmogoyf", "")); + cubeCards.add(new CardIdentity("Tear Asunder", "")); + cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", "")); + cubeCards.add(new CardIdentity("Teferi, Time Raveler", "")); + cubeCards.add(new CardIdentity("Temple Garden", "")); + cubeCards.add(new CardIdentity("Tenacious Underdog", "")); + cubeCards.add(new CardIdentity("Tendrils of Agony", "")); + cubeCards.add(new CardIdentity("Territorial Kavu", "")); + cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new CardIdentity("The Gitrog, Ravenous Ride", "")); + cubeCards.add(new CardIdentity("The Mightstone and Weakstone", "")); + cubeCards.add(new CardIdentity("The One Ring", "")); + cubeCards.add(new CardIdentity("The Wandering Emperor", "")); + cubeCards.add(new CardIdentity("Thieving Skydiver", "")); + cubeCards.add(new CardIdentity("Third Path Iconoclast", "")); + cubeCards.add(new CardIdentity("Thopter Foundry", "")); + cubeCards.add(new CardIdentity("Thought Scour", "")); + cubeCards.add(new CardIdentity("Thoughtseize", "")); + cubeCards.add(new CardIdentity("Thraben Inspector", "")); + cubeCards.add(new CardIdentity("Three Steps Ahead", "")); + cubeCards.add(new CardIdentity("Through the Breach", "")); + cubeCards.add(new CardIdentity("Thundering Falls", "")); + cubeCards.add(new CardIdentity("Thundermaw Hellkite", "")); + cubeCards.add(new CardIdentity("Tidehollow Sculler", "")); + cubeCards.add(new CardIdentity("Time Spiral", "")); + cubeCards.add(new CardIdentity("Time Walk", "")); + cubeCards.add(new CardIdentity("Time Warp", "")); + cubeCards.add(new CardIdentity("Timeless Dragon", "")); + cubeCards.add(new CardIdentity("Timetwister", "")); + cubeCards.add(new CardIdentity("Tinker", "")); + cubeCards.add(new CardIdentity("Tireless Tracker", "")); + cubeCards.add(new CardIdentity("Tishana's Tidebinder", "")); + cubeCards.add(new CardIdentity("Titania, Protector of Argoth", "")); + cubeCards.add(new CardIdentity("Tolarian Academy", "")); + cubeCards.add(new CardIdentity("Torsten, Founder of Benalia", "")); + cubeCards.add(new CardIdentity("Touch the Spirit Realm", "")); + cubeCards.add(new CardIdentity("Tough Cookie", "")); + cubeCards.add(new CardIdentity("Tourach, Dread Cantor", "")); + cubeCards.add(new CardIdentity("Toxic Deluge", "")); + cubeCards.add(new CardIdentity("Treachery", "")); + cubeCards.add(new CardIdentity("Treasure Cruise", "")); + cubeCards.add(new CardIdentity("Trinket Mage", "")); + cubeCards.add(new CardIdentity("Triplicate Titan", "")); + cubeCards.add(new CardIdentity("Troll of Khazad-dum", "")); + cubeCards.add(new CardIdentity("Tropical Island", "")); + cubeCards.add(new CardIdentity("True-Name Nemesis", "")); + cubeCards.add(new CardIdentity("Trumpeting Carnosaur", "")); + cubeCards.add(new CardIdentity("Tundra", "")); + cubeCards.add(new CardIdentity("Turnabout", "")); + cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", "")); + cubeCards.add(new CardIdentity("Ulvenwald Oddity", "")); + cubeCards.add(new CardIdentity("Umezawa's Jitte", "")); + cubeCards.add(new CardIdentity("Undercity Sewers", "")); + cubeCards.add(new CardIdentity("Underground Mortuary", "")); + cubeCards.add(new CardIdentity("Underground Sea", "")); + cubeCards.add(new CardIdentity("Underworld Breach", "")); + cubeCards.add(new CardIdentity("Unearth", "")); + cubeCards.add(new CardIdentity("Unexpectedly Absent", "")); + cubeCards.add(new CardIdentity("Unholy Heat", "")); + cubeCards.add(new CardIdentity("Upheaval", "")); + cubeCards.add(new CardIdentity("Urborg, Tomb of Yawgmoth", "")); + cubeCards.add(new CardIdentity("Urza's Saga", "")); + cubeCards.add(new CardIdentity("Urza, Lord High Artificer", "")); + cubeCards.add(new CardIdentity("Usher of the Fallen", "")); + cubeCards.add(new CardIdentity("Utopia Sprawl", "")); + cubeCards.add(new CardIdentity("Valki, God of Lies", "")); + cubeCards.add(new CardIdentity("Vampiric Tutor", "")); + cubeCards.add(new CardIdentity("Vaultborn Tyrant", "")); + cubeCards.add(new CardIdentity("Verdant Catacombs", "")); + cubeCards.add(new CardIdentity("Vindicate", "")); + cubeCards.add(new CardIdentity("Virtue of Loyalty", "")); + cubeCards.add(new CardIdentity("Virtue of Persistence", "")); + cubeCards.add(new CardIdentity("Volcanic Island", "")); + cubeCards.add(new CardIdentity("Voldaren Epicure", "")); + cubeCards.add(new CardIdentity("Walking Ballista", "")); + cubeCards.add(new CardIdentity("Wasteland", "")); + cubeCards.add(new CardIdentity("Waterlogged Grove", "")); + cubeCards.add(new CardIdentity("Watery Grave", "")); + cubeCards.add(new CardIdentity("Wheel of Fortune", "")); + cubeCards.add(new CardIdentity("Winds of Abandon", "")); + cubeCards.add(new CardIdentity("Windswept Heath", "")); + cubeCards.add(new CardIdentity("Wishclaw Talisman", "")); + cubeCards.add(new CardIdentity("Wooded Foothills", "")); + cubeCards.add(new CardIdentity("Woodfall Primus", "")); + cubeCards.add(new CardIdentity("Worldspine Wurm", "")); + cubeCards.add(new CardIdentity("Wrath of God", "")); + cubeCards.add(new CardIdentity("Wrenn and Six", "")); + cubeCards.add(new CardIdentity("Xander's Lounge", "")); + cubeCards.add(new CardIdentity("Yavimaya, Cradle of Growth", "")); + cubeCards.add(new CardIdentity("Yawgmoth's Will", "")); + cubeCards.add(new CardIdentity("Zagoth Triome", "")); + cubeCards.add(new CardIdentity("Ziatora's Proving Ground", "")); + cubeCards.add(new CardIdentity("Zirda, the Dawnwaker", "")); + cubeCards.add(new CardIdentity("Zuran Orb", "")); + } +} + diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 920649e019f..9f8dff66177 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -140,6 +140,9 @@ + + + diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 5c99c4fae18..9f686f743c1 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -134,6 +134,9 @@ + + + diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index b5cda3de44b..dcc20c38256 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -998,7 +998,7 @@ public class MageServerImpl implements MageServer { public void handleException(Exception ex) throws MageException { if (ex.getMessage() != null && !ex.getMessage().equals("No message")) { - throw new MageException("Server error: " + ex.getMessage()); + throw new MageException(ex.getMessage()); } if (ex instanceof ConcurrentModificationException) { diff --git a/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java b/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java index 3fd22546cb3..be3ab3f8053 100644 --- a/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java +++ b/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java @@ -1,4 +1,3 @@ - package mage.server.draft; import mage.cards.decks.Deck; @@ -11,8 +10,7 @@ import java.util.Map; import java.util.Set; /** - * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public enum CubeFactory { @@ -22,13 +20,12 @@ public enum CubeFactory { private final Map draftCubes = new LinkedHashMap<>(); - public DraftCube createDraftCube(String draftCubeName) { DraftCube draftCube; try { Constructor con = draftCubes.get(draftCubeName).getConstructor(); - draftCube = (DraftCube)con.newInstance(); + draftCube = (DraftCube) con.newInstance(); } catch (Exception ex) { logger.fatal("CubeFactory error", ex); return null; @@ -43,7 +40,7 @@ public enum CubeFactory { DraftCube draftCube; try { Constructor con = draftCubes.get(draftCubeName).getConstructor(Deck.class); - draftCube = (DraftCube)con.newInstance(cubeFromDeck); + draftCube = (DraftCube) con.newInstance(cubeFromDeck); } catch (Exception ex) { logger.fatal("CubeFactory error", ex); return null; @@ -57,10 +54,24 @@ public enum CubeFactory { return draftCubes.keySet(); } - public void addDraftCube(String name, Class draftCube) { - if (draftCube != null) { - this.draftCubes.put(name, draftCube); + public void addDraftCube(String configName, Class configCubeClass) { + // store cubes by auto-generated names, not from config + if (configCubeClass == null) { + return; } - } + DraftCube draftCube = null; + try { + Constructor con = configCubeClass.getConstructor(); + draftCube = (DraftCube) con.newInstance(); + if (this.draftCubes.containsKey(draftCube.getName())) { + throw new IllegalArgumentException("already exists " + draftCube.getName()); + } + } catch (Exception e) { + logger.error("Can't create draft cube named by " + configName, e); + return; + } + + this.draftCubes.put(draftCube.getName(), configCubeClass); + } } diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java index e7749b83539..fee2de4c3fe 100644 --- a/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java +++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java @@ -1,5 +1,3 @@ - - package mage.server.tournament; import mage.cards.Sets; @@ -15,7 +13,6 @@ import java.lang.reflect.Constructor; import java.util.*; /** - * * @author BetaSteward_at_googlemail.com */ public enum TournamentFactory { @@ -27,7 +24,6 @@ public enum TournamentFactory { private final List tournamentTypeViews = new ArrayList<>(); - public Tournament createTournament(String tournamentType, TournamentOptions options) { Tournament tournament; @@ -37,8 +33,8 @@ public enum TournamentFactory { // transfer set information, create short info string for included sets tournament.setTournamentType(tournamentTypes.get(tournamentType)); if (tournament.getTournamentType().isLimited()) { - Map setInfo = new LinkedHashMap<>(); - for (String setCode: options.getLimitedOptions().getSetCodes()) { + Map setInfo = new LinkedHashMap<>(); + for (String setCode : options.getLimitedOptions().getSetCodes()) { tournament.getSets().add(Sets.findSet(setCode)); int count = setInfo.getOrDefault(setCode, 0); setInfo.put(setCode, count + 1); @@ -52,25 +48,31 @@ public enum TournamentFactory { } else { draftCube = CubeFactory.instance.createDraftCube(tournament.getOptions().getLimitedOptions().getDraftCubeName()); } - tournament.getOptions().getLimitedOptions().setDraftCube(draftCube); - tournament.setBoosterInfo(tournament.getOptions().getLimitedOptions().getDraftCubeName()); + String boosterInfo = ""; + if (draftCube != null) { + // make sure all loaded (will raise error on empty cards list) + draftCube.validateData(); + tournament.getOptions().getLimitedOptions().setDraftCube(draftCube); + boosterInfo = draftCube.getName(); + } + tournament.setBoosterInfo(boosterInfo); } else if (tournament.getTournamentType().isRandom()) { - StringBuilder rv = new StringBuilder( "Chaos Draft using sets: "); - for (Map.Entry entry: setInfo.entrySet()){ + StringBuilder rv = new StringBuilder("Chaos Draft using sets: "); + for (Map.Entry entry : setInfo.entrySet()) { rv.append(entry.getKey()); rv.append(';'); } tournament.setBoosterInfo(rv.toString()); } else if (tournament.getTournamentType().isReshuffled()) { - StringBuilder rv = new StringBuilder( "Chaos Reshuffled Draft using sets: "); - for (Map.Entry entry: setInfo.entrySet()){ + StringBuilder rv = new StringBuilder("Chaos Reshuffled Draft using sets: "); + for (Map.Entry entry : setInfo.entrySet()) { rv.append(entry.getKey()); rv.append(';'); } tournament.setBoosterInfo(rv.toString()); } else { StringBuilder sb = new StringBuilder(); - for (Map.Entry entry:setInfo.entrySet()) { + for (Map.Entry entry : setInfo.entrySet()) { sb.append(entry.getValue().toString()).append('x').append(entry.getKey()).append(' '); } tournament.setBoosterInfo(sb.toString()); @@ -78,12 +80,9 @@ public enum TournamentFactory { } else { tournament.setBoosterInfo(""); } - - } catch (Exception ex) { - logger.fatal("TournamentFactory error ", ex); - return null; + } catch (Exception e) { + throw new IllegalStateException("Can't create new tourney: " + e, e); } - logger.debug("Tournament created: " + tournamentType + ' ' + tournament.getId()); return tournament; } diff --git a/Mage.Server/src/test/data/config_error.xml b/Mage.Server/src/test/data/config_error.xml index fd5bffb2f95..b563a95c386 100644 --- a/Mage.Server/src/test/data/config_error.xml +++ b/Mage.Server/src/test/data/config_error.xml @@ -105,6 +105,9 @@ + + + diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 864939b06d5..2331dadecb8 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -24,6 +24,8 @@ import mage.abilities.hint.common.MonarchHint; import mage.abilities.keyword.*; import mage.cards.*; import mage.cards.decks.CardNameUtil; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; import mage.cards.decks.DeckCardLists; import mage.cards.decks.importer.DeckImporter; import mage.cards.repository.*; @@ -42,6 +44,7 @@ import mage.game.permanent.token.custom.CreatureToken; import mage.game.permanent.token.custom.XmageToken; import mage.sets.TherosBeyondDeath; import mage.target.targetpointer.TargetPointer; +import mage.tournament.cubes.CubeFromDeck; import mage.util.CardUtil; import mage.utils.SystemUtil; import mage.verify.mtgjson.MtgJsonCard; @@ -3090,7 +3093,7 @@ public class VerifyCardDataTest { } @Test - public void test_checkCardsInCubes() { + public void test_checkCardsInCubes() throws Exception { Reflections reflections = new Reflections("mage.tournament.cubes."); Set> cubesList = reflections.getSubTypesOf(DraftCube.class); Assert.assertFalse("Can't find any cubes", cubesList.isEmpty()); @@ -3103,7 +3106,24 @@ public class VerifyCardDataTest { continue; } - DraftCube cube = (DraftCube) createNewObject(cubeClass); + // cube from deck must use real deck to check complete + DraftCube cube; + if (cubeClass.isAssignableFrom(CubeFromDeck.class)) { + DeckCardLists deckCardLists = new DeckCardLists(); + deckCardLists.setCards(Arrays.asList( + new DeckCardInfo("Adanto Vanguard", "1", "XLN"), + new DeckCardInfo("Boneyard Parley", "94", "XLN"), + new DeckCardInfo("Forest", "276", "XLN") + )); + Deck deck = Deck.load(deckCardLists, true); + Constructor con = cubeClass.getConstructor(Deck.class); + cube = (DraftCube) con.newInstance(deck); + Assert.assertFalse(deckCardLists.getCards().isEmpty()); + Assert.assertEquals("deck must be loaded to cube", cube.getCubeCards().size(), deckCardLists.getCards().size()); + Assert.assertTrue("cube's name must contains cards count", cube.getName().contains(deckCardLists.getCards().size() + " cards")); + } else { + cube = (DraftCube) createNewObject(cubeClass); + } if (cube.getCubeCards().isEmpty()) { errorsList.add("Error: broken cube, empty cards list: " + cube.getClass().getCanonicalName()); } diff --git a/Mage/src/main/java/mage/game/draft/DraftCube.java b/Mage/src/main/java/mage/game/draft/DraftCube.java index d01888a5c39..b168655aa6a 100644 --- a/Mage/src/main/java/mage/game/draft/DraftCube.java +++ b/Mage/src/main/java/mage/game/draft/DraftCube.java @@ -1,20 +1,27 @@ package mage.game.draft; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import mage.cards.Card; +import mage.cards.ExpansionSet; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.util.RandomUtil; import org.apache.log4j.Logger; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + /** - * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public abstract class DraftCube { + SimpleDateFormat UPDATE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); + private static final Logger logger = Logger.getLogger(DraftCube.class); + + public static class CardIdentity { private final String name; @@ -38,36 +45,78 @@ public abstract class DraftCube { public String getName() { return name; } + public String getExtension() { return extension; } + public String getCardNumber() { return number; } } - private static final Logger logger = Logger.getLogger(DraftCube.class); - private final String name; private final String code; + private String updateInfo; + private final Date updateDate; private static final int boosterSize = 15; protected List cubeCards = new ArrayList<>(); protected List leftCubeCards = new ArrayList<>(); protected DraftCube(String name) { + this(name, "", 0, 0, 0); + } + + public DraftCube(String name, String updateInfo, int updateYear, int updateMonth, int updateDay) { this.name = name; this.code = getClass().getSimpleName(); + this.updateInfo = updateInfo; + this.updateDate = updateYear == 0 ? null : ExpansionSet.buildDate(updateYear, updateMonth, updateDay); } public String getName() { - return name; + String res = this.name; + + List extra = new ArrayList<>(); + if (this.updateInfo != null && !this.updateInfo.isEmpty()) { + extra.add(this.updateInfo); + } + if (this.updateDate != null) { + extra.add(UPDATE_DATE_FORMAT.format(this.updateDate)); + } + if (!extra.isEmpty()) { + res += String.format(" (%s)", String.join(", ", extra)); + } + + return res; + } + + /** + * Validate inner data - is it fine to start (example: cube from deck must has loaded cards) + */ + public void validateData() { + if (cubeCards.isEmpty()) { + throw new IllegalArgumentException("Can't create cube draft - found empty cards list: " + this.getName()); + } } public String getCode() { return code; } + public String getUpdateInfo() { + return updateInfo; + } + + public void setUpdateInfo(String info) { + this.updateInfo = info; + } + + public Date getUpdateDate() { + return updateDate; + } + public List getCubeCards() { return cubeCards; }