From 2e7e78d1e5390bcc7a7dc41d2f723335792eb868 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 31 May 2025 13:10:03 +0400 Subject: [PATCH] GUI, table: allow unlimited draft bots amount, fixed computer names in 10+ tables (part of #13701) --- .../client/dialog/NewTournamentDialog.java | 8 +++---- .../mage/client/table/TablePlayerPanel.java | 6 ++++- .../java/mage/client/table/TablesPanel.form | 4 ++-- .../java/mage/client/table/TablesPanel.java | 4 ++-- .../client/table/TournamentPlayerPanel.java | 2 +- Mage.Server/config/config.xml | 2 +- Mage.Server/release/config/config.xml | 2 +- .../main/java/mage/server/MageServerImpl.java | 13 +++++------ .../java/mage/server/TableController.java | 4 ++-- .../main/java/mage/players/PlayerType.java | 22 ++++++++++++++----- 10 files changed, 40 insertions(+), 27 deletions(-) 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 23379fd2513..a22adb61fb9 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -41,7 +41,7 @@ public class NewTournamentDialog extends MageDialog { private static final Logger logger = Logger.getLogger(NewTournamentDialog.class); // it's ok to have 4 players at the screen, 6 is fine for big screens too - private static final int MAX_PLAYERS_PER_GAME = 6; + private static final int MAX_WORKABLE_PLAYERS_PER_GAME = 6; // temp settings on loading players list private final List prefPlayerTypes = new ArrayList<>(); @@ -699,12 +699,12 @@ public class NewTournamentDialog extends MageDialog { // draft bots are loses and hide at the start, so count only human and AI if (tOptions.getMatchOptions().isSingleGameTourney()) { int workablePlayers = tOptions.getPlayerTypes().stream() - .mapToInt(p -> p.equals(PlayerType.COMPUTER_DRAFT_BOT) ? 0 : 1) + .mapToInt(p -> p.isWorkablePlayer() ? 1 : 0) .sum(); - if (workablePlayers > MAX_PLAYERS_PER_GAME) { + if (workablePlayers > MAX_WORKABLE_PLAYERS_PER_GAME) { JOptionPane.showMessageDialog( MageFrame.getDesktop(), - String.format("Warning, in single game mode you can choose %d human/ai players but selected %d", MAX_PLAYERS_PER_GAME, workablePlayers), + String.format("Warning, in single game mode you can choose %d human/ai players but selected %d", MAX_WORKABLE_PLAYERS_PER_GAME, workablePlayers), "Warning", JOptionPane.WARNING_MESSAGE ); diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java index 34840279052..61c7614e6ad 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java @@ -41,6 +41,10 @@ public class TablePlayerPanel extends javax.swing.JPanel { this.newPlayerPanel.setSkillLevel(playerSkill); } + public static String extractAiPlayerNumberFromLabel(String label) { + return ClientDefaultSettings.computerName + " " + label.substring(Math.max(0, label.length() - 2)).trim(); + } + public boolean joinTable(UUID roomId, UUID tableId) throws IOException, ClassNotFoundException { if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) { return SessionHandler.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), (PlayerType) this.cbPlayerType.getSelectedItem(), this.newPlayerPanel.getSkillLevel(), DeckImporter.importDeckFromFile(this.newPlayerPanel.getDeckFile(), true), ""); @@ -124,7 +128,7 @@ public class TablePlayerPanel extends javax.swing.JPanel { private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed if (getPlayerType() != PlayerType.HUMAN) { this.newPlayerPanel.setVisible(true); - this.newPlayerPanel.setPlayerName(ClientDefaultSettings.computerName + " " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1)); + this.newPlayerPanel.setPlayerName(extractAiPlayerNumberFromLabel(this.lblPlayerNum.getText())); } else { this.newPlayerPanel.setVisible(false); } diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form index b970a25e99f..49390111ad9 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form @@ -201,7 +201,7 @@ - + @@ -216,7 +216,7 @@ - + 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 13301a9c6cd..302fe95ffbb 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1163,7 +1163,7 @@ public class TablesPanel extends javax.swing.JPanel { filterBar1.add(btnTypeMatch); btnTypeTourneyConstructed.setSelected(true); - btnTypeTourneyConstructed.setText("Constructed tourn."); + btnTypeTourneyConstructed.setText("Constructed tourney"); btnTypeTourneyConstructed.setToolTipText("Shows all constructed tournament tables."); btnTypeTourneyConstructed.setActionCommand("typeTourneyConstructed"); btnTypeTourneyConstructed.setFocusPainted(false); @@ -1178,7 +1178,7 @@ public class TablesPanel extends javax.swing.JPanel { filterBar1.add(btnTypeTourneyConstructed); btnTypeTourneyLimited.setSelected(true); - btnTypeTourneyLimited.setText("Limited tourn."); + btnTypeTourneyLimited.setText("Limited tourney"); btnTypeTourneyLimited.setToolTipText("Shows all limited tournament tables."); btnTypeTourneyLimited.setActionCommand("typeTourneyLimited"); btnTypeTourneyLimited.setFocusPainted(false); diff --git a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java index a6eca85360b..f4e34e82dec 100644 --- a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java @@ -145,7 +145,7 @@ public class TournamentPlayerPanel extends javax.swing.JPanel { private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) { this.pnlPlayerName.setVisible(true); - this.txtPlayerName.setText(ClientDefaultSettings.computerName + " " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1)); + this.txtPlayerName.setText(TablePlayerPanel.extractAiPlayerNumberFromLabel(this.lblPlayerNum.getText())); this.txtPlayerName.setEditable(false); this.txtPlayerName.setEnabled(false); } else { diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 3f1eba60fb0..920649e019f 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -21,7 +21,7 @@ minUserNameLength - minmal allowed length of a user name to connect to the server maxUserNameLength - maximal allowed length of a user name to connect to the server userNamePattern - pattern for user name validity check - maxAiOpponents - number of allowed AI opponents on the server + maxAiOpponents - number of allowed workable AI opponents on the server (draft bots are unlimited) saveGameActivated - allow game save and replay options (not working correctly yet) authenticationActivated - "true" = user have to register to signon "false" = user need not to register diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 1f77ab3d9b7..5c99c4fae18 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -18,7 +18,7 @@ minUserNameLength - minmal allowed length of a user name to connect to the server maxUserNameLength - maximal allowed length of a user name to connect to the server userNamePattern - pattern for user name validity check - maxAiOpponents - number of allowed AI opponents on the server + maxAiOpponents - number of allowed workable AI opponents on the server (draft bots are unlimited) saveGameActivated - allow game save and replay options (not working correctly yet) authenticationActivated - "true" = user have to register to signon "false" = user need not to register * mail configs only needed if authentication is activated: diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index bb69e76ff44..b5cda3de44b 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -222,15 +222,12 @@ public class MageServerImpl implements MageServer { throw new MageException("No message"); } - // check AI players max + // limit number of workable AI opponents (draft bots are unlimited) String maxAiOpponents = managerFactory.configSettings().getMaxAiOpponents(); if (maxAiOpponents != null) { - int aiPlayers = 0; - for (PlayerType playerType : options.getPlayerTypes()) { - if (playerType != PlayerType.HUMAN) { - aiPlayers++; - } - } + int aiPlayers = options.getPlayerTypes().stream() + .mapToInt(t -> t.isAI() && t.isWorkablePlayer() ? 1 : 0) + .sum(); int max = Integer.parseInt(maxAiOpponents); if (aiPlayers > max) { user.showUserMessage("Create tournament", "It's only allowed to use a maximum of " + max + " AI players."); @@ -324,7 +321,7 @@ public class MageServerImpl implements MageServer { UUID userId = session.get().getUserId(); if (logger.isTraceEnabled()) { Optional user = managerFactory.userManager().getUser(userId); - user.ifPresent(user1 -> logger.trace("join tourn. tableId: " + tableId + ' ' + name)); + user.ifPresent(user1 -> logger.trace("join tourney tableId: " + tableId + ' ' + name)); } if (userId == null) { logger.fatal("Got no userId from sessionId" + sessionId + " tableId" + tableId); diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index d11b3c5950b..b23895416cd 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -94,7 +94,7 @@ public class TableController { } table = new Table(roomId, options.getTournamentType(), options.getName(), controllerName, DeckValidatorFactory.instance.createDeckValidator(options.getMatchOptions().getDeckType()), options.getPlayerTypes(), new TableRecorderImpl(managerFactory.userManager()), tournament, options.getMatchOptions().getBannedUsers(), options.isPlaneChase()); - chatId = managerFactory.chatManager().createChatSession("Tourn. table " + table.getId()); + chatId = managerFactory.chatManager().createChatSession("Tourney table " + table.getId()); } private void init() { @@ -575,7 +575,7 @@ public class TableController { logger.error("No tournament object - userId: " + userId + " table: " + table.getId()); return; } - if (this.userId != null && this.userId.equals(userId) // tourn. sub tables have no creator user + if (this.userId != null && this.userId.equals(userId) // tourney sub tables have no creator user && (table.getState() == TableState.WAITING || table.getState() == TableState.READY_TO_START)) { // table not started yet and user is the owner, removeUserFromAllTablesAndChat the table diff --git a/Mage/src/main/java/mage/players/PlayerType.java b/Mage/src/main/java/mage/players/PlayerType.java index 75bd6058227..4a8fccd8922 100644 --- a/Mage/src/main/java/mage/players/PlayerType.java +++ b/Mage/src/main/java/mage/players/PlayerType.java @@ -8,15 +8,19 @@ package mage.players; * @author IGOUDT */ public enum PlayerType { - HUMAN("Human"), - COMPUTER_DRAFT_BOT("Computer - draftbot"), - COMPUTER_MONTE_CARLO("Computer - monte carlo"), - COMPUTER_MAD("Computer - mad"); + HUMAN("Human", false, true), + COMPUTER_DRAFT_BOT("Computer - draftbot", true, false), + COMPUTER_MONTE_CARLO("Computer - monte carlo", true, true), + COMPUTER_MAD("Computer - mad", true, true); final String description; + final boolean isAI; + final boolean isWorkablePlayer; // false for draft bots cause it does nothing in real game and just loose a game - PlayerType(String description) { + PlayerType(String description, boolean isAI, boolean isWorkablePlayer) { this.description = description; + this.isAI = isAI; + this.isWorkablePlayer = isWorkablePlayer; } @Override @@ -24,6 +28,14 @@ public enum PlayerType { return description; } + public boolean isAI() { + return this.isAI; + } + + public boolean isWorkablePlayer() { + return this.isWorkablePlayer; + } + public static PlayerType getByDescription(String description) { for (PlayerType type : values()) { if (type.description.equals(description)) {