Merge branch 'master' into External-master
All checks were successful
/ build_release (push) Successful in 16m24s
44
.forgejo/workflows/release.yml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
concurrency:
|
||||
group: "release"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_release:
|
||||
runs-on: node-debian
|
||||
container:
|
||||
image: maven:3-eclipse-temurin-11
|
||||
steps:
|
||||
- name: Install prerequisites
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get -y install git nodejs
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build Mage
|
||||
run: |
|
||||
mvn -T 12 clean install -DskipTests
|
||||
|
||||
- name: Build Client
|
||||
run: |
|
||||
cd Mage.Client && mvn package assembly:single
|
||||
|
||||
- name: Build Server
|
||||
run: |
|
||||
cd Mage.Server && mvn package assembly:single
|
||||
|
||||
- uses: forgejo/upload-artifact@v4
|
||||
with:
|
||||
name: client.zip
|
||||
path: ./Mage.Client/target/mage-client.zip
|
||||
|
||||
- uses: forgejo/upload-artifact@v4
|
||||
with:
|
||||
name: server.zip
|
||||
path: ./Mage.Server/target/mage-server.zip
|
||||
|
||||
10
.github/dependabot.yml
vendored
|
|
@ -1,10 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
- package-ecosystem: 'maven'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
23
.github/labeler.yml
vendored
|
|
@ -1,23 +0,0 @@
|
|||
dev:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: [ '*', 'Utils/**', '/.github/**' ]
|
||||
|
||||
engine:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: [ 'Mage/**' ]
|
||||
|
||||
client:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: [ 'Mage.Client/**', 'Mage.Common/**', 'Mage.Plugins/**' ]
|
||||
|
||||
server:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: [ 'Mage.Server*/**' ]
|
||||
|
||||
tests:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: [ 'Mage.Verify/**', 'Mage.Tests/**', 'Mage.Reports/**' ]
|
||||
|
||||
cards:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: [ 'Mage.Sets/**' ]
|
||||
15
.github/workflows/labeler-auto.yml
vendored
|
|
@ -1,15 +0,0 @@
|
|||
name: "Pull Request Labeler (auto)"
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: label-the-PR
|
||||
uses: actions/labeler@v5
|
||||
with:
|
||||
configuration-path: '.github/labeler.yml'
|
||||
24
.github/workflows/labeler-manual.yml
vendored
|
|
@ -1,24 +0,0 @@
|
|||
name: "Pull Request Labeler (manual)"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
oldPRs:
|
||||
# no multi lines support, so call by single PR only
|
||||
description: 'PR number to process'
|
||||
required: true
|
||||
type: string
|
||||
default: '123'
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: label-the-PR
|
||||
uses: actions/labeler@v5
|
||||
with:
|
||||
configuration-path: '.github/labeler.yml'
|
||||
pr-number: |
|
||||
${{ github.event.inputs.oldPRs }}
|
||||
21
.github/workflows/mtg-fetch-cards.yml
vendored
|
|
@ -1,21 +0,0 @@
|
|||
name: Mtg Card Fetch Bot
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
fetch-card-references:
|
||||
name: Fetch MTG Card
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: ldeluigi/mtg-fetch-action@v1
|
||||
1
.gitignore
vendored
|
|
@ -62,3 +62,4 @@ Utils/*implemented.txt
|
|||
# build tools
|
||||
mage-bundle.zip
|
||||
.env
|
||||
.classpath
|
||||
|
|
|
|||
36
Dockerfile
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
FROM eclipse-temurin:11
|
||||
|
||||
# Set XMage config defaults
|
||||
ENV LANG=C.UTF-8 \
|
||||
XMAGE_DOCKER_SERVER_ADDRESS="0.0.0.0" \
|
||||
XMAGE_DOCKER_PORT="17171" \
|
||||
XMAGE_DOCKER_SEONDARY_BIND_PORT="17179" \
|
||||
XMAGE_DOCKER_MAX_SECONDS_IDLE="600" \
|
||||
XMAGE_DOCKER_AUTHENTICATION_ACTIVATED="false" \
|
||||
XMAGE_DOCKER_SERVER_NAME="mage-server" \
|
||||
XMAGE_DOCKER_MAILGUN_API_KEY="" \
|
||||
XMAGE_DOCKER_MAILGUN_DOMAIN="" \
|
||||
XMAGE_DOCKER_MAIL_SMTP_HOST="" \
|
||||
XMAGE_DOCKER_MAIL_SMTP_PORT="" \
|
||||
XMAGE_DOCKER_MAIL_USER="" \
|
||||
XMAGE_DOCKER_MAIL_PASSWORD="" \
|
||||
XMAGE_DOCKER_MAIL_FROM_ADDRESS="" \
|
||||
XMAGE_DOCKER_MAX_GAME_THREADS="10" \
|
||||
XMAGE_DOCKER_MAX_AI_OPPONENTS="15" \
|
||||
XMAGE_DOCKER_JAVA_OPTS="-Xmx1024m -XX:MaxPermSize=384m -Dlog4j.configuration=file:./config/log4j.properties"
|
||||
# Install dependencies
|
||||
RUN set -ex && \
|
||||
apt update && \
|
||||
apt install -y curl ca-certificates bash jq unzip
|
||||
|
||||
# Download latest xmage
|
||||
WORKDIR /xmage
|
||||
|
||||
RUN <<EOF
|
||||
wget $(curl -Ls -o /dev/null -w %{url_effective} "https://git.cef.icu/Failure/foul-magics/actions/runs/latest" | sed 's/$/\/artifacts\/server.zip/')
|
||||
unzip server.zip
|
||||
unzip mage-server.zip
|
||||
rm server.zip mage-server.zip
|
||||
EOF
|
||||
|
||||
CMD ["./dockerRun.sh"]
|
||||
|
|
@ -7,6 +7,7 @@ import mage.cards.decks.Deck;
|
|||
import mage.cards.repository.CardRepository;
|
||||
import mage.cards.repository.CardScanner;
|
||||
import mage.cards.repository.RepositoryUtil;
|
||||
import mage.cards.repository.TagRepository;
|
||||
import mage.client.cards.BigCard;
|
||||
import mage.client.chat.ChatPanelBasic;
|
||||
import mage.client.components.*;
|
||||
|
|
@ -58,11 +59,14 @@ import org.apache.log4j.Logger;
|
|||
import org.junit.Assert;
|
||||
import org.mage.card.arcane.ManaSymbols;
|
||||
import org.mage.card.arcane.SvgUtils;
|
||||
import org.mage.plugins.card.dl.sources.TagSource;
|
||||
import org.mage.plugins.card.images.DownloadPicturesService;
|
||||
import org.mage.plugins.card.info.CardInfoPaneImpl;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.PopupMenuEvent;
|
||||
|
|
@ -90,7 +94,7 @@ import java.util.stream.Collectors;
|
|||
*/
|
||||
public class MageFrame extends javax.swing.JFrame implements MageClient {
|
||||
|
||||
private static final String TITLE_NAME = "XMage";
|
||||
private static final String TITLE_NAME = "XMage (Foul Magics)";
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(MageFrame.class);
|
||||
private static final String LITE_MODE_ARG = "-lite";
|
||||
|
|
@ -297,14 +301,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
errorDialog.setLocation(100, 100);
|
||||
desktopPane.add(errorDialog, errorDialog.isModal() ? JLayeredPane.MODAL_LAYER : JLayeredPane.PALETTE_LAYER);
|
||||
|
||||
try {
|
||||
this.whatsNewDialog = new WhatsNewDialog();
|
||||
} catch (Throwable e) {
|
||||
// example: JavaFX is not supported on old MacOS with OpenJDK
|
||||
// https://bugs.openjdk.java.net/browse/JDK-8202132
|
||||
LOGGER.error("JavaFX is not supported by your system. What's new page will be disabled.", e);
|
||||
this.whatsNewDialog = null;
|
||||
}
|
||||
|
||||
PING_SENDER_EXECUTOR.scheduleAtFixedRate(SessionHandler::ping, TablesPanel.PING_SERVER_SECS, TablesPanel.PING_SERVER_SECS, TimeUnit.SECONDS);
|
||||
|
||||
|
|
@ -385,11 +381,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
|
||||
setWindowTitle(); // make sure title is actual on startup
|
||||
});
|
||||
|
||||
// run what's new checks (loading in background)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
showWhatsNewDialog(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -998,6 +989,8 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
popupDownload = new javax.swing.JPopupMenu();
|
||||
menuDownloadSymbols = new javax.swing.JMenuItem();
|
||||
menuDownloadImages = new javax.swing.JMenuItem();
|
||||
menuDownloadTags = new javax.swing.JMenuItem();
|
||||
|
||||
desktopPane = new MageJDesktop();
|
||||
mageToolbar = new javax.swing.JToolBar();
|
||||
btnPreferences = new javax.swing.JButton();
|
||||
|
|
@ -1058,6 +1051,14 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
}
|
||||
});
|
||||
popupDownload.add(menuDownloadImages);
|
||||
|
||||
menuDownloadTags.setText("Download Scryfall Tagger tags (this will freeze for a bit)");
|
||||
menuDownloadTags.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
TagSource.instance.syncTagRepositiory();
|
||||
}
|
||||
});
|
||||
popupDownload.add(menuDownloadTags);
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
setMinimumSize(new java.awt.Dimension(1000, 500));
|
||||
|
|
@ -1199,7 +1200,9 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void btnDeckEditorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeckEditorActionPerformed
|
||||
|
||||
|
||||
private void btnDeckEditorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeckEditorActionPerformed
|
||||
showDeckEditor(DeckEditorMode.FREE_BUILDING, null, null, null, 0);
|
||||
}//GEN-LAST:event_btnDeckEditorActionPerformed
|
||||
|
||||
|
|
@ -1648,6 +1651,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
private javax.swing.JMenuItem menuDebugTestModalDialog;
|
||||
private javax.swing.JMenuItem menuDownloadImages;
|
||||
private javax.swing.JMenuItem menuDownloadSymbols;
|
||||
private javax.swing.JMenuItem menuDownloadTags;
|
||||
private javax.swing.JPopupMenu popupDebug;
|
||||
private javax.swing.JPopupMenu popupDownload;
|
||||
private javax.swing.JToolBar.Separator separatorDebug;
|
||||
|
|
@ -1960,6 +1964,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
|
||||
menuDownloadSymbols.setFont(font);
|
||||
menuDownloadImages.setFont(font);
|
||||
menuDownloadTags.setFont(font);
|
||||
menuDebugTestModalDialog.setFont(font);
|
||||
menuDebugTestCardRenderModesDialog.setFont(font);
|
||||
menuDebugTestCustomCode.setFont(font);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public enum MageTray {
|
|||
private Image flashedImage;
|
||||
private TrayIcon trayIcon;
|
||||
|
||||
private int state = 0;
|
||||
private int state = 3;
|
||||
|
||||
public void install() {
|
||||
if (!SystemTray.isSupported()) {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,15 @@ package mage.client.constants;
|
|||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import javafx.util.Pair;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -14,6 +20,72 @@ public final class Constants {
|
|||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static final ImmutableList<Pair<String, String[]>> foulMagicsSets = ImmutableList.of(
|
||||
new Pair<String, String[]>("Set 3 - Old Fat Men", new String[] {
|
||||
"Ninth Edition",
|
||||
"Tenth Edition",
|
||||
"* Time Spiral Block",
|
||||
"* Lorwyn Block",
|
||||
"* Shards of Alara Block",
|
||||
"Scars of Mirrodin",
|
||||
"Zendikar",
|
||||
"Rise of the Eldrazi",
|
||||
"Worldwake",
|
||||
"* Innistrad Block",
|
||||
"Foul Magic Block 3 Extras"
|
||||
}),
|
||||
new Pair<String, String[]>("Set 2.5 - Ravnica Cultural Exchange", new String[] {
|
||||
"* March of the Machine Block",
|
||||
"* Phyrexia: All Will Be One Block",
|
||||
"* The Brothers' War Block",
|
||||
"* Dominaria United Block",
|
||||
"* Kamigawa: Neon Dynasty Block",
|
||||
"* Theros Beyond Death Block",
|
||||
"* Strixhaven: School of Mages Block",
|
||||
"* The Lost Caverns of Ixalan Block",
|
||||
"* Ikoria: Lair of Behemoths Block",
|
||||
"* Adventures in the Forgotten Realms Block",
|
||||
"Modern Horizons 3",
|
||||
"The Lord of the Rings: Tales of Middle-earth",
|
||||
"Double Masters 2022",
|
||||
"Rise of the Eldrazi",
|
||||
"Modern Horizons 2",
|
||||
"Foundations",
|
||||
"Foundations Jumpstart",
|
||||
"* Guilds of Ravnica Block",
|
||||
"* Return to Ravnica Block",
|
||||
}),
|
||||
new Pair<String, String[]>("Set 2 - Phyrexians, Eldrazi, Asians - Oh my!", new String[] {
|
||||
"* March of the Machine Block",
|
||||
"* Phyrexia: All Will Be One Block",
|
||||
"* The Brothers' War Block",
|
||||
"* Dominaria United Block",
|
||||
"* Kamigawa: Neon Dynasty Block",
|
||||
"* Theros Beyond Death Block",
|
||||
"* Strixhaven: School of Mages Block",
|
||||
"* The Lost Caverns of Ixalan Block",
|
||||
"* Ikoria: Lair of Behemoths Block",
|
||||
"* Adventures in the Forgotten Realms Block",
|
||||
"Modern Horizons 3",
|
||||
"The Lord of the Rings: Tales of Middle-earth",
|
||||
"Double Masters 2022",
|
||||
"Rise of the Eldrazi",
|
||||
"Modern Horizons 2"
|
||||
}),
|
||||
new Pair<String, String[]>("Set 1 - In Da Beegeening", new String[] {
|
||||
"Foundations",
|
||||
"Foundations Jumpstart",
|
||||
"* Guilds of Ravnica Block",
|
||||
"* Return to Ravnica Block",
|
||||
}),
|
||||
new Pair<String, String[]>("Set 0.5 - Foundationally Gaming", new String[] {
|
||||
"Foundations",
|
||||
"Foundations Jumpstart",
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
|
||||
public static final int FRAME_MAX_HEIGHT = 367;
|
||||
public static final int FRAME_MAX_WIDTH = 256;
|
||||
public static final int ART_MAX_HEIGHT = 168;
|
||||
|
|
@ -147,5 +219,6 @@ public final class Constants {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,46 @@
|
|||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnExpansionSearchActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="cbFoulMagicPresets">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[120, 20]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[120, 20]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[120, 20]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbFoulMagicPresetSelected"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="FoulMagicPreset"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="btnFoulMagicPreset">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/buttons/brick.png"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Set to Foul Magic preset"/>
|
||||
<Property name="alignmentX" type="float" value="1.0"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[23, 23]"/>
|
||||
</Property>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnFoulMagicPresetSet"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator2">
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="chkPennyDreadful">
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ import mage.cards.decks.PennyDreadfulLegalityUtil;
|
|||
import mage.cards.repository.*;
|
||||
import mage.client.MageFrame;
|
||||
import mage.client.cards.*;
|
||||
import mage.client.constants.Constants;
|
||||
import mage.client.constants.Constants.SortBy;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.deckeditor.table.TableModel;
|
||||
import mage.client.deckeditor.table.TaggerModel;
|
||||
import mage.client.dialog.CheckBoxList;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.gui.FastSearchUtil;
|
||||
|
|
@ -31,13 +33,20 @@ import mage.view.CardsView;
|
|||
import org.apache.log4j.Logger;
|
||||
import org.mage.card.arcane.ManaSymbolsCellRenderer;
|
||||
|
||||
import javafx.util.Pair;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.*;
|
||||
|
||||
import static mage.client.dialog.PreferencesDialog.*;
|
||||
|
|
@ -93,6 +102,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
|
||||
cardSelectorScrollPane.setOpaque(false);
|
||||
cardSelectorScrollPane.getViewport().setOpaque(false);
|
||||
|
||||
taggerScrollPane.setOpaque(false);
|
||||
taggerScrollPane.getViewport().setOpaque(false);
|
||||
|
||||
cbSortBy.setModel(new DefaultComboBoxModel<>(SortBy.values()));
|
||||
cbSortBy.setSelectedItem(sortSetting.getSortBy());
|
||||
jTextFieldSearch.addActionListener(searchAction);
|
||||
|
|
@ -102,19 +115,36 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
tbColor.setOpaque(true); // false = transparent
|
||||
tbTypes.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
|
||||
tbTypes.setOpaque(true); // false = transparent
|
||||
taggerScrollPane.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
|
||||
taggerScrollPane.setOpaque(true);
|
||||
cardSelectorBottomPanel.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
|
||||
cardSelectorBottomPanel.setOpaque(true); // false = transparent
|
||||
}
|
||||
|
||||
private void initListViewComponents() {
|
||||
taggerTable = new JTable();
|
||||
mainTable = new JTable();
|
||||
|
||||
|
||||
mainModel = new TableModel();
|
||||
tagsModel = new TaggerModel(() -> filterCards());
|
||||
|
||||
tagsModel.addListeners(taggerTable);
|
||||
taggerTable.setModel(tagsModel);
|
||||
TableColumnModel taggerTableModel = taggerTable.getColumnModel();
|
||||
taggerTableModel.getColumn(0).setMaxWidth(30);
|
||||
taggerTableModel.getColumn(0).setPreferredWidth(30);
|
||||
taggerTableModel.getColumn(1).setMaxWidth(30);
|
||||
taggerTableModel.getColumn(1).setPreferredWidth(30);
|
||||
taggerTableModel.getColumn(2).setMaxWidth(160);
|
||||
taggerTableModel.getColumn(2).setPreferredWidth(160);
|
||||
taggerTableModel.getColumn(4).setMaxWidth(60);
|
||||
DefaultTableCellRenderer myRenderer = (DefaultTableCellRenderer) mainTable.getDefaultRenderer(String.class);
|
||||
|
||||
|
||||
mainModel.addListeners(mainTable);
|
||||
|
||||
mainTable.setModel(mainModel);
|
||||
mainTable.setForeground(Color.white);
|
||||
DefaultTableCellRenderer myRenderer = (DefaultTableCellRenderer) mainTable.getDefaultRenderer(String.class);
|
||||
myRenderer.setBackground(new Color(0, 0, 0, 100));
|
||||
mainTable.getColumnModel().getColumn(0).setMaxWidth(0);
|
||||
mainTable.getColumnModel().getColumn(0).setPreferredWidth(10);
|
||||
|
|
@ -132,6 +162,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
|
||||
// mainTable.setToolTipText(cardSelectorScrollPane.getToolTipText());
|
||||
cardSelectorScrollPane.setViewportView(mainTable);
|
||||
|
||||
taggerScrollPane.setViewportView(taggerTable);
|
||||
|
||||
taggerTable.setOpaque(false);
|
||||
|
||||
mainTable.setOpaque(false);
|
||||
cbSortBy.setEnabled(false);
|
||||
chkPiles.setEnabled(false);
|
||||
|
|
@ -184,6 +219,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
mainTable.setFont(GUISizeHelper.tableFont);
|
||||
mainTable.setRowHeight(GUISizeHelper.tableRowHeight);
|
||||
|
||||
taggerTable.getTableHeader().setFont(GUISizeHelper.tableFont);
|
||||
taggerTable.setFont(GUISizeHelper.tableFont);
|
||||
taggerTable.setRowHeight(GUISizeHelper.tableRowHeight);
|
||||
}
|
||||
|
||||
public void switchToGrid() {
|
||||
|
|
@ -237,26 +275,44 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
|
||||
if (limited) {
|
||||
List<Predicate<MageObject>> predicates = new ArrayList<>();
|
||||
List<Predicate<MageObject>> exclusion = new ArrayList<>();
|
||||
|
||||
if (this.tbGreen.isSelected()) {
|
||||
predicates.add(new ColorPredicate(ObjectColor.GREEN));
|
||||
} else {
|
||||
exclusion.add(new ColorPredicate(ObjectColor.GREEN));
|
||||
}
|
||||
if (this.tbRed.isSelected()) {
|
||||
predicates.add(new ColorPredicate(ObjectColor.RED));
|
||||
} else {
|
||||
exclusion.add(new ColorPredicate(ObjectColor.RED));
|
||||
}
|
||||
if (this.tbBlack.isSelected()) {
|
||||
predicates.add(new ColorPredicate(ObjectColor.BLACK));
|
||||
} else {
|
||||
exclusion.add(new ColorPredicate(ObjectColor.BLACK));
|
||||
}
|
||||
if (this.tbBlue.isSelected()) {
|
||||
predicates.add(new ColorPredicate(ObjectColor.BLUE));
|
||||
} else {
|
||||
exclusion.add(new ColorPredicate(ObjectColor.BLUE));
|
||||
}
|
||||
if (this.tbWhite.isSelected()) {
|
||||
predicates.add(new ColorPredicate(ObjectColor.WHITE));
|
||||
} else {
|
||||
exclusion.add(new ColorPredicate(ObjectColor.WHITE));
|
||||
}
|
||||
|
||||
if (this.tbColorless.isSelected()) {
|
||||
predicates.add(ColorlessPredicate.instance);
|
||||
} else {
|
||||
exclusion.add(ColorlessPredicate.instance);
|
||||
}
|
||||
if (this.tbLimitColors.isSelected()) {
|
||||
filter.add(Predicates.and(Predicates.not(Predicates.or(exclusion)), Predicates.or(predicates)));
|
||||
} else {
|
||||
filter.add(Predicates.or(predicates));
|
||||
}
|
||||
filter.add(Predicates.or(predicates));
|
||||
|
||||
predicates.clear();
|
||||
if (this.tbLand.isSelected()) {
|
||||
|
|
@ -346,6 +402,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
criteria.red(this.tbRed.isSelected());
|
||||
criteria.white(this.tbWhite.isSelected());
|
||||
criteria.colorless(this.tbColorless.isSelected());
|
||||
criteria.limitColors(this.tbLimitColors.isSelected());
|
||||
|
||||
// if you add new type filter then sync it with CardType
|
||||
if (this.tbLand.isSelected()) {
|
||||
|
|
@ -463,7 +520,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
|
||||
if (limited) {
|
||||
for (Card card : cards) {
|
||||
if (filter.match(card, null)) {
|
||||
if (filter.match(card, null) && tagsModel.testCard(card)) {
|
||||
filteredCards.add(card);
|
||||
}
|
||||
}
|
||||
|
|
@ -482,6 +539,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
if (!filter.match(card, null)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tagsModel.testCard(card)) {
|
||||
continue;
|
||||
}
|
||||
// found
|
||||
filteredCards.add(card);
|
||||
}
|
||||
|
|
@ -559,9 +620,12 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
tbBlack = new javax.swing.JToggleButton();
|
||||
tbWhite = new javax.swing.JToggleButton();
|
||||
tbColorless = new javax.swing.JToggleButton();
|
||||
tbLimitColors = new javax.swing.JToggleButton();
|
||||
jSeparator1 = new javax.swing.JToolBar.Separator();
|
||||
cbExpansionSet = new javax.swing.JComboBox<>();
|
||||
btnExpansionSearch = new javax.swing.JButton();
|
||||
cbFoulMagicPresets = new javax.swing.JComboBox<>();
|
||||
btnFoulMagicPreset = new javax.swing.JButton();
|
||||
jSeparator2 = new javax.swing.JToolBar.Separator();
|
||||
chkPennyDreadful = new javax.swing.JCheckBox();
|
||||
btnBooster = new javax.swing.JButton();
|
||||
|
|
@ -594,6 +658,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
jButtonRemoveFromMain = new javax.swing.JButton();
|
||||
jButtonAddToSideboard = new javax.swing.JButton();
|
||||
jButtonRemoveFromSideboard = new javax.swing.JButton();
|
||||
jButtonRandomCard = new javax.swing.JButton();
|
||||
jTextFieldSearch = new javax.swing.JTextField();
|
||||
chkNames = new javax.swing.JCheckBox();
|
||||
chkTypes = new javax.swing.JCheckBox();
|
||||
|
|
@ -603,7 +668,72 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
jButtonClean = new javax.swing.JButton();
|
||||
cardCountLabel = new javax.swing.JLabel();
|
||||
cardCount = new javax.swing.JLabel();
|
||||
|
||||
// Brings me back to the Tk days
|
||||
|
||||
taggerScrollPane = new javax.swing.JScrollPane();
|
||||
taggerContainer = new javax.swing.JPanel();
|
||||
taggerContainer.setPreferredSize(new Dimension(200, 400));
|
||||
taggerContainer.setLayout(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.fill = GridBagConstraints.BOTH;
|
||||
gbc.weighty = 1;
|
||||
gbc.weightx = 1;
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
|
||||
taggerControlBar = new javax.swing.JPanel();
|
||||
taggerControlBar.setLayout(new GridBagLayout());
|
||||
|
||||
taggerReset = new javax.swing.JButton("Reset");
|
||||
taggerReset.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
tagsModel.clear();
|
||||
}
|
||||
});
|
||||
|
||||
taggerContainer.add(taggerScrollPane, gbc);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weighty = 0;
|
||||
gbc.weightx = 1;
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
taggerContainer.add(taggerControlBar, gbc);
|
||||
|
||||
gbc.fill = GridBagConstraints.NONE;
|
||||
gbc.weighty = 0;
|
||||
gbc.weightx = 0;
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
|
||||
taggerControlBar.add(taggerReset, gbc);
|
||||
|
||||
taggerSearch = new javax.swing.JTextField();
|
||||
taggerSearch.getDocument().addDocumentListener(new DocumentListener() {
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
changedUpdate(e);
|
||||
}
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
changedUpdate(e);
|
||||
}
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
tagsModel.search(taggerSearch.getText());
|
||||
}
|
||||
});
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weighty = 1;
|
||||
gbc.weightx = 1;
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 0;
|
||||
taggerControlBar.add(taggerSearch, gbc);
|
||||
|
||||
// End hell
|
||||
|
||||
tablePanel = new javax.swing.JSplitPane(JSplitPane.HORIZONTAL_SPLIT, cardSelectorScrollPane, taggerContainer);
|
||||
tablePanel.setOneTouchExpandable(true);
|
||||
tablePanel.setResizeWeight(1.0);
|
||||
tablePanel.setDividerLocation(1.0);
|
||||
|
||||
tbColor.setFloatable(false);
|
||||
tbColor.setRollover(true);
|
||||
tbColor.setToolTipText("Hold the ALT-key while clicking to deselect all other colors or hold the CTRL-key to select only all other colors.");
|
||||
|
|
@ -701,6 +831,23 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
}
|
||||
});
|
||||
tbColor.add(tbColorless);
|
||||
|
||||
tbLimitColors.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/lock.png"))); // NOI18N
|
||||
tbLimitColors.setSelected(false);
|
||||
tbLimitColors.setToolTipText("Limit results to ONLY these colors");
|
||||
tbLimitColors.setActionCommand("LimitColors");
|
||||
tbLimitColors.setFocusable(false);
|
||||
tbLimitColors.setPreferredSize(new java.awt.Dimension(28, 28));
|
||||
tbLimitColors.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
tbLimitColors.setSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/lock.png"))); // NOI18N
|
||||
tbLimitColors.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
tbLimitColors.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
tbLimitColorsActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
tbColor.add(tbLimitColors);
|
||||
|
||||
tbColor.add(jSeparator1);
|
||||
|
||||
reloadSetsCombobox();
|
||||
|
|
@ -741,6 +888,31 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
}
|
||||
});
|
||||
tbColor.add(btnExpansionSearch);
|
||||
|
||||
List<String> setNames = new LinkedList<String>();
|
||||
for (Pair<String, String[]> pair : Constants.foulMagicsSets) {
|
||||
setNames.add(pair.getKey());
|
||||
}
|
||||
|
||||
DefaultComboBoxModel presetModel = new DefaultComboBoxModel<>(setNames.toArray());
|
||||
cbFoulMagicPresets.setModel(presetModel);
|
||||
|
||||
tbColor.add(cbFoulMagicPresets);
|
||||
|
||||
btnFoulMagicPreset.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/brick.png"))); // NOI18N
|
||||
btnFoulMagicPreset.setToolTipText("Set to Foul Magic preset");
|
||||
btnFoulMagicPreset.setAlignmentX(1.0F);
|
||||
btnFoulMagicPreset.setFocusable(false);
|
||||
btnFoulMagicPreset.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
btnFoulMagicPreset.setPreferredSize(new java.awt.Dimension(24, 24));
|
||||
btnFoulMagicPreset.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
btnFoulMagicPreset.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
btnFoulMagicPresetSet(evt);
|
||||
}
|
||||
});
|
||||
tbColor.add(btnFoulMagicPreset);
|
||||
|
||||
tbColor.add(jSeparator2);
|
||||
|
||||
chkPennyDreadful.setText("Penny Dreadful Only");
|
||||
|
|
@ -1036,6 +1208,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
|
||||
cardSelectorBottomPanel.setOpaque(false);
|
||||
cardSelectorBottomPanel.setPreferredSize(new java.awt.Dimension(897, 40));
|
||||
tablePanel.setOpaque(false);
|
||||
tablePanel.setPreferredSize(new java.awt.Dimension(600, 40));
|
||||
|
||||
jButtonAddToMain.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/deck_in.png"))); // NOI18N
|
||||
jButtonAddToMain.setToolTipText("<html>Add selected cards to deck.<br/>\nAlternative: <strong>Double click</strong> the card in card selector to move a card to the deck.");
|
||||
|
|
@ -1084,6 +1258,18 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
jButtonRemoveFromSideboardActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jButtonRandomCard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/dice.png"))); // NOI18N
|
||||
jButtonRandomCard.setToolTipText("Add a random card from the current search to your deck.");
|
||||
jButtonRandomCard.setMargin(null);
|
||||
jButtonRandomCard.setMaximumSize(new java.awt.Dimension(35, 23));
|
||||
jButtonRandomCard.setMinimumSize(new java.awt.Dimension(35, 23));
|
||||
jButtonRandomCard.setPreferredSize(new java.awt.Dimension(30, 28));
|
||||
jButtonRandomCard.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jButtonRandomCardActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jTextFieldSearch.setToolTipText("Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)");
|
||||
|
||||
|
|
@ -1167,8 +1353,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
cardCountLabel.setToolTipText("Number of cards currently shown.");
|
||||
|
||||
cardCount.setText("0");
|
||||
|
||||
|
||||
javax.swing.GroupLayout cardSelectorBottomPanelLayout = new javax.swing.GroupLayout(cardSelectorBottomPanel);
|
||||
|
||||
cardSelectorBottomPanel.setLayout(cardSelectorBottomPanelLayout);
|
||||
cardSelectorBottomPanelLayout.setHorizontalGroup(
|
||||
cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
|
|
@ -1181,6 +1368,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
.addComponent(jButtonAddToSideboard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(2, 2, 2)
|
||||
.addComponent(jButtonRemoveFromSideboard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(2,2,2)
|
||||
.addComponent(jButtonRandomCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jTextFieldSearch, javax.swing.GroupLayout.PREFERRED_SIZE, 219, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
|
|
@ -1215,6 +1404,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
.addComponent(jButtonRemoveFromMain, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(jButtonAddToSideboard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(jButtonRemoveFromSideboard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(jButtonRandomCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jTextFieldSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(jButtonSearch)
|
||||
|
|
@ -1235,9 +1425,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(tbColor, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(tbTypes, javax.swing.GroupLayout.DEFAULT_SIZE, 1057, Short.MAX_VALUE)
|
||||
.addComponent(cardSelectorScrollPane)
|
||||
.addComponent(tablePanel)
|
||||
.addComponent(cardSelectorBottomPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1057, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
|
|
@ -1245,10 +1436,13 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
.addGap(0, 0, 0)
|
||||
.addComponent(tbTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(cardSelectorScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 237, Short.MAX_VALUE)
|
||||
.addComponent(tablePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 237, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(cardSelectorBottomPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
);
|
||||
|
||||
|
||||
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void cbExpansionSetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbExpansionSetActionPerformed
|
||||
|
|
@ -1364,6 +1558,14 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
}
|
||||
}
|
||||
}//GEN-LAST:event_jButtonAddToMainActionPerformed
|
||||
|
||||
private void jButtonRandomCardActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonAddToMainActionPerformed
|
||||
int n = mainTable.getRowCount();
|
||||
mainModel.doubleClick(ThreadLocalRandom.current().nextInt(n), null, false);
|
||||
if (limited) {
|
||||
mainModel.fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void jButtonAddToSideboardActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonAddToSideboardActionPerformed
|
||||
if (mainTable.getSelectedRowCount() > 0) {
|
||||
|
|
@ -1421,7 +1623,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
private void tbColorlessActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tbColorlessActionPerformed
|
||||
filterCardsColor(evt.getModifiers(), evt.getActionCommand());
|
||||
}//GEN-LAST:event_tbColorlessActionPerformed
|
||||
|
||||
|
||||
private void tbLimitColorsActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tbColorlessActionPerformed
|
||||
filterCards();
|
||||
}
|
||||
|
||||
private void tbCreaturesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tbCreaturesActionPerformed
|
||||
filterCardsType(evt.getModifiers(), evt.getActionCommand());
|
||||
}//GEN-LAST:event_tbCreaturesActionPerformed
|
||||
|
|
@ -1465,6 +1671,33 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
private void chkUniqueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkRulesActionPerformed
|
||||
// TODO add your handling code here:
|
||||
}//GEN-LAST:event_chkRulesActionPerformed
|
||||
|
||||
private void btnFoulMagicPresetSet(java.awt.event.ActionEvent evt) {
|
||||
reloadSetsCombobox();
|
||||
if (cbExpansionSet.getItemAt(0).startsWith(MULTI_SETS_SELECTION_TEXT)) {
|
||||
cbExpansionSet.removeItemAt(0);
|
||||
}
|
||||
|
||||
listCodeSelected.uncheckAll();
|
||||
String[] selectedFormats = Constants.foulMagicsSets.get(this.cbFoulMagicPresets.getSelectedIndex()).getValue();
|
||||
if (selectedFormats.length == 1) {
|
||||
this.cbExpansionSet.setSelectedItem(selectedFormats[0]);
|
||||
filterCards();
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> formats = ConstructedFormats.getTypes(false);
|
||||
for (int i = 0; i < formats.size(); i++) {
|
||||
if (Arrays.stream(selectedFormats).anyMatch(formats.get(i)::equals)) {
|
||||
listCodeSelected.setChecked(i - 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
String message = String.format("%s: %s", MULTI_SETS_SELECTION_TEXT, "[Foul Magics]");
|
||||
cbExpansionSet.insertItemAt(message, 0);
|
||||
cbExpansionSet.setSelectedIndex(0);
|
||||
filterCards();
|
||||
}
|
||||
|
||||
private void btnExpansionSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExpansionSearchActionPerformed
|
||||
// search and check multiple items
|
||||
|
|
@ -1505,6 +1738,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
isSetsFilterLoading = false;
|
||||
}
|
||||
|
||||
|
||||
// update data
|
||||
filterCards();
|
||||
});
|
||||
|
|
@ -1565,7 +1799,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
}
|
||||
|
||||
private TableModel mainModel;
|
||||
private TaggerModel tagsModel;
|
||||
private JTable mainTable;
|
||||
private JTable taggerTable;
|
||||
private ICardGrid currentView;
|
||||
|
||||
private final CheckBoxList listCodeSelected;
|
||||
|
|
@ -1575,12 +1811,14 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
private javax.swing.JButton btnBooster;
|
||||
private javax.swing.JButton btnClear;
|
||||
private javax.swing.JButton btnExpansionSearch;
|
||||
private javax.swing.JButton btnFoulMagicPreset;
|
||||
private javax.swing.JLabel cardCount;
|
||||
private javax.swing.JLabel cardCountLabel;
|
||||
private javax.swing.JPanel cardSelectorBottomPanel;
|
||||
private javax.swing.JScrollPane cardSelectorScrollPane;
|
||||
private javax.swing.JComboBox<String> cbExpansionSet;
|
||||
private javax.swing.JComboBox<SortBy> cbSortBy;
|
||||
private javax.swing.JComboBox<SortBy> cbFoulMagicPresets;
|
||||
private javax.swing.JCheckBox chkNames;
|
||||
private javax.swing.JCheckBox chkPennyDreadful;
|
||||
private javax.swing.JCheckBox chkPiles;
|
||||
|
|
@ -1592,6 +1830,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
private javax.swing.JButton jButtonClean;
|
||||
private javax.swing.JButton jButtonRemoveFromMain;
|
||||
private javax.swing.JButton jButtonRemoveFromSideboard;
|
||||
private javax.swing.JButton jButtonRandomCard;
|
||||
private javax.swing.JButton jButtonSearch;
|
||||
private javax.swing.JToolBar.Separator jSeparator1;
|
||||
private javax.swing.JToolBar.Separator jSeparator2;
|
||||
|
|
@ -1607,6 +1846,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
private javax.swing.JToggleButton tbBlue;
|
||||
private javax.swing.JToolBar tbColor;
|
||||
private javax.swing.JToggleButton tbColorless;
|
||||
private javax.swing.JToggleButton tbLimitColors;
|
||||
private javax.swing.JToggleButton tbCommon;
|
||||
private javax.swing.JToggleButton tbCreatures;
|
||||
private javax.swing.JToggleButton tbEnchantments;
|
||||
|
|
@ -1623,6 +1863,17 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
private javax.swing.JToolBar tbTypes;
|
||||
private javax.swing.JToggleButton tbUncommon;
|
||||
private javax.swing.JToggleButton tbWhite;
|
||||
|
||||
private javax.swing.JScrollPane taggerScrollPane;
|
||||
private javax.swing.JPanel taggerControlBar;
|
||||
private javax.swing.JTextField taggerSearch;
|
||||
private javax.swing.JButton taggerReset;
|
||||
|
||||
|
||||
|
||||
private javax.swing.JPanel taggerContainer;
|
||||
private javax.swing.JSplitPane tablePanel;
|
||||
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
private final mage.client.cards.CardGrid cardGrid; // grid for piles view mode (example: selected cards in drafting)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,253 @@
|
|||
package mage.client.deckeditor.table;
|
||||
|
||||
import mage.client.cards.BigCard;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.Tag;
|
||||
import mage.cards.repository.TagRepository;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import java.awt.event.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class TaggerModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = -528008802935423048L;
|
||||
|
||||
private static final Logger log = Logger.getLogger(TableModel.class);
|
||||
|
||||
protected BigCard bigCard;
|
||||
protected UUID gameId;
|
||||
private List<Tag> view = new ArrayList<Tag>();
|
||||
public Map<String, Boolean> filtered = new HashMap<String, Boolean>();
|
||||
public Set<String> whitelist = new HashSet<String>();
|
||||
public Set<String> blacklist = new HashSet<String>();
|
||||
|
||||
private final String[] column = {"Inc", "Exc", "Name", "Description", "# Cards"};
|
||||
|
||||
private boolean descending = false;
|
||||
private int columnSortedBy = 0;
|
||||
private int recentSortedColumn;
|
||||
private boolean recentAscending;
|
||||
private Runnable doSort;
|
||||
private int whitelistCount = 0;
|
||||
|
||||
|
||||
public TaggerModel(Runnable sortFn) {
|
||||
doSort = sortFn;
|
||||
view = TagRepository.instance.getAllTags();
|
||||
if (view.size() == 0) {
|
||||
Tag placeholder = new Tag();
|
||||
placeholder.id = "";
|
||||
placeholder.description = "Please download Scryfall tags from the 'download' tab & reopen";
|
||||
placeholder.label = "Data missing!";
|
||||
|
||||
Tag placeholder2 = new Tag();
|
||||
placeholder2.id = "";
|
||||
placeholder2.description = "You may need do a Scryfall download to associate IDs";
|
||||
placeholder2.label = "Also!";
|
||||
view.add(placeholder2);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
view = TagRepository.instance.getAllTags();
|
||||
filtered.clear();
|
||||
this.whitelist.clear();
|
||||
this.blacklist.clear();
|
||||
fireTableDataChanged();
|
||||
doSort.run();
|
||||
}
|
||||
|
||||
public void search(String query) {
|
||||
view = TagRepository.instance.searchTags(query);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return view.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return column.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int n) {
|
||||
return column[n];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row, int column) {
|
||||
Tag tag = view.get(row);
|
||||
switch (column) {
|
||||
case 0:
|
||||
return filtered.getOrDefault(tag.id, false) == true ? "X" : "";
|
||||
case 1:
|
||||
return filtered.getOrDefault(tag.id, true) == false ? "X" : "";
|
||||
case 2:
|
||||
return view.get(row).label;
|
||||
case 3:
|
||||
return view.get(row).description;
|
||||
case 4:
|
||||
return Long.toString(TagRepository.instance.getTagCardCount(view.get(row)));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public void doubleClick(int index) {
|
||||
Tag tag = view.get(index);
|
||||
if (!filtered.containsKey(tag.id)) {
|
||||
filtered.put(tag.id, true);
|
||||
} else if (filtered.get(tag.id)) {
|
||||
filtered.put(tag.id, false);
|
||||
} else {
|
||||
filtered.remove(tag.id);
|
||||
}
|
||||
fireTableCellUpdated(index, 0);
|
||||
fireTableCellUpdated(index, 1);
|
||||
buildBlacklist();
|
||||
buildWhitelist();
|
||||
doSort.run();
|
||||
}
|
||||
|
||||
public void addListeners(final JTable table) {
|
||||
// sorts
|
||||
MouseListener mouse = new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (!SwingUtilities.isLeftMouseButton(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TableColumnModel columnModel = table.getColumnModel();
|
||||
|
||||
int viewColumn = columnModel.getColumnIndexAtX(e.getX());
|
||||
int column = table.convertColumnIndexToModel(viewColumn);
|
||||
|
||||
if (column != -1) {
|
||||
descending = !descending;
|
||||
columnSortedBy = column;
|
||||
sort();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
table.getTableHeader().addMouseListener(mouse);
|
||||
|
||||
// updates card detail, listens to any mouse clicks
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (!SwingUtilities.isLeftMouseButton(e)) {
|
||||
return;
|
||||
}
|
||||
if (e.getClickCount() % 2 == 0) {
|
||||
doubleClick(table.getSelectedRow());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean sort() {
|
||||
switch (columnSortedBy) {
|
||||
// I do not care.
|
||||
case 0:
|
||||
view.sort((t1, t2) -> (filtered.getOrDefault(t1.id, false) == true ? "X" : "").compareTo((filtered.getOrDefault(t2.id, false) == true ? "X" : "")));
|
||||
break;
|
||||
case 1:
|
||||
view.sort((t1, t2) -> (filtered.getOrDefault(t1.id, true) == false ? "X" : "").compareTo((filtered.getOrDefault(t2.id, true) == false ? "X" : "")));
|
||||
break;
|
||||
case 2:
|
||||
view.sort((t1, t2) -> ObjectUtils.compare(t1.label, t2.label));
|
||||
break;
|
||||
case 3:
|
||||
view.sort((t1, t2) -> ObjectUtils.compare(t1.description, t2.description));
|
||||
break;
|
||||
case 4:
|
||||
view.sort((t1, t2) -> TagRepository.instance.getTagCardCount(t2) - TagRepository.instance.getTagCardCount(t1));
|
||||
break;
|
||||
}
|
||||
|
||||
if (descending) {
|
||||
Collections.reverse(view);
|
||||
}
|
||||
|
||||
fireTableDataChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> buildWhitelist() {
|
||||
whitelist.clear();
|
||||
whitelistCount = 0;
|
||||
// whitelists are a bit weird, each one needs to reduce the next
|
||||
Set<String> temp = new HashSet<String>();
|
||||
|
||||
boolean fresh = true;
|
||||
|
||||
for (String key : filtered.keySet()) {
|
||||
temp.clear();
|
||||
if (filtered.get(key)) {
|
||||
whitelistCount++;
|
||||
for (CardInfo card : TagRepository.instance.getCardsByTagId(key)) {
|
||||
temp.add(card.getName());
|
||||
}
|
||||
if (fresh) {
|
||||
whitelist.addAll(temp);
|
||||
} else {
|
||||
whitelist.retainAll(temp);
|
||||
}
|
||||
fresh = false;
|
||||
}
|
||||
}
|
||||
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
|
||||
public Set<String> buildBlacklist() {
|
||||
blacklist.clear();
|
||||
for (String key : filtered.keySet()) {
|
||||
if (!filtered.get(key)) {
|
||||
for (CardInfo card : TagRepository.instance.getCardsByTagId(key)) {
|
||||
blacklist.remove(card.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
public boolean testCard(Card card) {
|
||||
if (!hasAnySelected()) {
|
||||
return true;
|
||||
}
|
||||
return !blacklist.contains(card.getName()) && (whitelist.size() == 0 || whitelist.contains(card.getName()));
|
||||
}
|
||||
|
||||
|
||||
public boolean hasAnySelected() {
|
||||
return filtered.size() > 0;
|
||||
}
|
||||
|
||||
public int getRecentSortedColumn() {
|
||||
return recentSortedColumn;
|
||||
}
|
||||
|
||||
public boolean isRecentAscending() {
|
||||
return recentAscending;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -329,6 +329,7 @@ public class ConnectDialog extends MageDialog {
|
|||
});
|
||||
|
||||
btnFindBeta.setText("BETA");
|
||||
btnFindBeta.setEnabled(false);
|
||||
btnFindBeta.setToolTipText("Connect to BETA server, AI disabled (use any username without registration)");
|
||||
btnFindBeta.setAlignmentY(0.0F);
|
||||
btnFindBeta.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ public class DownloadImagesDialog extends MageDialog {
|
|||
comboSets = new javax.swing.JComboBox<>();
|
||||
fillerMode1 = new javax.swing.Box.Filler(new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 32767));
|
||||
buttonSearchSet = new javax.swing.JButton();
|
||||
|
||||
panelRedownload = new javax.swing.JPanel();
|
||||
checkboxRedownload = new javax.swing.JCheckBox();
|
||||
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 3), new java.awt.Dimension(32767, 5));
|
||||
|
|
@ -354,8 +355,9 @@ public class DownloadImagesDialog extends MageDialog {
|
|||
buttonSearchSetActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
panelModeSelect.add(buttonSearchSet);
|
||||
|
||||
|
||||
panelModeInner.add(panelModeSelect);
|
||||
|
||||
panelMode.add(panelModeInner);
|
||||
|
|
@ -426,7 +428,7 @@ public class DownloadImagesDialog extends MageDialog {
|
|||
private void buttonSearchSetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSearchSetActionPerformed
|
||||
FastSearchUtil.showFastSearchForStringComboBox(comboSets, FastSearchUtil.DEFAULT_EXPANSION_SEARCH_MESSAGE, 400, 500);
|
||||
}//GEN-LAST:event_buttonSearchSetActionPerformed
|
||||
|
||||
|
||||
private void buttonStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonStopActionPerformed
|
||||
// TODO implement stop feature for cancel button
|
||||
}//GEN-LAST:event_buttonStopActionPerformed
|
||||
|
|
|
|||
|
|
@ -1332,6 +1332,11 @@
|
|||
<Property name="text" type="java.lang.String" value="set to default"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="cbVerticalLayout">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Vertical Layout (Experimental)"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="panelSizeDetailedSettings">
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
public static final String KEY_GAME_USE_PROFANITY_FILTER = "gameUseProfanityFilter";
|
||||
|
||||
// size settings
|
||||
public static final String KEY_GUI_VERTICAL_LAYOUT = "guiVerticalLayoyut";
|
||||
|
||||
|
||||
public static final String KEY_GUI_CARD_BATTLEFIELD_SIZE = "guiCardBattlefieldSize";
|
||||
public static final String KEY_GUI_CARD_HAND_SIZE = "guiCardHandSize";
|
||||
public static final String KEY_GUI_CARD_EDITOR_SIZE = "guiCardEditorSize";
|
||||
|
|
@ -865,6 +868,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
buttonSizeDefault4 = new javax.swing.JButton();
|
||||
buttonSizeDefault5 = new javax.swing.JButton();
|
||||
buttonSizeDefault6 = new javax.swing.JButton();
|
||||
cbVerticalLayout = new javax.swing.JCheckBox();
|
||||
panelSizeDetailedSettings = new javax.swing.JPanel();
|
||||
labelSizeGroup1 = new javax.swing.JLabel();
|
||||
panelSize1 = new javax.swing.JPanel();
|
||||
|
|
@ -1735,6 +1739,9 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
|
||||
buttonSizeDefault6.setText("set to default");
|
||||
panelSizeDefaultSettings.add(buttonSizeDefault6);
|
||||
|
||||
cbVerticalLayout.setText("Vertical Layout (Experimental)");
|
||||
panelSizeDetailedSettings.add(cbVerticalLayout);
|
||||
|
||||
panelSizeDetailedSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Detailed settings"));
|
||||
panelSizeDetailedSettings.setLayout(new java.awt.GridLayout(16, 1));
|
||||
|
|
@ -3023,6 +3030,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
prefs.putInt(paramName, paramValue);
|
||||
updateCache(paramName, Integer.toString(paramValue));
|
||||
}
|
||||
// Hopefully this works
|
||||
save(prefs, dialog.cbVerticalLayout, KEY_GUI_VERTICAL_LAYOUT, "true", "false");
|
||||
|
||||
saveGUISize(false, false);
|
||||
|
||||
|
|
@ -3448,6 +3457,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
load(prefs, dialog.cbDraftLogAutoSave, KEY_DRAFT_LOG_AUTO_SAVE, "true");
|
||||
load(prefs, dialog.cbLimitedDeckAutoSave, KEY_LIMITED_DECK_AUTO_SAVE, "true");
|
||||
load(prefs, dialog.cbGameJsonLogAutoSave, KEY_JSON_GAME_LOG_AUTO_SAVE, "true", "false");
|
||||
|
||||
load(prefs, dialog.cbVerticalLayout, KEY_GUI_VERTICAL_LAYOUT, "true", "false");
|
||||
|
||||
String autoTargetParam;
|
||||
try {
|
||||
|
|
@ -4101,6 +4112,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
private javax.swing.JCheckBox cbUseDefaultImageFolder;
|
||||
private javax.swing.JCheckBox cbUseRandomBattleImage;
|
||||
private javax.swing.JCheckBox cbUseSameSettingsForReplacementEffect;
|
||||
private javax.swing.JCheckBox cbVerticalLayout;
|
||||
private javax.swing.JCheckBox checkBoxBeforeCOthers;
|
||||
private javax.swing.JCheckBox checkBoxBeforeCYou;
|
||||
private javax.swing.JCheckBox checkBoxDrawOthers;
|
||||
|
|
|
|||
|
|
@ -131,6 +131,8 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
private final Map<String, MageSplitter> splitters = new LinkedHashMap<>(); // settings key, splitter
|
||||
// do not save splitters in intermediate state, e.g. connection to new server with active game
|
||||
private boolean isSplittersFullyRestored = false;
|
||||
|
||||
private boolean vertical = false;
|
||||
|
||||
public static class MageSplitter {
|
||||
JSplitPane splitPane;
|
||||
|
|
@ -236,6 +238,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
|
||||
public GamePanel() {
|
||||
this.vertical = PreferencesDialog.getCachedValue(KEY_GUI_VERTICAL_LAYOUT, "false").equals("true");
|
||||
initComponents = true;
|
||||
initComponents();
|
||||
|
||||
|
|
@ -267,9 +270,16 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
pnlCommandsSkipAndStack.add(pnlShortCuts, BorderLayout.NORTH);
|
||||
pnlCommandsSkipAndStack.add(stackObjects, BorderLayout.CENTER);
|
||||
// ... split: feedback + hand <|> skip + stack
|
||||
splitHandAndStack.setLeftComponent(pnlCommandsFeedbackAndHand);
|
||||
splitHandAndStack.setRightComponent(pnlCommandsSkipAndStack);
|
||||
splitHandAndStack.setResizeWeight(DIVIDER_KEEP_RIGHT_COMPONENT);
|
||||
if (vertical) {
|
||||
splitHandAndStack.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
|
||||
splitHandAndStack.setBottomComponent(pnlCommandsFeedbackAndHand);
|
||||
splitHandAndStack.setTopComponent(pnlCommandsSkipAndStack);
|
||||
splitHandAndStack.setResizeWeight(DIVIDER_KEEP_RIGHT_COMPONENT);
|
||||
} else {
|
||||
splitHandAndStack.setLeftComponent(pnlCommandsFeedbackAndHand);
|
||||
splitHandAndStack.setRightComponent(pnlCommandsSkipAndStack);
|
||||
splitHandAndStack.setResizeWeight(DIVIDER_KEEP_RIGHT_COMPONENT);
|
||||
}
|
||||
pnlCommandsFeedbackAndHand.setMinimumSize(new Dimension(0, 0)); // allow any sizes for hand
|
||||
pnlCommandsSkipAndStack.setMinimumSize(new Dimension(0, 0)); // allow any sizes for stack
|
||||
// ... all
|
||||
|
|
@ -310,7 +320,9 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
final JLayeredPane jLayeredBackgroundPane = new JLayeredPane();
|
||||
jLayeredBackgroundPane.setSize(1024, 768);
|
||||
this.add(jLayeredBackgroundPane);
|
||||
jLayeredBackgroundPane.add(splitGameAndBigCard, JLayeredPane.DEFAULT_LAYER);
|
||||
var basePane = this.vertical ? splitBattlefieldAndChats : splitGameAndBigCard;
|
||||
jLayeredBackgroundPane.add(basePane, JLayeredPane.DEFAULT_LAYER);
|
||||
|
||||
|
||||
Map<String, JComponent> myUi = getUIComponents(jLayeredBackgroundPane);
|
||||
Plugins.instance.updateGamePanel(myUi);
|
||||
|
|
@ -322,7 +334,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
int width = ((JComponent) e.getSource()).getWidth();
|
||||
int height = ((JComponent) e.getSource()).getHeight();
|
||||
jLayeredBackgroundPane.setSize(width, height);
|
||||
splitGameAndBigCard.setSize(width, height);
|
||||
basePane.setSize(width, height);
|
||||
|
||||
if (height < storedHeight) {
|
||||
// TODO: wtf, is it needs? Research and delete that code with storedHeight
|
||||
|
|
@ -560,7 +572,11 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
float guiScale = GUISizeHelper.dialogGuiScale;
|
||||
int hGap = GUISizeHelper.guiSizeScale(SKIP_BUTTONS_SPACE_H, guiScale);
|
||||
int vGap = GUISizeHelper.guiSizeScale(SKIP_BUTTONS_SPACE_V, guiScale);
|
||||
pnlShortCuts.setLayout(new FlowLayout(FlowLayout.RIGHT, hGap, vGap));
|
||||
if (vertical) {
|
||||
pnlShortCuts.setLayout(new FlowLayout(FlowLayout.CENTER, 1, vGap));
|
||||
} else {
|
||||
pnlShortCuts.setLayout(new FlowLayout(FlowLayout.RIGHT, hGap, vGap));
|
||||
}
|
||||
// skip buttons - sizes
|
||||
Dimension strictSize = new Dimension(2 * GUISizeHelper.gameCommandButtonHeight, GUISizeHelper.gameCommandButtonHeight);
|
||||
setSkipButtonSize(btnCancelSkip, guiScale, strictSize);
|
||||
|
|
@ -2277,7 +2293,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initComponents() {
|
||||
private boolean initComponents() {
|
||||
abilityPicker = new mage.client.components.ability.AbilityPicker(GUISizeHelper.dialogGuiScale);
|
||||
pnlHelperHandButtonsStackArea = new javax.swing.JPanel();
|
||||
pnlShortCuts = new javax.swing.JPanel();
|
||||
|
|
@ -2361,7 +2377,11 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
|
||||
// split: chat <|> game logs
|
||||
splitChatAndLogs = new javax.swing.JSplitPane();
|
||||
splitChatAndLogs.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
|
||||
if (vertical) {
|
||||
splitChatAndLogs.setOrientation(javax.swing.JSplitPane.HORIZONTAL_SPLIT);
|
||||
} else {
|
||||
splitChatAndLogs.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
|
||||
}
|
||||
splitChatAndLogs.setResizeWeight(DIVIDER_KEEP_LEFT_COMPONENT);
|
||||
splitChatAndLogs.setTopComponent(userChatPanel);
|
||||
splitChatAndLogs.setBottomComponent(gameChatPanel);
|
||||
|
|
@ -2371,8 +2391,15 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
splitBattlefieldAndChats.setBorder(null);
|
||||
splitBattlefieldAndChats.setResizeWeight(DIVIDER_KEEP_RIGHT_COMPONENT);
|
||||
splitBattlefieldAndChats.setOneTouchExpandable(true);
|
||||
splitBattlefieldAndChats.setLeftComponent(pnlHelperHandButtonsStackArea);
|
||||
splitBattlefieldAndChats.setRightComponent(splitChatAndLogs);
|
||||
|
||||
if (vertical) {
|
||||
splitBattlefieldAndChats.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
|
||||
splitBattlefieldAndChats.setBottomComponent(pnlHelperHandButtonsStackArea);
|
||||
splitBattlefieldAndChats.setTopComponent(splitChatAndLogs);
|
||||
} else {
|
||||
splitBattlefieldAndChats.setLeftComponent(pnlHelperHandButtonsStackArea);
|
||||
splitBattlefieldAndChats.setRightComponent(splitChatAndLogs);
|
||||
}
|
||||
|
||||
// warning, it's important to store/restore splitters in same order as real life GUI
|
||||
// from outer to inner (otherwise panels will be hidden or weird)
|
||||
|
|
@ -2755,7 +2782,10 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
|
||||
// split: game <|> chat/log
|
||||
splitGameAndBigCard.setLeftComponent(splitBattlefieldAndChats);
|
||||
splitGameAndBigCard.setRightComponent(bigCardPanel);
|
||||
if (!vertical) {
|
||||
splitGameAndBigCard.setRightComponent(bigCardPanel);
|
||||
}
|
||||
return vertical;
|
||||
}
|
||||
|
||||
private void removeListener() {
|
||||
|
|
@ -2823,7 +2853,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
this.btnSkipForward.removeActionListener(al);
|
||||
}
|
||||
|
||||
final BasicSplitPaneUI myUi = (BasicSplitPaneUI) splitGameAndBigCard.getUI();
|
||||
final BasicSplitPaneUI myUi = vertical ? (BasicSplitPaneUI) splitBattlefieldAndChats.getUI() : (BasicSplitPaneUI) splitGameAndBigCard.getUI();
|
||||
final BasicSplitPaneDivider divider = myUi.getDivider();
|
||||
final JButton upArrowButton = (JButton) divider.getComponent(0);
|
||||
for (ActionListener al : upArrowButton.getActionListeners()) {
|
||||
|
|
|
|||
|
|
@ -893,7 +893,7 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
formatFilterList.add(RowFilter.regexFilter("^Oathbreaker", TablesTableModel.COLUMN_DECK_TYPE));
|
||||
}
|
||||
if (btnFormatLimited.isSelected()) {
|
||||
formatFilterList.add(RowFilter.regexFilter("^Limited", TablesTableModel.COLUMN_DECK_TYPE));
|
||||
formatFilterList.add(RowFilter.regexFilter("^(?:(?:Unl)|L)imited", 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|^Constructed - Freeform Unlimited|^Australian Highlander|^European Highlander|^Canadian Highlander|^Constructed - Old|^Constructed - Historic", TablesTableModel.COLUMN_DECK_TYPE));
|
||||
|
|
|
|||
|
|
@ -18,5 +18,7 @@ public enum ClientEventType {
|
|||
DRAFT_PICK_CARD,
|
||||
DRAFT_MARK_CARD,
|
||||
//
|
||||
PLAYER_TYPE_CHANGED
|
||||
PLAYER_TYPE_CHANGED,
|
||||
//
|
||||
TAG_DOUBLE_CLICK
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ public class GathererSets implements Iterable<DownloadJob> {
|
|||
"NEC", "YNEO", "NEO", "SNC", "NCC", "CLB", "2X2", "DMU", "DMC", "40K", "GN3",
|
||||
"UNF", "BRO", "BRC", "BOT", "J22", "DMR", "ONE", "ONC", "SCH",
|
||||
"MOM", "MOC", "MUL", "MAT", "LTR", "CMM", "WOE", "WHO", "RVR", "WOT",
|
||||
"WOC", "SPG", "LCI", "LCC", "REX", "PIP", "MKM", "MKC", "CLU", "OTJ",
|
||||
"WOC", "SPG", "LCI", "LCC", "REX", "PIP", "YMKM", "MKM", "MKC", "CLU", "OTJ",
|
||||
"OTC", "OTP", "BIG", "MH3", "M3C", "ACR", "BLB", "BLC", "DSK", "DSC",
|
||||
"MB2", "FDN", "INR", "J25", "DRC", "DFT", "TDC", "TDM", "FCA", "FIC",
|
||||
"FIN", "SIS", "SIR", "SLD", "AKR", "MD1", "ANB", "LTC", "BRR", "HA1",
|
||||
|
|
@ -182,6 +182,7 @@ public class GathererSets implements Iterable<DownloadJob> {
|
|||
codeReplacements.put("WTH", "WL");
|
||||
codeReplacements.put("YMID", "Y22");
|
||||
codeReplacements.put("YNEO", "Y22NEO");
|
||||
|
||||
}
|
||||
|
||||
public GathererSets() {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class ScryfallApiCard {
|
|||
transient public String imageLarge = "";
|
||||
|
||||
// potentially interesting fields, can be used in other places
|
||||
//public UUID oracle_id; // TODO: implement card hint with oracle/cr ruling texts (see Rulings bulk data)
|
||||
public String oracle_id; // TODO: implement card hint with oracle/cr ruling texts (see Rulings bulk data)
|
||||
//public Integer edhrec_rank; // TODO: use it to rating cards for AI and draft bots
|
||||
//public Object legalities; // TODO: add verify check for bans list
|
||||
//public Boolean full_art; // TODO: add verify check for full art usage in sets
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.google.gson.JsonObject;
|
|||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import mage.MageException;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import mage.client.util.CardLanguage;
|
||||
import mage.util.JsonUtil;
|
||||
|
|
@ -477,7 +478,17 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
|
||||
// prepare data
|
||||
// memory optimization: fewer data, from 1145 MB to 470 MB
|
||||
card.prepareCompatibleData();
|
||||
try {
|
||||
card.prepareCompatibleData();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to process card: ".concat(card.name));
|
||||
logger.warn("Skipping...");
|
||||
continue;
|
||||
}
|
||||
|
||||
// I LOVE POTENTIAL SQL INJECTION!!!!!
|
||||
CardRepository.instance.execSQL("UPDATE card SET oracleId = '"+ card.oracle_id +"' WHERE setCode = '"+ card.set +"' AND cardNumber = '"+ card.collector_number +"'");
|
||||
|
||||
|
||||
// keep only usefully languages
|
||||
// memory optimization: fewer items, from 470 MB to 96 MB
|
||||
|
|
|
|||
|
|
@ -2,10 +2,16 @@ package org.mage.plugins.card.dl.sources;
|
|||
|
||||
import org.tritonus.share.ArraySet;
|
||||
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.CardWithHalves;
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.Sets;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
|
@ -570,6 +576,7 @@ public class ScryfallImageSupportCards {
|
|||
add("RVR"); // Ravnica Remastered
|
||||
add("PL24"); // Year of the Dragon 2024
|
||||
add("PIP"); // Fallout
|
||||
add("YMKM"); // Alchemy: Murders at Karlov Manor
|
||||
add("MKM"); // Murders at Karlov Manor
|
||||
add("MKC"); // Murders at Karlov Manor Commander
|
||||
add("PSS4"); // MKM Standard Showdown
|
||||
|
|
@ -624,6 +631,9 @@ public class ScryfallImageSupportCards {
|
|||
|
||||
// Custom sets using Scryfall images - must provide a direct link for each card in directDownloadLinks
|
||||
add("CALC"); // Custom Alchemized versions of existing cards
|
||||
|
||||
// Foul Magics sets
|
||||
add("FMB3E");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -771,6 +781,32 @@ public class ScryfallImageSupportCards {
|
|||
put("ECL/Steam Vents/348b", "https://api.scryfall.com/cards/ecl/348/en?format=image&face=back");
|
||||
put("ECL/Temple Garden/351b", "https://api.scryfall.com/cards/ecl/351/en?format=image&face=back");
|
||||
|
||||
for (ExpansionSet set : Sets.getInstance().values()) {
|
||||
for (Integer key : set.cardAliases.keySet()) {
|
||||
var value = set.cardAliases.get(key);
|
||||
|
||||
put(
|
||||
String.format("%s/%s/%s", set.getCode(), value.cardInfo.getName(), value.cardInfo.getCardNumber()),
|
||||
String.format("https://api.scryfall.com/cards/%s/%s/en?face=front&format=image", value.targetSet.toLowerCase(), value.targetSetNumber)
|
||||
);
|
||||
if (CardWithHalves.class.isAssignableFrom(value.cardInfo.getCardClass())) {
|
||||
try {
|
||||
UUID uid = UUID.randomUUID();
|
||||
CardSetInfo info = new CardSetInfo(set.getName(), set.getCode(), value.targetSet, value.cardInfo.getRarity());
|
||||
CardWithHalves backCard = (CardWithHalves) value.cardInfo.getCardClass().getDeclaredConstructor(UUID.class, CardSetInfo.class).newInstance(
|
||||
uid, info);
|
||||
|
||||
put(
|
||||
String.format("%s/%s/%s", set.getCode(), backCard.getRightHalfCard().getName(), value.cardInfo.getCardNumber()),
|
||||
String.format("https://api.scryfall.com/cards/%s/%s/en?face=back", value.targetSet.toLowerCase(), value.targetSetNumber)
|
||||
);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -794,6 +830,7 @@ public class ScryfallImageSupportCards {
|
|||
if (directDownloadLinks.containsKey(linkCode2)) {
|
||||
return linkCode2;
|
||||
}
|
||||
|
||||
|
||||
// default
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import mage.cards.repository.Tag;
|
||||
import mage.cards.repository.TagRepository;
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
|
||||
class RawTag {
|
||||
public String object;
|
||||
public String id;
|
||||
public String label;
|
||||
public String type;
|
||||
public String description;
|
||||
public List<String> oracle_ids;
|
||||
}
|
||||
|
||||
class OracleResponse {
|
||||
public String object;
|
||||
public boolean has_more;
|
||||
public List<RawTag> data;
|
||||
}
|
||||
|
||||
public enum TagSource {
|
||||
instance;
|
||||
private static final Logger LOGGER = Logger.getLogger(TagSource.class);
|
||||
|
||||
public void syncTagRepositiory() {
|
||||
String oracle = XmageURLConnection.downloadText("https://api.scryfall.com/private/tags/oracle");
|
||||
OracleResponse response = new Gson().fromJson(oracle, OracleResponse.class);
|
||||
String tagCount = Integer.toString(response.data.size());
|
||||
int i = 0;
|
||||
for (RawTag rawTag : response.data) {
|
||||
LOGGER.debug("Syncing tag: " + rawTag.label + " (" + Integer.toString(i) + "/" + tagCount + ")");
|
||||
Tag tag = new Tag();
|
||||
tag.id = rawTag.id;
|
||||
tag.description = rawTag.description;
|
||||
tag.label = rawTag.label;
|
||||
TagRepository.instance.syncTag(tag, rawTag.oracle_ids);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
BIN
Mage.Client/src/main/resources/buttons/brick.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
Mage.Client/src/main/resources/buttons/dice.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Mage.Client/src/main/resources/buttons/lock.png
Normal file
|
After Width: | Height: | Size: 304 B |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 595 B After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 831 B After Width: | Height: | Size: 5 KiB |
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 6.3 KiB |
|
|
@ -0,0 +1,30 @@
|
|||
package mage.deck;
|
||||
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.cards.decks.DeckValidator;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class Unlimited extends DeckValidator {
|
||||
|
||||
public Unlimited() {
|
||||
super("Unlimited", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeckMinSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSideboardMinSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(Deck deck) {
|
||||
boolean valid = true;
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
socketWriteTimeout="10000"
|
||||
maxGameThreads="10"
|
||||
maxSecondsIdle="300"
|
||||
minUserNameLength="3"
|
||||
minUserNameLength="1"
|
||||
maxUserNameLength="14"
|
||||
invalidUserNamePattern="[^a-z0-9_]"
|
||||
minPasswordLength="8"
|
||||
|
|
@ -63,6 +63,8 @@
|
|||
mailUser=""
|
||||
mailPassword=""
|
||||
mailFromAddress=""
|
||||
httpAuth="false"
|
||||
authUrl=""
|
||||
/>
|
||||
<playerTypes>
|
||||
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
|
||||
|
|
@ -225,5 +227,6 @@
|
|||
<deckType name="Block Constructed - Zendikar" jar="mage-deck-constructed.jar" className="mage.deck.ZendikarBlock"/>
|
||||
<deckType name="Block Constructed Custom - Star Wars" jar="mage-deck-constructed.jar" className="mage.deck.StarWarsBlock"/>
|
||||
<deckType name="Limited" jar="mage-deck-limited.jar" className="mage.deck.Limited"/>
|
||||
<deckType name="Unlimited" jar="mage-deck-limited.jar" className="mage.deck.Unlimited"/>
|
||||
</deckTypes>
|
||||
</config>
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@
|
|||
mailUser=""
|
||||
mailPassword=""
|
||||
mailFromAddress=""
|
||||
httpAuth="false"
|
||||
authUrl=""
|
||||
/>
|
||||
<playerTypes>
|
||||
<playerType name="Human" jar="mage-player-human-${project.version}.jar" className="mage.player.human.HumanPlayer"/>
|
||||
|
|
@ -219,5 +221,6 @@
|
|||
<deckType name="Block Constructed - Zendikar" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.ZendikarBlock"/>
|
||||
<deckType name="Block Constructed Custom - Star Wars" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.StarWarsBlock"/>
|
||||
<deckType name="Limited" jar="mage-deck-limited-${project.version}.jar" className="mage.deck.Limited"/>
|
||||
<deckType name="Unlimited" jar="mage-deck-limited-${project.version}.jar" className="mage.deck.Unlimited"/>
|
||||
</deckTypes>
|
||||
</config>
|
||||
|
|
@ -1,36 +1,36 @@
|
|||
#SAMPLE SERVER CONFIG (you must enable it by command line)
|
||||
|
||||
#default log level and active appenders (dest for logs)
|
||||
log4j.rootLogger=info, console, logfile
|
||||
|
||||
#custom log level for java classes
|
||||
log4j.logger.com.j256.ormlite=warn
|
||||
#log4j.logger.mage.player.ai=warn
|
||||
|
||||
#console log
|
||||
log4j.appender.console=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.console.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.console.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.console.Threshold=info
|
||||
|
||||
#file log - without rolling
|
||||
log4j.appender.logfile=org.apache.log4j.FileAppender
|
||||
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.logfile.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.logfile.File=mageserver.log
|
||||
|
||||
#file log - rolling by index
|
||||
log4j.appender.logfileByIndex=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.logfileByIndex.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.logfileByIndex.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.logfileByIndex.File=mageserver.log
|
||||
log4j.appender.logfileByIndex.MaxFileSize=10MB
|
||||
log4j.appender.logfileByIndex.MaxBackupIndex=5
|
||||
log4j.appender.logfileByIndex.append=true
|
||||
|
||||
#file log - rolling by dayly
|
||||
log4j.appender.logfileByDayly=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.logfileByDayly.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.logfileByDayly.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.logfileByDayly.File=mageserver.log
|
||||
#SAMPLE SERVER CONFIG (you must enable it by command line)
|
||||
|
||||
#default log level and active appenders (dest for logs)
|
||||
log4j.rootLogger=info, console, logfile
|
||||
|
||||
#custom log level for java classes
|
||||
log4j.logger.com.j256.ormlite=warn
|
||||
#log4j.logger.mage.player.ai=warn
|
||||
|
||||
#console log
|
||||
log4j.appender.console=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.console.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.console.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.console.Threshold=info
|
||||
|
||||
#file log - without rolling
|
||||
log4j.appender.logfile=org.apache.log4j.FileAppender
|
||||
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.logfile.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.logfile.File=mageserver.log
|
||||
|
||||
#file log - rolling by index
|
||||
log4j.appender.logfileByIndex=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.logfileByIndex.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.logfileByIndex.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.logfileByIndex.File=mageserver.log
|
||||
log4j.appender.logfileByIndex.MaxFileSize=10MB
|
||||
log4j.appender.logfileByIndex.MaxBackupIndex=5
|
||||
log4j.appender.logfileByIndex.append=true
|
||||
|
||||
#file log - rolling by dayly
|
||||
log4j.appender.logfileByDayly=org.apache.log4j.DailyRollingFileAppender
|
||||
log4j.appender.logfileByDayly.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.logfileByDayly.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M %n
|
||||
log4j.appender.logfileByDayly.File=mageserver.log
|
||||
log4j.appender.logfileByDayly.DatePattern='.'yyyy-MM-dd
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
grant {
|
||||
permission java.security.AllPermission;
|
||||
grant {
|
||||
permission java.security.AllPermission;
|
||||
};
|
||||
|
|
@ -64,6 +64,7 @@ public class AuthorizedUser {
|
|||
public boolean doCredentialsMatch(String name, String password) {
|
||||
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(this.hashAlgorithm);
|
||||
matcher.setHashIterations(this.hashIterations);
|
||||
|
||||
AuthenticationToken token = new UsernamePasswordToken(name, password);
|
||||
AuthenticationInfo info = new SimpleAuthenticationInfo(this.name,
|
||||
ByteSource.Util.bytes(Base64.decode(this.password)),
|
||||
|
|
|
|||
|
|
@ -371,6 +371,7 @@ public final class Main {
|
|||
if (throwable instanceof ClientDisconnectedException) {
|
||||
// client called a disconnect command (full disconnect without tables keep)
|
||||
// no need to keep session
|
||||
// the above i think is a lie
|
||||
logger.info("CLIENT DISCONNECTED - " + sessionInfo);
|
||||
logger.debug("- cause: client called disconnect command");
|
||||
managerFactory.sessionManager().disconnect(client.getSessionId(), DisconnectReason.LostConnection, true);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,17 @@ import org.jboss.remoting.callback.Callback;
|
|||
import org.jboss.remoting.callback.HandleCallbackException;
|
||||
import org.jboss.remoting.callback.InvokerCallbackHandler;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
|
@ -153,8 +164,8 @@ public class Session {
|
|||
if (userName.length() > config.getMaxUserNameLength()) {
|
||||
return "User name may not be longer than " + config.getMaxUserNameLength() + " characters";
|
||||
}
|
||||
if (userName.length() <= 2) {
|
||||
return "User name is too short (2 characters or fewer)";
|
||||
if (userName.length() <= 1) {
|
||||
return "User name is too short (1 characters or fewer)";
|
||||
}
|
||||
if (userName.length() >= 250) {
|
||||
return "User name is too long (250 characters or more)";
|
||||
|
|
@ -243,6 +254,7 @@ public class Session {
|
|||
|
||||
// find auth user
|
||||
AuthorizedUser authorizedUser = null;
|
||||
|
||||
if (managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
authorizedUser = AuthorizedUserRepository.getInstance().getByName(userName);
|
||||
String errorMsg = "Wrong username or password. You must register your account first.";
|
||||
|
|
@ -268,6 +280,51 @@ public class Session {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (managerFactory.configSettings().isHttpAuth()) {
|
||||
try {
|
||||
JsonObject body = new JsonObject();
|
||||
body.addProperty("token", password);
|
||||
body.addProperty("username", userName);
|
||||
|
||||
String json = body.toString();
|
||||
|
||||
URL url = new URL(managerFactory.configSettings().getAuthUrl());
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("Content-Length", Integer.toString(json.length()));
|
||||
conn.setRequestProperty("User-Agent", "Tainted-Mage/1.0");
|
||||
|
||||
conn.setDoOutput(true);
|
||||
|
||||
|
||||
OutputStream os = conn.getOutputStream();
|
||||
os.write(json.getBytes());
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
String resp = in.readLine();
|
||||
in.close();
|
||||
JsonElement response = JsonParser.parseString(resp);
|
||||
if (response.isJsonObject() && response.getAsJsonObject().has("success") && response.getAsJsonObject().get("success").getAsBoolean()) {
|
||||
// s'all good, man
|
||||
} else {
|
||||
return "Failed to authenticate";
|
||||
}
|
||||
} else {
|
||||
return "Failed to authenticate: " + Integer.toString(responseCode);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "Error with external authentication. Please try again later.";
|
||||
}
|
||||
}
|
||||
|
||||
// create new user instance (auth or anon)
|
||||
boolean isReconnection = false;
|
||||
|
|
|
|||
|
|
@ -69,4 +69,8 @@ public interface ConfigSettings {
|
|||
List<Plugin> getDraftCubes();
|
||||
|
||||
List<Plugin> getDeckTypes();
|
||||
|
||||
boolean isHttpAuth();
|
||||
|
||||
String getAuthUrl();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ public enum UserStatsRepository {
|
|||
|
||||
TableUtils.createTableIfNotExists(connectionSource, UserStats.class);
|
||||
statsDao = DaoManager.createDao(connectionSource, UserStats.class);
|
||||
statsDao.executeRaw("PRAGMA journal_mode=WAL;");
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error creating user_stats repository - ", ex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,18 +19,21 @@
|
|||
<xs:attribute name="serverAddress" type="xs:string" use="required"/>
|
||||
<xs:attribute name="serverName" type="xs:string" use="required"/>
|
||||
<xs:attribute name="port" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="secondaryBindPort" type="xs:integer" use="required"/>
|
||||
<xs:attribute name="backlogSize" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="numAcceptThreads" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="maxPoolSize" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="leasePeriod" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="secondaryBindPort" type="xs:integer" use="required"/>
|
||||
<xs:attribute name="backlogSize" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="numAcceptThreads" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="maxPoolSize" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="leasePeriod" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="maxGameThreads" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="maxSecondsIdle" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="minUserNameLength" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="maxUserNameLength" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="userNamePattern" type="xs:string" use="required"/>
|
||||
<xs:attribute name="maxAiOpponents" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="saveGameActivated" type="xs:boolean" use="optional"/>
|
||||
<xs:attribute name="minUserNameLength" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="maxUserNameLength" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="userNamePattern" type="xs:string" use="required"/>
|
||||
<xs:attribute name="maxAiOpponents" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="saveGameActivated" type="xs:boolean" use="optional"/>
|
||||
<xs:attribute name="httpAuth" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="authUrl" type="xs:string" use="optional"/>
|
||||
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
|
|
|
|||
|
|
@ -142,5 +142,13 @@ public class ConfigWrapper implements ConfigSettings {
|
|||
public List<Plugin> getDeckTypes() {
|
||||
return config.getDeckTypes().getDeckType();
|
||||
}
|
||||
|
||||
public boolean isHttpAuth() {
|
||||
return config.getServer().isHttpAuth();
|
||||
}
|
||||
|
||||
public String getAuthUrl() {
|
||||
return config.getServer().getAuthUrl();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@
|
|||
<xs:attribute name="mailUser" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="mailPassword" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="mailFromAddress" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="httpAuth" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="authUrl" type="xs:string" use="optional"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
|
|
|
|||
50
Mage.Sets/src/mage/cards/e/EmporiumThopterist.java
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package mage.cards.e;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.ConjureCardEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Failure
|
||||
*/
|
||||
public final class EmporiumThopterist extends CardImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Thopter creatures");
|
||||
|
||||
static {
|
||||
filter.add(SubType.THOPTER.getPredicate());
|
||||
}
|
||||
|
||||
public EmporiumThopterist(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
|
||||
|
||||
this.subtype.add(SubType.VEDALKEN);
|
||||
this.subtype.add(SubType.ARTIFICER);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Thopters you control get +2/+0.
|
||||
this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield, filter, false)));
|
||||
// At the beginning of your upkeep, conjure a card named Ornithopter into your hand.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConjureCardEffect("Ornithopter")));
|
||||
}
|
||||
|
||||
private EmporiumThopterist(final EmporiumThopterist card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmporiumThopterist copy() {
|
||||
return new EmporiumThopterist(this);
|
||||
}
|
||||
}
|
||||
28
Mage.Sets/src/mage/sets/AlchemyMurdersAtKarlovManor.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
package mage.sets;
|
||||
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.ExpansionSet.SetCardInfo;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public final class AlchemyMurdersAtKarlovManor extends ExpansionSet {
|
||||
|
||||
private static final AlchemyMurdersAtKarlovManor instance = new AlchemyMurdersAtKarlovManor();
|
||||
|
||||
public static AlchemyMurdersAtKarlovManor getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private AlchemyMurdersAtKarlovManor() {
|
||||
super("Alchemy: Murders at Karlov Manor", "YMKM", ExpansionSet.buildDate(2024, 3, 5), SetType.SUPPLEMENTAL);
|
||||
this.hasBasicLands = false;
|
||||
|
||||
cards.add(new SetCardInfo("Emporium Thopterist", 5, Rarity.UNCOMMON, mage.cards.e.EmporiumThopterist.class));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
49
Mage.Sets/src/mage/sets/FoulMagicBlock3Extras.java
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package mage.sets;
|
||||
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.ExpansionSet.SetCardInfo;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
|
||||
public class FoulMagicBlock3Extras extends ExpansionSet {
|
||||
|
||||
|
||||
private static final FoulMagicBlock3Extras instance = new FoulMagicBlock3Extras();
|
||||
|
||||
public static FoulMagicBlock3Extras getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private FoulMagicBlock3Extras() {
|
||||
super("Foul Magic Block 3 Extras", "FMB3E", ExpansionSet.buildDate(2025, 10, 25), SetType.CUSTOM_SET);
|
||||
this.hasBasicLands = false;
|
||||
addDualAlias("MH3", "252", new SetCardInfo("Bloodsoaked Insight", 1, Rarity.UNCOMMON, mage.cards.b.BloodsoakedInsight.class));
|
||||
addDualAlias("MH3", "243", new SetCardInfo("Boggart Trawler", 2, Rarity.UNCOMMON, mage.cards.b.BoggartTrawler.class));
|
||||
addDualAlias("MH3", "249", new SetCardInfo("Bridgeworks Battle", 3, Rarity.UNCOMMON, mage.cards.b.BridgeworksBattle.class));
|
||||
addDualAlias("MH3", "250", new SetCardInfo("Disciple of Freyalise", 4, Rarity.UNCOMMON, mage.cards.d.DiscipleOfFreyalise.class));
|
||||
addDualAlias("MH3", "253", new SetCardInfo("Drowner of Truth", 5, Rarity.UNCOMMON, mage.cards.d.DrownerOfTruth.class));
|
||||
addDualAlias("MH3", "244", new SetCardInfo("Fell the Profane", 6, Rarity.UNCOMMON, mage.cards.f.FellTheProfane.class));
|
||||
addDualAlias("MH3", "254", new SetCardInfo("Glasswing Grace", 7, Rarity.UNCOMMON, mage.cards.g.GlasswingGrace.class));
|
||||
addDualAlias("MH3", "240", new SetCardInfo("Hydroelectric Specimen", 8, Rarity.UNCOMMON, mage.cards.h.HydroelectricSpecimen.class));
|
||||
addDualAlias("MH3", "255", new SetCardInfo("Legion Leadership", 9, Rarity.UNCOMMON, mage.cards.l.LegionLeadership.class));
|
||||
addDualAlias("MH3", "246", new SetCardInfo("Pinnacle Monk", 10, Rarity.UNCOMMON, mage.cards.p.PinnacleMonk.class));
|
||||
addDualAlias("MH3", "238", new SetCardInfo("Razorgrass Ambush", 11, Rarity.UNCOMMON, mage.cards.r.RazorgrassAmbush.class));
|
||||
addDualAlias("MH3", "256", new SetCardInfo("Revitalizing Repast", 12, Rarity.UNCOMMON, mage.cards.r.RevitalizingRepast.class));
|
||||
addDualAlias("MH3", "257", new SetCardInfo("Rush of Inspiration", 13, Rarity.UNCOMMON, mage.cards.r.RushOfInspiration.class));
|
||||
addDualAlias("MH3", "241", new SetCardInfo("Sink into Stupor", 14, Rarity.UNCOMMON, mage.cards.s.SinkIntoStupor.class));
|
||||
addDualAlias("MH3", "258", new SetCardInfo("Strength of the Harvest", 15, Rarity.UNCOMMON, mage.cards.s.StrengthOfTheHarvest.class));
|
||||
addDualAlias("MH3", "259", new SetCardInfo("Stump Stomp", 16, Rarity.UNCOMMON, mage.cards.s.StumpStomp.class));
|
||||
addDualAlias("MH3", "248", new SetCardInfo("Sundering Eruption", 17, Rarity.UNCOMMON, mage.cards.s.SunderingEruption.class));
|
||||
addDualAlias("MH3", "260", new SetCardInfo("Suppression Ray", 18, Rarity.UNCOMMON, mage.cards.s.SuppressionRay.class));
|
||||
addDualAlias("MH3", "261", new SetCardInfo("Waterlogged Teachings", 19, Rarity.UNCOMMON, mage.cards.w.WaterloggedTeachings.class));
|
||||
addDualAlias("MH3", "239", new SetCardInfo("Witch Enchanter", 20, Rarity.UNCOMMON, mage.cards.w.WitchEnchanter.class));
|
||||
|
||||
addAlias("KTK", "230", new SetCardInfo("Bloodstained Mire", 21, Rarity.RARE, mage.cards.b.BloodstainedMire.class));
|
||||
addAlias("KTK", "233", new SetCardInfo("Flooded Strand", 22, Rarity.RARE, mage.cards.f.FloodedStrand.class));
|
||||
addAlias("KTK", "239", new SetCardInfo("Polluted Delta", 23, Rarity.RARE, mage.cards.p.PollutedDelta.class));
|
||||
addAlias("KTK", "248", new SetCardInfo("Windswept Heath", 24, Rarity.RARE, mage.cards.w.WindsweptHeath.class));
|
||||
addAlias("KTK", "249", new SetCardInfo("Wooded Foothills", 25, Rarity.RARE, mage.cards.w.WoodedFoothills.class));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ public final class ModernHorizons3 extends ExpansionSet {
|
|||
public static ModernHorizons3 getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
private ModernHorizons3() {
|
||||
super("Modern Horizons 3", "MH3", ExpansionSet.buildDate(2024, 6, 7), SetType.SUPPLEMENTAL_MODERN_LEGAL);
|
||||
this.blockName = "Modern Horizons 3";
|
||||
|
|
|
|||
|
|
@ -18,12 +18,41 @@ import java.io.Serializable;
|
|||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public abstract class ExpansionSet implements Serializable {
|
||||
|
||||
public class CardAlias {
|
||||
public String targetSet;
|
||||
public String targetSetNumber;
|
||||
public boolean hasBack;
|
||||
public SetCardInfo cardInfo;
|
||||
public CardAlias(String targetSet, String targetSetNumber, boolean hasBack, SetCardInfo cardInfo) {
|
||||
this.targetSet = targetSet;
|
||||
this.targetSetNumber = targetSetNumber;
|
||||
this.hasBack = hasBack;
|
||||
this.cardInfo = cardInfo;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExpansionSet.class);
|
||||
|
||||
// Foul magic alias tweaks
|
||||
public final HashMap<Integer, CardAlias> cardAliases = new HashMap<Integer, CardAlias>();
|
||||
|
||||
public void addAlias(String setCode, String setNumber, SetCardInfo cardInfo) {
|
||||
cards.add(cardInfo);
|
||||
cardAliases.put(cardInfo.getCardNumberAsInt(), new CardAlias(setCode, setNumber, false, cardInfo));
|
||||
}
|
||||
|
||||
public void addDualAlias(String setCode, String setNumber, SetCardInfo cardInfo) {
|
||||
cards.add(cardInfo);
|
||||
cardAliases.put(cardInfo.getCardNumberAsInt(), new CardAlias(setCode, setNumber, true, cardInfo));
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove all usage to default (see below), keep bfz/zen/ust art styles for specific sets only
|
||||
// the main different in art styles - full art lands can have big mana icon at the bottom
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@ package mage.cards.repository;
|
|||
import com.j256.ormlite.stmt.QueryBuilder;
|
||||
import com.j256.ormlite.stmt.SelectArg;
|
||||
import com.j256.ormlite.stmt.Where;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -39,6 +42,7 @@ public class CardCriteria {
|
|||
private boolean red;
|
||||
private boolean white;
|
||||
private boolean colorless;
|
||||
private boolean limitColors;
|
||||
private Integer manaValue;
|
||||
private String sortBy;
|
||||
private Long start;
|
||||
|
|
@ -68,6 +72,12 @@ public class CardCriteria {
|
|||
this.minCardNumber = Integer.MIN_VALUE;
|
||||
this.maxCardNumber = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public CardCriteria limitColors(boolean limitColors) {
|
||||
this.limitColors = limitColors;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CardCriteria black(boolean black) {
|
||||
this.black = black;
|
||||
|
|
@ -311,35 +321,56 @@ public class CardCriteria {
|
|||
clausesCount++;
|
||||
}
|
||||
|
||||
List<String> exclusion = new ArrayList<>();
|
||||
|
||||
int colorClauses = 0;
|
||||
if (black) {
|
||||
where.eq("black", true);
|
||||
colorClauses++;
|
||||
} else {
|
||||
exclusion.add("black");
|
||||
}
|
||||
if (blue) {
|
||||
where.eq("blue", true);
|
||||
colorClauses++;
|
||||
} else {
|
||||
exclusion.add("blue");
|
||||
}
|
||||
if (green) {
|
||||
where.eq("green", true);
|
||||
colorClauses++;
|
||||
} else {
|
||||
exclusion.add("green");
|
||||
}
|
||||
if (red) {
|
||||
where.eq("red", true);
|
||||
colorClauses++;
|
||||
} else {
|
||||
exclusion.add("red");
|
||||
}
|
||||
if (white) {
|
||||
where.eq("white", true);
|
||||
colorClauses++;
|
||||
} else {
|
||||
exclusion.add("white");
|
||||
}
|
||||
if (colorless) {
|
||||
where.eq("black", false).eq("blue", false).eq("green", false).eq("red", false).eq("white", false);
|
||||
where.and(5);
|
||||
colorClauses++;
|
||||
}
|
||||
|
||||
if (colorClauses > 0) {
|
||||
where.or(colorClauses);
|
||||
clausesCount++;
|
||||
|
||||
if (this.limitColors) {
|
||||
for (String color : exclusion) {
|
||||
where.not();
|
||||
where.eq(color, true);
|
||||
clausesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minCardNumber != Integer.MIN_VALUE) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ public class CardInfo {
|
|||
protected String setCode;
|
||||
@DatabaseField(indexName = "setCode_cardNumber_index")
|
||||
protected String cardNumber;
|
||||
@DatabaseField(indexName = "oracleId_index", canBeNull = true)
|
||||
protected String oracleId;
|
||||
/**
|
||||
* Fast access to numerical card number (number without prefix/postfix: 123b -> 123)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -38,8 +38,10 @@ public enum CardRepository {
|
|||
|
||||
// TODO: delete db version from cards and expansions due un-used (cause dbs re-created on each update now)
|
||||
private static final String VERSION_ENTITY_NAME = "card";
|
||||
private static final long CARD_DB_VERSION = 54; // raise this if db structure was changed
|
||||
private static final long CARD_DB_VERSION = 55; // raise this if db structure was changed
|
||||
private static final long CARD_CONTENT_VERSION = 241; // raise this if new cards were added to the server
|
||||
private static final long CARD_CONTENT_VERSION_FOUL = 2; // raise this if specifically foul magic patches changed things
|
||||
|
||||
|
||||
private Dao<CardInfo, Object> cardsDao;
|
||||
|
||||
|
|
@ -578,6 +580,16 @@ public enum CardRepository {
|
|||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<CardInfo> findCardsWithTagRelations(List<TagRelation> tagRelations) {
|
||||
try {
|
||||
List<CardInfo> result = cardsDao.queryBuilder().where().in("oracleId", tagRelations.stream().map(id -> id.oracle_id).toArray()).query();
|
||||
return result;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return new ArrayList<CardInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public List<CardInfo> findCards(String name, long limitByMaxAmount) {
|
||||
return findCards(name, limitByMaxAmount, false, true);
|
||||
|
|
@ -670,7 +682,7 @@ public enum CardRepository {
|
|||
}
|
||||
|
||||
public long getContentVersionConstant() {
|
||||
return CARD_CONTENT_VERSION;
|
||||
return CARD_CONTENT_VERSION + CARD_CONTENT_VERSION_FOUL;
|
||||
}
|
||||
|
||||
public void closeDB(boolean writeCompact) {
|
||||
|
|
|
|||
20
Mage/src/main/java/mage/cards/repository/Tag.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package mage.cards.repository;
|
||||
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
@DatabaseTable(tableName = "tag")
|
||||
public class Tag {
|
||||
|
||||
@DatabaseField(id = true)
|
||||
public String id;
|
||||
|
||||
@DatabaseField()
|
||||
public String label;
|
||||
|
||||
@DatabaseField(canBeNull = true)
|
||||
public String description;
|
||||
}
|
||||
18
Mage/src/main/java/mage/cards/repository/TagRelation.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package mage.cards.repository;
|
||||
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
@DatabaseTable(tableName = "tag_relation")
|
||||
public class TagRelation {
|
||||
|
||||
@DatabaseField(indexName = "tags_oracle_id_index", uniqueCombo = true)
|
||||
protected String oracle_id;
|
||||
|
||||
@DatabaseField(indexName = "tags_tag_id_index", foreign = true, columnName = "tag_id", uniqueCombo = true)
|
||||
protected Tag tag;
|
||||
|
||||
}
|
||||
180
Mage/src/main/java/mage/cards/repository/TagRepository.java
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
package mage.cards.repository;
|
||||
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||
import com.j256.ormlite.stmt.DeleteBuilder;
|
||||
import com.j256.ormlite.stmt.QueryBuilder;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.support.DatabaseConnection;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
|
||||
import mage.constants.SetType;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author North, JayDi85
|
||||
*/
|
||||
public enum TagRepository {
|
||||
|
||||
instance;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TagRepository.class);
|
||||
|
||||
// fixes limit for out of memory problems
|
||||
private static final AtomicInteger databaseFixes = new AtomicInteger();
|
||||
|
||||
private static final int MAX_DATABASE_FIXES = 10;
|
||||
|
||||
private static final String VERSION_ENTITY_NAME = "tags";
|
||||
private static final long TAG_VERSION = 3; // raise this if db structure was changed
|
||||
|
||||
private Dao<Tag, Object> tagsDao;
|
||||
private Dao<TagRelation, Object> tagRelationDao;
|
||||
|
||||
|
||||
TagRepository() {
|
||||
File file = new File("db");
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, true));
|
||||
|
||||
boolean isObsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, TAG_VERSION);
|
||||
boolean isNewBuild = RepositoryUtil.isNewBuildRun(connectionSource, VERSION_ENTITY_NAME, TagRepository.class); // recreate db on new build
|
||||
if (isObsolete || isNewBuild) {
|
||||
//System.out.println("Local cards db is outdated, cleaning...");
|
||||
TableUtils.dropTable(connectionSource, TagRelation.class, true);
|
||||
TableUtils.dropTable(connectionSource, Tag.class, true);
|
||||
}
|
||||
|
||||
TableUtils.createTableIfNotExists(connectionSource, Tag.class);
|
||||
TableUtils.createTableIfNotExists(connectionSource, TagRelation.class);
|
||||
|
||||
tagsDao = DaoManager.createDao(connectionSource, Tag.class);
|
||||
tagRelationDao = DaoManager.createDao(connectionSource, TagRelation.class);
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(TagRepository.class).error("Error creating tags repository - " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Tag> getTagsFromCard(CardInfo card) {
|
||||
try {
|
||||
List<TagRelation> relations = tagRelationDao.queryForEq("oracle_id", card.oracleId);
|
||||
return tagsDao.queryBuilder().where().in("id", relations.stream().map((TagRelation rel) -> rel.tag)).query();
|
||||
} catch (SQLException e) {
|
||||
return new ArrayList<Tag>();
|
||||
}
|
||||
}
|
||||
|
||||
public List<CardInfo> getCardsByTag(Tag tag) {
|
||||
try {
|
||||
List<TagRelation> relations = tagRelationDao.queryForEq("tag_id", tag.id);
|
||||
return CardRepository.instance.findCardsWithTagRelations(relations);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return new ArrayList<CardInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public List<CardInfo> getCardsByTagId(String id) {
|
||||
try {
|
||||
return getCardsByTag(tagsDao.queryForId(id));
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return new ArrayList<CardInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public void syncTag(final Tag tag, List<String> oracleIds ) {
|
||||
if (tag == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
tagsDao.createOrUpdate(tag);
|
||||
// clear out old ones
|
||||
DeleteBuilder<TagRelation, Object> cleanser = tagRelationDao.deleteBuilder();
|
||||
cleanser.where().eq("tag_id", tag);
|
||||
cleanser.delete();
|
||||
|
||||
tagRelationDao.callBatchTasks(() -> {
|
||||
// only add new cards (no updates)
|
||||
logger.info("DB: refreshing tag " + tag.label);
|
||||
|
||||
for (String oracleId : oracleIds) {
|
||||
TagRelation relation = new TagRelation();
|
||||
relation.oracle_id = oracleId;
|
||||
relation.tag = tag;
|
||||
try {
|
||||
tagRelationDao.create(relation);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void closeDB(boolean writeCompact) {
|
||||
try {
|
||||
if (tagsDao != null && tagsDao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = tagsDao.getConnectionSource().getReadWriteConnection(tagsDao.getTableName());
|
||||
if (writeCompact) {
|
||||
conn.executeStatement("SHUTDOWN COMPACT", DatabaseConnection.DEFAULT_RESULT_FLAGS); // compact data and rewrite whole db
|
||||
} else {
|
||||
conn.executeStatement("SHUTDOWN IMMEDIATELY", DatabaseConnection.DEFAULT_RESULT_FLAGS); // close without any writes
|
||||
}
|
||||
tagsDao.getConnectionSource().releaseConnection(conn);
|
||||
}
|
||||
} catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
public void openDB() {
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, true));
|
||||
tagsDao = DaoManager.createDao(connectionSource, Tag.class);
|
||||
tagRelationDao = DaoManager.createDao(connectionSource, TagRelation.class);
|
||||
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(TagRepository.class).error("Error opening tag repository - " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Tag> getAllTags() {
|
||||
try {
|
||||
return tagsDao.queryForAll();
|
||||
} catch (SQLException e) {
|
||||
return new ArrayList<Tag>();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Tag> searchTags(String query) {
|
||||
try {
|
||||
return tagsDao.queryBuilder().where().like("label", "%"+query.replace(' ', '-')+"%").query();
|
||||
} catch (SQLException e) {
|
||||
return new ArrayList<Tag>();
|
||||
}
|
||||
}
|
||||
|
||||
public int getTagCardCount(Tag tag) {
|
||||
try {
|
||||
return (int) tagRelationDao.queryBuilder().where().eq("tag_id", tag.id).countOf();
|
||||
} catch (SQLException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
4
pom.xml
|
|
@ -56,6 +56,10 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
|
|
|
|||