Merge branch 'External-master'
All checks were successful
/ build_release (push) Successful in 10m38s

This commit is contained in:
Failure 2025-07-19 22:36:15 -07:00
commit 3062dd066d
838 changed files with 24448 additions and 4173 deletions

View file

@ -122,6 +122,9 @@ public final class Constants {
public static final String RESOURCE_SYMBOL_FOLDER_SVG = "svg";
public static final String RESOURCE_SYMBOL_FOLDER_PNG = "png";
// download rarity icons to large folder by default
public static final String RESOURCE_PATH_SYMBOLS_RARITY_DEFAULT_PATH = RESOURCE_PATH_SYMBOLS + File.separator + RESOURCE_SYMBOL_FOLDER_LARGE;
public enum ResourceSymbolSize {
SMALL, // TODO: delete SMALL, MEDIUM and LARGE as outdated (svg or generated png works fine)
MEDIUM,
@ -133,12 +136,12 @@ public final class Constants {
// resources - sets
public static final String RESOURCE_PATH_SETS = File.separator + "sets";
public static final String RESOURCE_SET_FOLDER_SMALL = "small";
public static final String RESOURCE_SET_FOLDER_MEDIUM = ""; // empty, medium images laydown in "sets" folder, TODO: delete that and auto gen, use png for html, not gif
public static final String RESOURCE_SET_FOLDER_LARGE = "large";
public static final String RESOURCE_SET_FOLDER_SVG = "svg";
public enum ResourceSetSize {
SMALL,
MEDIUM,
LARGE,
SVG
}

View file

@ -44,7 +44,7 @@ public class DeckGeneratorPool {
// List of cards so far in the deck
private final List<Card> deckCards = new ArrayList<>();
// List of reserve cards found to fix up undersized decks
private final List<Card> reserveSpells = new ArrayList<>();
private final Map<String, Card> reserveSpells = new HashMap<>();
private final Deck deck;
/**
@ -170,12 +170,13 @@ public class DeckGeneratorPool {
* @param card the card to add.
*/
public void addCard(Card card) {
Object cnt = cardCounts.get((card.getName()));
if (cnt == null)
cardCounts.put(card.getName(), 0);
int existingCount = cardCounts.get((card.getName()));
cardCounts.put(card.getName(), existingCount + 1);
int count = cardCounts.getOrDefault(card.getName(), 0);
cardCounts.put(card.getName(), count + 1);
deckCards.add(card);
if (deckCards.stream().distinct().collect(Collectors.toList()).size() != deckCards.size()) {
System.out.println("wtf " + card.getName());
}
}
public void clearCards(boolean isClearReserve) {
@ -198,8 +199,8 @@ public class DeckGeneratorPool {
// Only cards with CMC < 7 and don't already exist in the deck
// can be added to our reserve pool as not to overwhelm the curve
// with high CMC cards and duplicates.
if (cardCMC < 7 && getCardCount(card.getName()) == 0) {
this.reserveSpells.add(card);
if (cardCMC < 7 && getCardCount(card.getName()) == 0 && !this.reserveSpells.containsKey(card.getName())) {
this.reserveSpells.put(card.getName(), card);
return true;
}
return false;
@ -416,48 +417,38 @@ public class DeckGeneratorPool {
* @return a fixed list of cards for this deck.
*/
private List<Card> getFixedSpells() {
int spellSize = deckCards.size();
int spellsSize = deckCards.size();
int nonLandSize = (deckSize - landCount);
// Less spells than needed
if (spellSize < nonLandSize) {
int spellsNeeded = nonLandSize - spellSize;
// If we haven't got enough spells in reserve to fulfil the amount we need, skip adding any.
if (reserveSpells.size() >= spellsNeeded) {
List<Card> spellsToAdd = new ArrayList<>(spellsNeeded);
// Initial reservoir
for (int i = 0; i < spellsNeeded; i++)
spellsToAdd.add(reserveSpells.get(i));
for (int i = spellsNeeded + 1; i < reserveSpells.size() - 1; i++) {
int j = RandomUtil.nextInt(i);
Card randomCard = reserveSpells.get(j);
if (isValidSpellCard(randomCard) && j < spellsToAdd.size()) {
spellsToAdd.set(j, randomCard);
}
// fewer spells than needed - add
if (spellsSize < nonLandSize) {
int needExtraSpells = nonLandSize - spellsSize;
List<Card> possibleSpells = new ArrayList<>(reserveSpells.values());
while (needExtraSpells > 0) {
Card card = RandomUtil.randomFromCollection(possibleSpells);
if (card == null) {
break;
}
// Add randomly selected spells needed
deckCards.addAll(spellsToAdd);
if (isValidSpellCard(card)) {
needExtraSpells--;
deckCards.add(card);
}
possibleSpells.remove(card);
}
}
// More spells than needed
else if (spellSize > (deckSize - landCount)) {
int spellsRemoved = (spellSize) - (deckSize - landCount);
for (int i = 0; i < spellsRemoved; ++i) {
deckCards.remove(RandomUtil.nextInt(deckCards.size()));
// more spells than needed - remove
if (spellsSize > nonLandSize) {
int removeCount = spellsSize - nonLandSize;
for (int i = 0; i < removeCount; ++i) {
deckCards.remove(RandomUtil.randomFromCollection(deckCards));
}
}
// Check we have exactly the right amount of cards for a deck.
if (deckCards.size() != nonLandSize) {
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors");
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors (wrong non land cards amount)");
}
// Return the fixed amount
return deckCards;
}
@ -619,48 +610,75 @@ public class DeckGeneratorPool {
if (needCommandersCount > 0 && !genPool.cardCounts.isEmpty()) {
throw new IllegalArgumentException("Wrong code usage: generateSpells with creatures and commanders must be called as first");
}
List<CardInfo> cardPool = CardRepository.instance.findCards(criteria);
List<Card> cardsPool = CardRepository.instance.findCards(criteria).stream()
.map(CardInfo::createMockCard)
.filter(genPool::isValidSpellCard)
.collect(Collectors.toList());
List<Card> commandersPool = cardsPool.stream()
.filter(genPool::isValidCommander)
.collect(Collectors.toList());
List<DeckGeneratorCMC.CMC> deckCMCs = genPool.getCMCsForSpellCount(needCardsCount);
int count = 0;
int usedCardsCount = 0;
int validCommanders = 0;
int reservesAdded = 0;
if (cardPool.size() > 0 && cardPool.size() >= needCardsCount) {
if (cardsPool.size() > 0 && cardsPool.size() >= needCardsCount) {
int tries = 0;
List<Card> possibleCards = new ArrayList<>(cardsPool);
List<Card> possibleCommanders = new ArrayList<>(commandersPool);
while (true) {
tries++;
// can't finish deck, stop and use reserved cards later
if (tries > DeckGenerator.MAX_TRIES) {
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors");
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors (max tries exceeded)");
break;
}
// can finish deck - but make sure it has commander
if (count >= needCardsCount) {
if (usedCardsCount >= needCardsCount) {
if (validCommanders < needCommandersCount) {
// reset deck search from scratch (except reserved cards)
count = 0;
usedCardsCount = 0;
validCommanders = 0;
deckCMCs = genPool.getCMCsForSpellCount(needCardsCount);
genPool.clearCards(false);
genPool.clearCards(true);
possibleCards = new ArrayList<>(cardsPool);
possibleCommanders = new ArrayList<>(commandersPool);
continue;
}
break;
}
Card card = cardPool.get(RandomUtil.nextInt(cardPool.size())).createMockCard();
if (possibleCards.isEmpty()) {
throw new IllegalStateException("Not enough cards to generate deck (possible cards is empty)");
}
// choose commander first
Card card = null;
if (validCommanders < needCommandersCount && !possibleCommanders.isEmpty()) {
card = RandomUtil.randomFromCollection(possibleCommanders);
}
// choose other cards after commander
if (card == null) {
card = RandomUtil.randomFromCollection(possibleCards);
}
if (!genPool.isValidSpellCard(card)) {
possibleCards.remove(card);
possibleCommanders.remove(card);
continue;
}
int cardCMC = card.getManaValue();
for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) {
if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) {
int currentAmount = deckCMC.getAmount();
if (currentAmount > 0) {
deckCMC.setAmount(currentAmount - 1);
int needAmount = deckCMC.getAmount();
if (needAmount > 0) {
deckCMC.setAmount(needAmount - 1);
genPool.addCard(card.copy());
count++;
usedCardsCount++;
// make sure it has compatible commanders
if (genPool.isValidCommander(card)) {
validCommanders++;
@ -674,7 +692,7 @@ public class DeckGeneratorPool {
}
}
} else {
throw new IllegalStateException("Not enough cards to generate deck.");
throw new IllegalStateException("Not enough cards to generate deck (cards pool too small)");
}
}

View file

@ -19,7 +19,6 @@ import mage.client.dialog.AddLandDialog;
import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.Event;
import mage.client.util.GUISizeHelper;
import mage.client.util.Listener;
import mage.client.util.audio.AudioManager;
import mage.components.CardInfoPane;
@ -31,7 +30,6 @@ import mage.util.XmageThreadFactory;
import mage.view.CardView;
import mage.view.SimpleCardView;
import org.apache.log4j.Logger;
import org.mage.card.arcane.ManaSymbols;
import javax.swing.*;
import javax.swing.border.Border;
@ -534,7 +532,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
}
}
});
refreshDeck(true);
refreshDeck(true, false);
// auto-import dropped files from OS
if (mode == DeckEditorMode.FREE_BUILDING) {
@ -673,20 +672,28 @@ public class DeckEditorPanel extends javax.swing.JPanel {
private void refreshDeck() {
if (this.isVisible()) { // TODO: test auto-close deck with active lands dialog, e.g. on timeout
refreshDeck(false);
refreshDeck(false, false);
}
}
private void refreshDeck(boolean useLayout) {
private void refreshDeck(boolean useLayout, boolean useDeckValidation) {
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
this.txtDeckName.setText(deck.getName());
deckArea.loadDeck(deck, useLayout, bigCard);
if (useDeckValidation) {
validateDeck();
}
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
private void validateDeck() {
this.deckLegalityDisplay.setVisible(true);
this.deckLegalityDisplay.validateDeck(deck);
}
private void setTimeout(int s) {
int minute = s / 60;
int second = s - (minute * 60);
@ -762,7 +769,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (newDeck != null) {
deck = newDeck;
refreshDeck();
refreshDeck(false, true);
}
// save last deck import folder
@ -818,7 +825,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
Deck deckToAppend = Deck.load(DeckImporter.importDeckFromFile(tempDeckPath, errorMessages, false), true, true);
processAndShowImportErrors(errorMessages);
this.deck = Deck.append(deckToAppend, this.deck);
refreshDeck();
refreshDeck(false, true);
} catch (GameException e1) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
} finally {
@ -922,7 +929,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (newDeck != null) {
deck = newDeck;
refreshDeck();
refreshDeck(false, true);
return true;
}
@ -1441,7 +1448,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
if (newDeck != null) {
deck = newDeck;
refreshDeck(true);
refreshDeck(true, true);
}
// save last deck history
@ -1474,7 +1481,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
// in deck editor mode - clear all cards
deck = new Deck();
}
refreshDeck();
refreshDeck(false, true);
}//GEN-LAST:event_btnNewActionPerformed
private void btnExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExitActionPerformed
@ -1483,7 +1490,9 @@ public class DeckEditorPanel extends javax.swing.JPanel {
private void btnAddLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddLandActionPerformed
AddLandDialog dialog = new AddLandDialog();
dialog.showDialog(deck, mode, this::refreshDeck);
dialog.showDialog(deck, mode, () -> {
this.refreshDeck(false, true);
});
}//GEN-LAST:event_btnAddLandActionPerformed
private void btnGenDeckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnGenDeckActionPerformed
@ -1501,7 +1510,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} finally {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck();
refreshDeck(false, true);
}//GEN-LAST:event_btnGenDeckActionPerformed
private void btnSubmitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubmitActionPerformed
@ -1548,8 +1557,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
}//GEN-LAST:event_btnExportActionPerformed
private void btnLegalityActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLegalityActionPerformed
this.deckLegalityDisplay.setVisible(true);
this.deckLegalityDisplay.validateDeck(deck);
validateDeck();
}//GEN-LAST:event_btnLegalityActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables

View file

@ -75,7 +75,7 @@ public class DraftPickLogger {
private void appendToDraftLog(String data) {
if (logging) {
try {
Files.write(logPath, data.getBytes(), StandardOpenOption.APPEND);
Files.write(logPath, data.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (IOException ex) {
LOGGER.error(null, ex);
}

View file

@ -257,10 +257,7 @@ public class CallbackClientImpl implements CallbackClient {
if (panel != null) {
Session session = SessionHandler.getSession();
if (session.isJsonLogActive()) {
UUID gameId = callback.getObjectId();
appendJsonEvent("GAME_OVER", callback.getObjectId(), message);
String logFileName = "game-" + gameId + ".json";
S3Uploader.upload(logFileName, gameId.toString());
}
panel.endMessage(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMessage());
}

View file

@ -1,46 +0,0 @@
package mage.client.remote;
import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.Upload;
import org.apache.log4j.Logger;
import java.io.File;
public class S3Uploader {
private static final Logger logger = Logger.getLogger(S3Uploader.class);
public static Boolean upload(String filePath, String keyName) throws Exception {
String existingBucketName = System.getenv("S3_BUCKET") != null ? System.getenv("S3_BUCKET")
: "xmage-game-logs-dev";
String accessKeyId = System.getenv("AWS_ACCESS_ID");
String secretKeyId = System.getenv("AWS_SECRET_KEY");
if (accessKeyId == null || accessKeyId.isEmpty()
|| secretKeyId == null || secretKeyId.isEmpty()
|| existingBucketName.isEmpty()) {
logger.info("Aborting json log sync.");
return false;
}
String path = new File("./" + filePath).getCanonicalPath();
logger.info("Syncing " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId);
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKeyId, secretKeyId);
TransferManager tm = new TransferManager(awsCreds);
Upload upload = tm.upload(existingBucketName, "/game/" + keyName + ".json", new File(path));
try {
upload.waitForUploadResult();
logger.info("Sync Complete For " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId);
new File(path);
return true;
} catch (AmazonClientException amazonClientException) {
logger.fatal("Unable to upload file, upload was aborted.", amazonClientException);
return false;
}
}
}

View file

@ -896,7 +896,7 @@ public class TablesPanel extends javax.swing.JPanel {
formatFilterList.add(RowFilter.regexFilter("^Limited", TablesTableModel.COLUMN_DECK_TYPE));
}
if (btnFormatOther.isSelected()) {
formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform|^Australian Highlander|^European Highlander|^Canadian Highlander|^Constructed - Old|^Constructed - Historic", TablesTableModel.COLUMN_DECK_TYPE));
formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform|^Constructed - Freeform Unlimited|^Australian Highlander|^European Highlander|^Canadian Highlander|^Constructed - Old|^Constructed - Historic", TablesTableModel.COLUMN_DECK_TYPE));
}
// skill