From 81c2a62250868bb25f62ccdaef034fc2328ec59d Mon Sep 17 00:00:00 2001
From: Aaron Miller <aaronm@cldtk.com>
Date: Sat, 29 Sep 2018 19:14:39 -0700
Subject: [PATCH] Add "minimum rating" option to matches and tournaments

---
 .../mage/client/dialog/NewTableDialog.java    | 34 ++++++++++++++-----
 .../client/dialog/NewTournamentDialog.java    | 18 +++++++++-
 .../mage/client/dialog/PreferencesDialog.java |  2 ++
 .../java/mage/client/table/TablesPanel.java   | 14 +++++---
 .../src/main/java/mage/view/TableView.java    |  9 +++--
 .../main/java/mage/server/MageServerImpl.java | 28 ++++++++++++++-
 .../java/mage/server/TableController.java     | 30 ++++++++++++++++
 .../java/mage/game/match/MatchOptions.java    |  5 +++
 .../game/tournament/TournamentOptions.java    |  5 +++
 9 files changed, 126 insertions(+), 19 deletions(-)

diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
index 0e3121f61d..c47059ed79 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java
@@ -51,6 +51,7 @@ public class NewTableDialog extends MageDialog {
         this.spnNumWins.setModel(new SpinnerNumberModel(1, 1, 5, 1));
         this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1));
         this.spnQuitRatio.setModel(new SpinnerNumberModel(100, 0, 100, 5));
+        this.spnMinimumRating.setModel(new SpinnerNumberModel(0, 0, 3000, 10));
         this.spnEdhPowerLevel.setModel(new SpinnerNumberModel(100, 0, 100, 5));
         MageFrame.getUI().addButton(MageComponents.NEW_TABLE_OK_BUTTON, btnOK);
     }
@@ -102,8 +103,10 @@ public class NewTableDialog extends MageDialog {
         btnPreviousConfiguration2 = new javax.swing.JButton();
         btnCancel = new javax.swing.JButton();
         lblQuitRatio = new javax.swing.JLabel();
+        lblMinimumRating = new javax.swing.JLabel();
         lblEdhPowerLevel = new javax.swing.JLabel();
         spnQuitRatio = new javax.swing.JSpinner();
+        spnMinimumRating = new javax.swing.JSpinner();
         spnEdhPowerLevel = new javax.swing.JSpinner();
 
         setTitle("New Table");
@@ -186,9 +189,11 @@ public class NewTableDialog extends MageDialog {
         btnCancel.addActionListener(evt -> btnCancelActionPerformed(evt));
 
         lblQuitRatio.setText("Allowed quit %");
+        lblMinimumRating.setText("Minimum rating");
         lblEdhPowerLevel.setText("EDH power level");
 
         spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table");
+        spnMinimumRating.setToolTipText("Players with rating less than this value can't join this table");
         spnEdhPowerLevel.setToolTipText("Players with decks with a higher power level can't join this table");
 
         javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
@@ -239,9 +244,10 @@ public class NewTableDialog extends MageDialog {
                                                                 .addComponent(lblQuitRatio)
                                                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                 .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                                                .addComponent(lblEdhPowerLevel)
+                                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                                                                .addComponent(lblMinimumRating)
                                                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                                                                .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE))))
+                                                                .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE))))
                                         .addComponent(jLabel1, javax.swing.GroupLayout.Alignment.LEADING)
                                         .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING)
                                         .addGroup(layout.createSequentialGroup()
@@ -270,7 +276,11 @@ public class NewTableDialog extends MageDialog {
                                                 .addGap(18, 18, 18)
                                                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                         .addComponent(lblNumWins)
-                                                        .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)))
+                                                        .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE))
+                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                                        .addComponent(lblEdhPowerLevel)
+                                                        .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)))
                                         .addComponent(jSeparator2)
                                         .addComponent(player1Panel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                         .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
@@ -297,14 +307,13 @@ public class NewTableDialog extends MageDialog {
                                         .addComponent(cbTimeLimit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                                        .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                         .addComponent(lbDeckType)
+                                        .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                        .addComponent(chkRated)
                                         .addComponent(lblQuitRatio)
-                                        .addComponent(chkRated)
                                         .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                        .addComponent(lblEdhPowerLevel)
-                                        .addComponent(chkRated)
-                                        .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                                        .addComponent(lblMinimumRating)
+                                        .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                         .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
@@ -326,6 +335,7 @@ public class NewTableDialog extends MageDialog {
                                                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                         .addComponent(lblSkillLevel)
                                                         .addComponent(lblNumWins)
+                                                        .addComponent(lblEdhPowerLevel)
                                                         .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                                 .addComponent(lblRange)
                                                                 .addComponent(lblAttack)))
@@ -334,7 +344,8 @@ public class NewTableDialog extends MageDialog {
                                                         .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                         .addComponent(cbAttackOption, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                         .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                                        .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
+                                                        .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                                        .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                 .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@@ -394,6 +405,7 @@ public class NewTableDialog extends MageDialog {
         options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue());
         options.setPassword(this.txtPassword.getText());
         options.setQuitRatio((Integer) this.spnQuitRatio.getValue());
+        options.setMinimumRating((Integer) this.spnMinimumRating.getValue());
         options.setEdhPowerLevel((Integer) this.spnEdhPowerLevel.getValue());
         String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
         options.setBannedUsers(IgnoreList.ignoreList(serverAddress));
@@ -695,6 +707,7 @@ public class NewTableDialog extends MageDialog {
         }
 
         this.spnQuitRatio.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_QUIT_RATIO, "100")));
+        this.spnMinimumRating.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_MINIMUM_RATING + versionStr, "0")));
         this.spnEdhPowerLevel.setValue(0);
     }
 
@@ -729,6 +742,7 @@ public class NewTableDialog extends MageDialog {
         PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_SPECTATORS_ALLOWED + versionStr, options.isSpectatorsAllowed() ? "Yes" : "No");
         PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PLANECHASE + versionStr, options.isPlaneChase() ? "Yes" : "No");
         PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_QUIT_RATIO + versionStr, Integer.toString(options.getQuitRatio()));
+        PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_MINIMUM_RATING + versionStr, Integer.toString(options.getMinimumRating()));
         StringBuilder playerTypesString = new StringBuilder();
         for (Object player : players) {
             if (playerTypesString.length() > 0) {
@@ -770,6 +784,7 @@ public class NewTableDialog extends MageDialog {
     private javax.swing.JLabel lblNumWins;
     private javax.swing.JLabel lblPassword;
     private javax.swing.JLabel lblQuitRatio;
+    private javax.swing.JLabel lblMinimumRating;
     private javax.swing.JLabel lblEdhPowerLevel;
     private javax.swing.JLabel lblRange;
     private javax.swing.JLabel lblSkillLevel;
@@ -779,6 +794,7 @@ public class NewTableDialog extends MageDialog {
     private javax.swing.JSpinner spnNumPlayers;
     private javax.swing.JSpinner spnNumWins;
     private javax.swing.JSpinner spnQuitRatio;
+    private javax.swing.JSpinner spnMinimumRating;
     private javax.swing.JSpinner spnEdhPowerLevel;
     private javax.swing.JTextField txtName;
     private javax.swing.JTextField txtPassword;
diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
index f5f2961f51..e11e0c6276 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java
@@ -74,6 +74,7 @@ public class NewTournamentDialog extends MageDialog {
         this.spnConstructTime.setModel(new SpinnerNumberModel(10, CONSTRUCTION_TIME_MIN, CONSTRUCTION_TIME_MAX, 2));
         this.spnNumRounds.setModel(new SpinnerNumberModel(2, 2, 10, 1));
         this.spnQuitRatio.setModel(new SpinnerNumberModel(100, 0, 100, 5));
+        this.spnMinimumRating.setModel(new SpinnerNumberModel(0, 0, 3000, 10));
     }
 
     public void showDialog(UUID roomId) {
@@ -164,6 +165,8 @@ public class NewTournamentDialog extends MageDialog {
         pnlRandomPacks = new javax.swing.JPanel();
         lblQuitRatio = new javax.swing.JLabel();
         spnQuitRatio = new javax.swing.JSpinner();
+        lblMinimumRating = new javax.swing.JLabel();
+        spnMinimumRating = new javax.swing.JSpinner();
 
         setTitle("New Tournament");
 
@@ -314,8 +317,10 @@ public class NewTournamentDialog extends MageDialog {
         pnlRandomPacks.setLayout(new javax.swing.BoxLayout(pnlRandomPacks, javax.swing.BoxLayout.Y_AXIS));
 
         lblQuitRatio.setText("Allowed quit %:");
+        lblMinimumRating.setText("Minimum rating:");
 
         spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table");
+        spnMinimumRating.setToolTipText("Players with rating less than this value can't join this table");
         spnNumSeats.setToolTipText("The number of seats for each duel. If more than 2, will set number of wins to 1");
         spnNumPlayers.setToolTipText("The total number of players who will draft");
 
@@ -385,11 +390,15 @@ public class NewTournamentDialog extends MageDialog {
                                                                                         .addComponent(lblNumWins)
                                                                                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                                         .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                                                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                                                                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                                                         .addComponent(lblQuitRatio)
                                                                                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                                         .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
                                                                                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                                                                                        .addComponent(lblMinimumRating)
+                                                                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                                                                                        .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                                                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                                                         .addComponent(chkRated))
                                                                                 .addComponent(cbTournamentType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE)))
                                                                 .addGroup(layout.createSequentialGroup()
@@ -443,6 +452,8 @@ public class NewTournamentDialog extends MageDialog {
                                         .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                         .addComponent(lblQuitRatio)
                                         .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                        .addComponent(lblMinimumRating)
+                                        .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                         .addComponent(chkRated))
                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
@@ -521,6 +532,7 @@ public class NewTournamentDialog extends MageDialog {
         tOptions.setWatchingAllowed(cbAllowSpectators.isSelected());
         tOptions.setPlaneChase(cbPlaneChase.isSelected());
         tOptions.setQuitRatio((Integer) spnQuitRatio.getValue());
+        tOptions.setMinimumRating((Integer) spnMinimumRating.getValue());
         for (TournamentPlayerPanel player : players) {
             tOptions.getPlayerTypes().add((PlayerType) player.getPlayerType().getSelectedItem());
         }
@@ -1061,6 +1073,7 @@ public class NewTournamentDialog extends MageDialog {
         this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_FREE_MULLIGANS + versionStr, "0")));
         this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_WINS + versionStr, "2")));
         this.spnQuitRatio.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_QUIT_RATIO + versionStr, "100")));
+        this.spnMinimumRating.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_MINIMUM_RATING + versionStr, "0")));
 
         TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem();
         activatePanelElements(tournamentType);
@@ -1146,6 +1159,7 @@ public class NewTournamentDialog extends MageDialog {
         PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_FREE_MULLIGANS + versionStr, Integer.toString(tOptions.getMatchOptions().getFreeMulligans()));
         PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_WINS + versionStr, Integer.toString(tOptions.getMatchOptions().getWinsNeeded()));
         PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_QUIT_RATIO + versionStr, Integer.toString(tOptions.getQuitRatio()));
+        PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_MINIMUM_RATING + versionStr, Integer.toString(tOptions.getMinimumRating()));
 
         if (tOptions.getTournamentType().startsWith("Sealed")) {
             PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_SEALED + versionStr, tOptions.getLimitedOptions().getSetCodes().toString());
@@ -1217,6 +1231,7 @@ public class NewTournamentDialog extends MageDialog {
     private javax.swing.JLabel lblPassword;
     private javax.swing.JLabel lblPlayer1;
     private javax.swing.JLabel lblQuitRatio;
+    private javax.swing.JLabel lblMinimumRating;
     private javax.swing.JLabel lblTournamentType;
     private mage.client.table.NewPlayerPanel player1Panel;
     private javax.swing.JPanel pnlDraftOptions;
@@ -1231,6 +1246,7 @@ public class NewTournamentDialog extends MageDialog {
     private javax.swing.JSpinner spnNumRounds;
     private javax.swing.JSpinner spnNumWins;
     private javax.swing.JSpinner spnQuitRatio;
+    private javax.swing.JSpinner spnMinimumRating;
     private javax.swing.JTextField txtName;
     private javax.swing.JTextField txtPassword;
     private org.jdesktop.beansbinding.BindingGroup bindingGroup;
diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java
index 4d595a005a..5826d2c85b 100644
--- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java
+++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java
@@ -222,6 +222,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
     public static final String KEY_NEW_TABLE_NUMBER_PLAYERS = "newTableNumberPlayers";
     public static final String KEY_NEW_TABLE_PLAYER_TYPES = "newTablePlayerTypes";
     public static final String KEY_NEW_TABLE_QUIT_RATIO = "newTableQuitRatio";
+    public static final String KEY_NEW_TABLE_MINIMUM_RATING = "newTableMinimumRating";
     public static final String KEY_NEW_TABLE_RATED = "newTableRated";
 
     // pref setting for new tournament dialog
@@ -243,6 +244,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
     public static final String KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS = "newTournamentAllowRollbacks";
     public static final String KEY_NEW_TOURNAMENT_DECK_FILE = "newTournamentDeckFile";
     public static final String KEY_NEW_TOURNAMENT_QUIT_RATIO = "newTournamentQuitRatio";
+    public static final String KEY_NEW_TOURNAMENT_MINIMUM_RATING = "newTournamentMinimumRating";
     public static final String KEY_NEW_TOURNAMENT_RATED = "newTournamentRated";
 
     // pref setting for deck generator
diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java
index d28badf2f7..e486c5f834 100644
--- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java
+++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java
@@ -1221,6 +1221,7 @@ public class TablesPanel extends javax.swing.JPanel {
             options.setSkillLevel(SkillLevel.CASUAL);
             options.setRollbackTurnsAllowed(true);
             options.setQuitRatio(100);
+            options.setMinimumRating(0);
             String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
             options.setBannedUsers(IgnoreList.ignoreList(serverAddress));
             table = SessionHandler.createTable(roomId, options);
@@ -1335,14 +1336,15 @@ class TableTableModel extends AbstractTableModel {
     public static final int COLUMN_SKILL = 8;
     public static final int COLUMN_RATING = 9;
     public static final int COLUMN_QUIT_RATIO = 10;
-    public static final int ACTION_COLUMN = 11; // column the action is located (starting with 0)
+    public static final int COLUMN_MINIMUM_RATING = 11;
+    public static final int ACTION_COLUMN = 12; // column the action is located (starting with 0)
 
     public static final String RATED_VALUE_YES = "YES";
     public static final String RATED_VALUE_NO = "";
 
     public static final String PASSWORD_VALUE_YES = "YES";
 
-    private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rating", "Quit %", "Action"};
+    private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rated", "Quit %", "Min Rating", "Action"};
 
     private TableView[] tables = new TableView[0];
 
@@ -1390,6 +1392,8 @@ class TableTableModel extends AbstractTableModel {
             case 10:
                 return tables[arg0].getQuitRatio();
             case 11:
+                return tables[arg0].getMinimumRating();
+            case 12:
                 switch (tables[arg0].getTableState()) {
 
                     case WAITING:
@@ -1419,14 +1423,14 @@ class TableTableModel extends AbstractTableModel {
                     default:
                         return "";
                 }
-            case 12:
-                return tables[arg0].isTournament();
             case 13:
+                return tables[arg0].isTournament();
+            case 14:
                 if (!tables[arg0].getGames().isEmpty()) {
                     return tables[arg0].getGames().get(0);
                 }
                 return null;
-            case 14:
+            case 15:
                 return tables[arg0].getTableId();
         }
         return "";
diff --git a/Mage.Common/src/main/java/mage/view/TableView.java b/Mage.Common/src/main/java/mage/view/TableView.java
index c3d748c344..948c7717bf 100644
--- a/Mage.Common/src/main/java/mage/view/TableView.java
+++ b/Mage.Common/src/main/java/mage/view/TableView.java
@@ -36,6 +36,7 @@ public class TableView implements Serializable {
     private List<SeatView> seats = new ArrayList<>();
     private List<UUID> games = new ArrayList<>();
     private final String quitRatio;
+    private final String minimumRating;
     private final boolean limited;
     private final boolean rated;
     private final boolean passworded;
@@ -111,6 +112,7 @@ public class TableView implements Serializable {
             this.additionalInfo = addInfo.toString();
             this.skillLevel = table.getMatch().getOptions().getSkillLevel();
             this.quitRatio = Integer.toString(table.getMatch().getOptions().getQuitRatio());
+            this.minimumRating = Integer.toString(table.getMatch().getOptions().getMinimumRating());
             this.limited = table.getMatch().getOptions().isLimited();
             this.rated = table.getMatch().getOptions().isRated();
             this.passworded = !table.getMatch().getOptions().getPassword().isEmpty();
@@ -159,6 +161,7 @@ public class TableView implements Serializable {
             this.deckType = table.getDeckType() + ' ' + table.getTournament().getBoosterInfo() + (tableNameInfo != null ? tableNameInfo : "");
             this.skillLevel = table.getTournament().getOptions().getMatchOptions().getSkillLevel();
             this.quitRatio = Integer.toString(table.getTournament().getOptions().getQuitRatio());
+            this.minimumRating = Integer.toString(table.getTournament().getOptions().getMinimumRating());
             this.limited = table.getTournament().getOptions().getMatchOptions().isLimited();
             this.rated = table.getTournament().getOptions().getMatchOptions().isRated();
             this.passworded = !table.getTournament().getOptions().getPassword().isEmpty();
@@ -223,9 +226,9 @@ public class TableView implements Serializable {
         return skillLevel;
     }
 
-    public String getQuitRatio() {
-        return quitRatio;
-    }
+    public String getQuitRatio() { return quitRatio; }
+
+    public String getMinimumRating() { return minimumRating; }
 
     public boolean isLimited() {
         return limited;
diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java
index f6676754ef..35e7fb4e90 100644
--- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java
+++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java
@@ -227,6 +227,20 @@ public class MageServerImpl implements MageServer {
                         user.showUserMessage("Create tournament", message);
                         throw new MageException("No message");
                     }
+                    // check if the user satisfies the minimumRating requirement.
+                    int minimumRating = options.getMinimumRating();
+                    int userRating;
+                    if (options.getMatchOptions().isLimited()) {
+                        userRating = user.getUserData().getLimitedRating();
+                    } else {
+                        userRating = user.getUserData().getConstructedRating();
+                    }
+                    if (userRating < minimumRating) {
+                        String message = new StringBuilder("Your rating ").append(userRating)
+                                .append(" is lower than the table requirement ").append(minimumRating).toString();
+                        user.showUserMessage("Create tournament", message);
+                        throw new MageException("No message");
+                    }
                     Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
                     if (!room.isPresent()) {
 
@@ -1386,7 +1400,19 @@ public class MageServerImpl implements MageServer {
                 user.showUserMessage("Create table", "Your quit ratio " + user.getMatchQuitRatio() + "% is higher than the table requirement " + quitRatio + '%');
                 throw new MageException("No message");
             }
-
+            // check if the user satisfies the minimumRating requirement.
+            int minimumRating = options.getMinimumRating();
+            int userRating;
+            if (options.isLimited()) {
+                userRating = user.getUserData().getLimitedRating();
+            } else {
+                userRating = user.getUserData().getConstructedRating();
+            }
+            if (userRating < minimumRating) {
+                String message = new StringBuilder("Your rating ").append(userRating).append(" is lower than the table requirement ").append(minimumRating).toString();
+                user.showUserMessage("Create table", message);
+                throw new MageException("No message");
+            }
             Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
             if (room.isPresent()) {
                 TableView table = room.get().createTable(userId, options);
diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java
index 58a0b695b8..1d511d7e4f 100644
--- a/Mage.Server/src/main/java/mage/server/TableController.java
+++ b/Mage.Server/src/main/java/mage/server/TableController.java
@@ -172,6 +172,21 @@ public class TableController {
             return false;
         }
 
+        // Check minimum rating.
+        int minimumRating = table.getTournament().getOptions().getMinimumRating();
+        int userRating;
+        if (table.getTournament().getOptions().getMatchOptions().isLimited()) {
+            userRating = user.getUserData().getLimitedRating();
+        } else {
+            userRating = user.getUserData().getConstructedRating();
+        }
+        if (userRating < minimumRating) {
+            String message = new StringBuilder("Your rating ").append(userRating)
+                    .append(" is lower than the table requirement ").append(minimumRating).toString();
+            user.showUserMessage("Join Table", message);
+            return false;
+        }
+
         Optional<Player> playerOptional = createPlayer(name, seat.getPlayerType(), skill);
         if (playerOptional.isPresent()) {
             Player player = playerOptional.get();
@@ -272,6 +287,21 @@ public class TableController {
             return false;
         }
 
+        // Check minimum rating.
+        int minimumRating = table.getMatch().getOptions().getMinimumRating();
+        int userRating;
+        if (table.getMatch().getOptions().isLimited()) {
+            userRating = user.getUserData().getLimitedRating();
+        } else {
+            userRating = user.getUserData().getConstructedRating();
+        }
+        if (userRating < minimumRating) {
+            String message = new StringBuilder("Your rating ").append(userRating)
+                    .append(" is lower than the table requirement ").append(minimumRating).toString();
+            user.showUserMessage("Join Table", message);
+            return false;
+        }
+
         // Check power level for table (currently only used for EDH/Commander table)
         int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel();
         if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase(Locale.ENGLISH).equals("commander")) {
diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java
index 02e6808416..47ad873e63 100644
--- a/Mage/src/main/java/mage/game/match/MatchOptions.java
+++ b/Mage/src/main/java/mage/game/match/MatchOptions.java
@@ -37,6 +37,7 @@ public class MatchOptions implements Serializable {
     protected boolean spectatorsAllowed;
     protected boolean planeChase;
     protected int quitRatio;
+    protected int minimumRating;
     protected int edhPowerLevel;
     protected boolean rated;
     protected int numSeatsForMatch;
@@ -205,6 +206,10 @@ public class MatchOptions implements Serializable {
         this.quitRatio = quitRatio;
     }
 
+    public int getMinimumRating() { return minimumRating; }
+
+    public void setMinimumRating(int minimumRating) { this.minimumRating = minimumRating; }
+
     public int getEdhPowerLevel() {
         return edhPowerLevel;
     }
diff --git a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java
index 2b042d19a1..c81294ce3e 100644
--- a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java
+++ b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java
@@ -24,6 +24,7 @@ public class TournamentOptions implements Serializable {
     protected int numberRounds;
     protected String password;
     protected int quitRatio;
+    protected int minimumRating;
 
     public TournamentOptions(String name, String matchType, int numSeats) {
         this.name = name;
@@ -98,4 +99,8 @@ public class TournamentOptions implements Serializable {
     public void setQuitRatio(int quitRatio) {
         this.quitRatio = quitRatio;
     }
+
+    public int getMinimumRating() { return minimumRating; }
+
+    public void setMinimumRating(int minimumRating) { this.minimumRating = minimumRating; }
 }