package net.sf.colossus.appmain; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.TreeSet; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.ScrollPaneConstants; import javax.swing.border.TitledBorder; import javax.swing.text.Document; import net.sf.colossus.common.Constants; import net.sf.colossus.common.Options; import net.sf.colossus.common.WhatNextManager; import net.sf.colossus.common.WhatNextManager.WhatToDoNext; import net.sf.colossus.game.Dice; import net.sf.colossus.gui.PickIntValue; import net.sf.colossus.gui.ShowReadme; import net.sf.colossus.guiutil.KFrame; import net.sf.colossus.guiutil.SaveWindow; import net.sf.colossus.server.VariantSupport; import net.sf.colossus.server.XMLSnapshotFilter; import net.sf.colossus.util.BuildInfo; import net.sf.colossus.util.StaticResourceLoader; import net.sf.colossus.util.SystemInfo; import net.sf.colossus.variant.Variant; /** * Class GetPlayers is a dialog used to enter players' * names, types, variant, etc. * * @author David Ripton * @author Romain Dolbeau */ @SuppressWarnings("serial") public final class GetPlayers extends KFrame { private static final Logger LOGGER = Logger.getLogger(GetPlayers.class .getName()); private static final String loadVariant = "Load External Variant"; // private static final long REMINDER_INTERVAL_DAYS = 30; private final Object mutex; private boolean mutexNotified = false; private final JLabel runningOnLabel; private final JComboBox<String> serveAtPortBox; private final TreeSet<String> sPortChoices; private final Vector<String> typeChoices = new Vector<String>(); @SuppressWarnings({ "rawtypes" }) private final JComboBox[] playerTypes = new JComboBox[Constants.MAX_MAX_PLAYERS]; @SuppressWarnings({ "rawtypes" }) private final JComboBox[] playerNames = new JComboBox[Constants.MAX_MAX_PLAYERS]; private JEditorPane readme = new JEditorPane(); private JScrollPane readmeScrollPane; private final JTabbedPane tabbedPane; private final JComboBox<String> variantBox; private final Vector<String> variantVector; private final JComboBox<String> viewModeBox; private final JComboBox<String> eventExpiringBox; private int serveAtPort = -1; // server serves at that. /** This is Game's options, which we will modify directly. */ private final Options options; private final Options stOptions; private final WhatNextManager whatNextManager; private final boolean byWebClient; private int oldDelay; private final JLabel delayLabel; private int oldLimit; private final JLabel timeLimitLabel; private final SaveWindow saveWindow; /** Clear options to abort */ public GetPlayers(final Options options, Object mutex, WhatNextManager whatNextMgr, boolean byWebClient) { super("Game Setup"); net.sf.colossus.util.InstanceTracker.register(this, "only one"); this.options = options; this.mutex = mutex; this.whatNextManager = whatNextMgr; this.stOptions = whatNextMgr.getStartOptions(); this.byWebClient = byWebClient; setupTypeChoices(); setBackground(Color.lightGray); Container mainPane = new Box(BoxLayout.Y_AXIS); JScrollPane mainScrollPane = new JScrollPane(mainPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); getContentPane().setLayout(new BorderLayout()); getContentPane().add(mainScrollPane, BorderLayout.CENTER); // dummy text, will be set finally when serveAtPort is handled. runningOnLabel = new JLabel("Running on..."); JPanel runningOnPane = new JPanel(); runningOnPane.setLayout(new GridLayout(0, 1)); runningOnPane.add(runningOnLabel); mainPane.add(runningOnPane); tabbedPane = new JTabbedPane(); // ================== Players tab ===================== // Box allPlayersPane = new Box(BoxLayout.Y_AXIS); tabbedPane.addTab("Players", allPlayersPane); mainPane.add(tabbedPane); for (int i = 0; i < Constants.MAX_MAX_PLAYERS; i++) { doOnePlayer(i, allPlayersPane); } Box optionPane = new Box(BoxLayout.Y_AXIS); tabbedPane.addTab("Options", optionPane); Box portPane = new Box(BoxLayout.X_AXIS); portPane.add(new JLabel("Serve game at port: ")); sPortChoices = new TreeSet<String>(); sPortChoices.add("" + Constants.defaultPort); int stPort = stOptions.getIntOption(Options.serveAtPort); if (stPort != -1 && stPort != Constants.defaultPort) { sPortChoices.add("" + stPort); } else { stPort = Constants.defaultPort; } serveAtPortBox = new JComboBox<String>( sPortChoices.toArray(new String[sPortChoices.size()])); /* moved further down; when run here it hangs up for a minute or * so in Java7 ? */ // serveAtPortBox.setEditable(true); serveAtPortBox.setSelectedItem("" + stPort); serveAtPortBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String portString = (String)serveAtPortBox.getSelectedItem(); serveAtPort = Integer.parseInt(portString); setRunningOnLabel(serveAtPort); } }); serveAtPort = stPort; portPane.add(serveAtPortBox); optionPane.add(portPane); setRunningOnLabel(stPort); JPanel checkboxPane = new JPanel(new GridLayout(0, 3)); checkboxPane.setBorder(new TitledBorder("General")); optionPane.add(checkboxPane); addCheckbox(Options.autosave, checkboxPane); addCheckbox(Options.autoStop, checkboxPane); addCheckbox(Options.autoQuit, checkboxPane); addCheckbox(Options.keepAccepting, checkboxPane); addCheckbox(Options.hotSeatMode, checkboxPane); String viewmodeName = options.getStringOption(Options.viewMode, Options.viewableEver); JPanel viewModePane = new JPanel(new GridLayout(0, 2)); viewModePane.setBorder(new TitledBorder( "Viewability of legion and events")); optionPane.add(viewModePane); viewModeBox = new JComboBox<String>(Options.viewModeArray); viewModeBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String value = (String)viewModeBox.getSelectedItem(); options.setOption(Options.viewMode, value); } }); viewModeBox.setSelectedItem(viewmodeName); viewModePane.add(new JLabel("Viewable legion content:")); viewModePane.add(viewModeBox); String eventExpiringVal = options.getStringOption( Options.eventExpiring, "5"); eventExpiringBox = new JComboBox<String>(Options.eventExpiringChoices); eventExpiringBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String value = (String)eventExpiringBox.getSelectedItem(); options.setOption(Options.eventExpiring, value); } }); eventExpiringBox.setSelectedItem(eventExpiringVal); viewModePane.add(new JLabel("Events expire after (turns):")); viewModePane.add(eventExpiringBox); options.setOption(Options.viewMode, viewmodeName); JPanel teleportPane = new JPanel(new GridLayout(0, 2)); teleportPane.setBorder(new TitledBorder("Teleport")); optionPane.add(teleportPane); addCheckbox(Options.noFirstTurnT2TTeleport, teleportPane); addCheckbox(Options.noFirstTurnTeleport, teleportPane); addCheckbox(Options.noTitanTeleport, teleportPane); addCheckbox(Options.towerToTowerTeleportOnly, teleportPane); addCheckbox(Options.noTowerTeleport, teleportPane); addCheckbox(Options.noFirstTurnWarlockRecruit, teleportPane); JPanel rulesOptionsPane = new JPanel(new GridLayout(0, 2)); rulesOptionsPane.setBorder(new TitledBorder("Rules")); optionPane.add(rulesOptionsPane); addCheckbox(Options.cumulativeSlow, rulesOptionsPane); addCheckbox(Options.oneHexAllowed, rulesOptionsPane); addCheckbox(Options.unlimitedMulligans, rulesOptionsPane); addCheckbox(Options.balancedTowers, rulesOptionsPane); addCheckbox(Options.sansLordAutoBattle, rulesOptionsPane); addCheckbox(Options.inactivityTimeout, rulesOptionsPane); addCheckbox(Options.pbBattleHits, rulesOptionsPane); addCheckbox(Options.fixedSequenceBattleDice, rulesOptionsPane); JPanel aiTimePane = new JPanel(new FlowLayout()); aiTimePane.setBorder(new TitledBorder("AI Timing")); optionPane.add(aiTimePane); oldDelay = options.getIntOption(Options.aiDelay); if (oldDelay < Constants.MIN_AI_DELAY || oldDelay > Constants.MAX_AI_DELAY) { oldDelay = Constants.DEFAULT_AI_DELAY; } delayLabel = new JLabel(); setDelayLabel(oldDelay); aiTimePane.add(delayLabel); JButton delayButton = new JButton(Options.aiDelay); delayButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { final int newDelay = PickIntValue.pickIntValue( GetPlayers.this, oldDelay, "Pick AI Delay (in ms)", Constants.MIN_AI_DELAY, Constants.MAX_AI_DELAY, 100, options); if (newDelay != oldDelay) { options.setOption(Options.aiDelay, newDelay); oldDelay = newDelay; } setDelayLabel(newDelay); } }); aiTimePane.add(delayButton); oldLimit = options.getIntOption(Options.aiTimeLimit); if (oldLimit < Constants.MIN_AI_TIME_LIMIT || oldLimit > Constants.MAX_AI_TIME_LIMIT) { oldLimit = Constants.DEFAULT_AI_TIME_LIMIT; } timeLimitLabel = new JLabel(); setTimeLimitLabel(oldLimit); aiTimePane.add(timeLimitLabel); JButton timeLimitButton = new JButton(Options.aiTimeLimit); timeLimitButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { final int newLimit = PickIntValue.pickIntValue( GetPlayers.this, oldLimit, "Pick AI Time Limit (in s)", Constants.MIN_AI_TIME_LIMIT, Constants.MAX_AI_TIME_LIMIT, 1, options); if (newLimit != oldLimit) { options.setOption(Options.aiTimeLimit, newLimit); oldLimit = newLimit; } setTimeLimitLabel(newLimit); } }); aiTimePane.add(timeLimitButton); JPanel variantPane = new JPanel(); variantPane.setBorder(new TitledBorder("Variant")); variantPane.setLayout(new GridLayout(0, 2)); mainPane.add(variantPane); // XXX Make sure chosen variant is in the list. String variantName = options.getStringOption(Options.variant, Constants.variantArray[0]); String variantFullPath = options.getStringOption( Options.variantFileWithFullPath, "null"); if (variantFullPath == null || variantFullPath.equals("")) { variantFullPath = "null"; } // validate variant name and full path we got from options: boolean isValidExternVariant = false; if (!variantFullPath.equals("null")) { File varFile = new File(variantFullPath); if (varFile.exists()) { isValidExternVariant = true; } else { LOGGER.warning("Invalid variant file name (full path: '" + variantFullPath + "') from Options! Resetting to Default."); variantName = Constants.variantArray[0]; variantFullPath = "null"; options.setOption(Options.variant, variantName); options.setOption(Options.variantFileWithFullPath, "null"); } } // We must ensure that the variantFullPath option is now never null // nor empty string any more, otherwise synchronizing it to clients // causes NPEs. options.setOption(Options.variantFileWithFullPath, variantFullPath); variantVector = new Vector<String>(Constants.numVariants, 1); boolean isBuiltinVariant = false; for (int i = 0; i < Constants.numVariants; i++) { String name = Constants.variantArray[i]; variantVector.add(i, name); if (name.equals(variantName)) { isBuiltinVariant = true; } } variantBox = new JComboBox<String>(variantVector); variantBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int oldMaxPlayers = VariantSupport.getMaxPlayers(); String newVarName = (String)variantBox.getSelectedItem(); if (VariantSupport.getVariantName().equals(newVarName)) { // re-selecting the same ; do nothing } else { actOnVariantChange(oldMaxPlayers, newVarName); } } }); if (isBuiltinVariant) { variantBox.setSelectedItem(variantName); } else if (isValidExternVariant) { VariantSupport.rememberFullPathFileForVariantName(variantName, variantFullPath); variantVector.add(variantName); variantBox.setSelectedItem(variantName); options.setOption(Options.variant, variantName); options .setOption(Options.variantFileWithFullPath, variantFullPath); } else { LOGGER.warning("Invalid variant! Resetting to Default."); variantName = Constants.variantArray[0]; variantFullPath = "null"; options.setOption(Options.variant, variantName); options.setOption(Options.variantFileWithFullPath, "null"); int oldMaxPlayers = VariantSupport.getMaxPlayers(); actOnVariantChange(oldMaxPlayers, variantName); // This should never happen... /* LOGGER .severe("Unexpected else case? Invalid variant name, perhaps?"); variantName = Constants.variantArray[0]; variantFullPath = "null"; options.setOption(Options.variant, variantName); options.setOption(Options.variantFileWithFullPath, "null"); */ } variantPane.add(variantBox); JButton buttonVariant = new JButton(loadVariant); variantPane.add(buttonVariant); buttonVariant.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doLoadVariant(); String varName = VariantSupport.getVariantName(); if (!(Constants.getVariantList().contains(varName))) { addVariantToBoxIfNeeded(varName); variantBox.setSelectedItem(varName); } } }); // ================== Variant README tab ===================== // if we don't pass the JEditorPane ("readme"), // it won't be updated when Variant changes. Document doc = VariantSupport.getCurrentVariant().getReadme(); readmeScrollPane = ShowReadme.readmeContentScrollPane(readme, doc); tabbedPane.addTab("Variant README", readmeScrollPane); JPanel clientPane = new JPanel(); clientPane.setBorder(new TitledBorder("Clients")); clientPane.setLayout(new GridLayout(0, 2)); mainPane.add(clientPane); JButton button3 = new JButton(Constants.runClient); button3.setMnemonic(KeyEvent.VK_C); clientPane.add(button3); button3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doClientDialog(); } }); JButton button5 = new JButton(Constants.runWebClient); button5.setMnemonic(KeyEvent.VK_W); clientPane.add(button5); button5.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doRunWebClient(); } }); JPanel gamePane = new JPanel(); gamePane.setBorder(new TitledBorder("Game Startup")); gamePane.setLayout(new GridLayout(0, 3)); mainPane.add(gamePane); JButton button1 = new JButton(Constants.newGame); button1.setMnemonic(KeyEvent.VK_N); gamePane.add(button1); button1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doNewGame(); } }); JButton button2 = new JButton(Constants.loadGame); button2.setMnemonic(KeyEvent.VK_L); gamePane.add(button2); button2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doLoadGame(); } }); JButton button4 = new JButton(Constants.quitGame); button4.setMnemonic(KeyEvent.VK_Q); gamePane.add(button4); button4.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doQuit(); } }); enablePlayers(); pack(); saveWindow = new SaveWindow(options, "GetPlayers"); Point loadLocation = saveWindow.loadLocation(); if (loadLocation == null) { // if we would save&restore both size + pos, // could use KFrame's restoreOrCenter and don't // need own centerOnScreen in here... centerOnScreen(); } else { setLocation(loadLocation); } addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { doQuit(); } }); setVisible(true); serveAtPortBox.setEditable(true); WelcomeDialog.showWelcomeDialogMaybe(options); if (SystemInfo.isOracleJava7()) { // warnAboutJava7(); } } private void actOnVariantChange(int oldMaxPlayers, String newVarName) { Variant variant = VariantSupport.loadVariantByName(newVarName, true); Document doc = variant.getReadme(); // If loading failed, change variantBox to what is actually now // loaded. Probably Default. String loadedName = variant.getName(); if (!loadedName.equals(variantBox.getSelectedItem())) { LOGGER.info("Ups, loaded variant '" + loadedName + "' does not match selected item in variantBox '" + variantBox.getSelectedItem() + "'"); // TODO somewhat hacked to just re-set it to "Default" // We just use the default from variant names array, // otherwise we would have to search through all items // ComboBox changes only if it is the same *object*) variantBox.setSelectedItem(Constants.variantArray[0]); newVarName = loadedName; doc = VariantSupport.getCurrentVariant().getReadme(); } else { LOGGER.finest("OK, loaded variant (" + loadedName + ") matches selected item in variantBox"); } options.setOption(Options.variant, newVarName); String varFileWithFullPath = VariantSupport .getFullPathFileForVariantName(newVarName); if (varFileWithFullPath == null) { varFileWithFullPath = "null"; } options .setOption(Options.variantFileWithFullPath, varFileWithFullPath); String prop = (String)doc .getProperty(StaticResourceLoader.KEY_CONTENT_TYPE); readme.setContentType(prop); readme.setDocument(doc); if (oldMaxPlayers != VariantSupport.getMaxPlayers()) { enablePlayers(); } } private void setRunningOnLabel(int port) { /* 11.2.2015: Removed the "on host" part, because with Java 7 * under certain circumstances the InetAdress.getLocalHostname() * might be stuck a minute or so, before it times out. */ String runningOnString = "Running Colossus Version " + BuildInfo.getReleaseVersion() + " (" + BuildInfo.getRevisionInfoString() + ")" + ", listening at port " + port; runningOnLabel.setText(runningOnString); } private void setDelayLabel(int delay) { delayLabel.setText(" Current AI delay: " + delay + " ms "); } private void setTimeLimitLabel(int limit) { timeLimitLabel.setText(" Current AI time limit: " + limit + " s "); } private void setupTypeChoices() { typeChoices.clear(); typeChoices.add(Constants.human); typeChoices.add(Constants.network); for (int i = 0; i < Constants.numAITypes; i++) { if (!(Constants.aiArray[i].equals(""))) { typeChoices.add(Constants.aiArray[i]); } } // Only show random AI choice if more than one AI. if (Constants.numAITypes >= 2) { typeChoices.add(Constants.anyAI); } typeChoices.add(Constants.none); } private void doOnePlayer(final int i, Container allPlayersPane) { JPanel onePlayerPane = new JPanel(); onePlayerPane.setLayout(new GridLayout(0, 3)); allPlayersPane.add(onePlayerPane); String s = "Player " + (i + 1); onePlayerPane.add(new JLabel(s)); final JComboBox<String> playerType = new JComboBox<String>(typeChoices); // Avoid scrolling (otherwise "Human" might not be visible directly) // the number is chosen to be larger then the current choices but not // too large to cause havoc if the number of choices increases playerType.setMaximumRowCount(12); String type = options.getStringOption(Options.playerType + i, Constants.none); playerType.setSelectedItem(type); onePlayerPane.add(playerType); playerType.addActionListener(new ActionListener() { private final int row = i; public void actionPerformed(ActionEvent e) { // If player type was changed to none, also change // player name to none. String value = (String)playerType.getSelectedItem(); if (value.equals(Constants.none)) { playerNames[row].setSelectedItem(Constants.none); } else if (value.equals(Constants.network)) { playerNames[row].setSelectedItem(Constants.byClient); } else if (value.endsWith(Constants.ai)) { playerNames[row].setSelectedItem(Constants.byColor); } else if (value.endsWith(Constants.human)) { String uniqueHuman = makeUniqueName(Constants.username, row); playerNames[row].setSelectedItem(uniqueHuman); } // If player type was changed away from none, also // change player name to something else. else if (playerNames[row].getSelectedItem().equals( Constants.none)) { playerNames[row].setSelectedItem(Constants.byColor); } } }); playerType.setEnabled(false); playerTypes[i] = playerType; String cmdlineName = options.getStringOption(Options.playerName); String name = options.getStringOption(Options.playerName + i); if (cmdlineName != null) { name = cmdlineName; } else if (name == null || name.length() == 0) { name = Constants.none; } else if (name.startsWith(Constants.byColor)) { name = Constants.byColor; } else if (name.startsWith(Constants.byType)) { name = Constants.byType; } else if (name.startsWith(Constants.byClient)) { name = Constants.byClient; } Vector<String> nameChoices = new Vector<String>(); nameChoices.add(name); if (!nameChoices.contains(Constants.byColor)) { nameChoices.add(Constants.byColor); } if (!nameChoices.contains(Constants.byType)) { nameChoices.add(Constants.byType); } if (!nameChoices.contains(Constants.username)) { nameChoices.add(Constants.username); } if (!nameChoices.contains(Constants.none)) { nameChoices.add(Constants.none); } final JComboBox<String> playerName = new JComboBox<String>(nameChoices); // setEditable moved to enablePlayers; if done here, it // was hanging for 1 minutes or so. onePlayerPane.add(playerName); playerName.addActionListener(new ActionListener() { final int row = i; public void actionPerformed(ActionEvent e) { // If player name was changed to none, also change // player type to none. String value = (String)playerName.getSelectedItem(); if (value.equals(Constants.none)) { playerTypes[row].setSelectedItem(Constants.none); } // If player type was changed away from none, also // change player name to something else. else if (playerTypes[row].getSelectedItem().equals( Constants.none)) { playerTypes[row].setSelectedItem(Constants.anyAI); } } }); playerName.setEnabled(false); playerNames[i] = playerName; } private void enablePlayers() { int maxPlayers = VariantSupport.getMaxPlayers(); for (int i = 0; i < Constants.MAX_MAX_PLAYERS; i++) { playerTypes[i].setEnabled(i < maxPlayers); playerNames[i].setEnabled(i < maxPlayers); playerNames[i].setEditable(true); } } private void addCheckbox(final String optname, Container pane) { JCheckBox cb = new JCheckBox(optname); cb.setSelected(options.getOption(optname)); cb.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { boolean selected = (e.getStateChange() == ItemEvent.SELECTED); options.setOption(optname, selected); } }); pane.add(cb); } /** Check if values are legal; if yes, caller can start the game. */ private boolean validateInputs() { options.clearPlayerInfo(); int numPlayers = 0; int numAIs = 0; List<String> names = new ArrayList<String>(); List<String> types = new ArrayList<String>(); StringBuilder sb = new StringBuilder(""); for (int i = 0; i < VariantSupport.getMaxPlayers(); i++) { String name = (String)(playerNames[i].getSelectedItem()); String type = (String)(playerTypes[i].getSelectedItem()); if (name.length() > 0 && !name.equals(Constants.none) && !type.equals(Constants.none)) { if (!type.equals(Constants.network) && !type.equals(Constants.human)) { numAIs++; } // Force all network players to byClient. if (type.equals(Constants.network)) { name = Constants.byClient; } // Don't allow local players to be called byClient. else if (name.startsWith(Constants.byClient)) { name = Constants.byColor; } // Make by* names unique by appending row number. if (name.equals(Constants.byColor) || name.equals(Constants.byType) || name.equals(Constants.byClient)) { name = name + i; } // Duplicate names are not allowed. if (names.contains(name)) { JOptionPane.showMessageDialog(this, "Duplicate player name '" + name + "'!"); options.clearPlayerInfo(); return false; } numPlayers++; names.add(name); types.add(type); } if (type.equals(Constants.human) && (name.startsWith(Constants.byColor) || name .startsWith(Constants.byType))) { sb.append("Invalid name \"" + name + "\" for Player " + (i + 1) + "\n"); } } if (numAIs >= numPlayers && options.getOption(Options.autoStop)) { String message = "Option '" + Options.autoStop + "' should be disabled when all players are AIs!"; JOptionPane.showMessageDialog(this, message); return false; } // preserve the non-used ones // (e.g. 7-12 for 6 player variant) for (int i = VariantSupport.getMaxPlayers(); i < Constants.MAX_MAX_PLAYERS; i++) { String name = (String)(playerNames[i].getSelectedItem()); String type = (String)(playerTypes[i].getSelectedItem()); if (name.length() > 0 && !name.equals(Constants.none) && !type.equals(Constants.none)) { options.setOption(Options.playerName + i, name); options.setOption(Options.playerType + i, type); } } if (sb.length() > 0) { String message = sb.substring(0) + "\n" + "Reason: <by...> names are not allowed for human players!"; JOptionPane.showMessageDialog(this, message); return false; } // Exit if there aren't enough unique player names. if (numPlayers < 1 || names.size() != numPlayers) { JOptionPane.showMessageDialog(this, "Not enough different unique player names!"); options.clearPlayerInfo(); return false; } // Okay. Copy names and types to options. for (int i = 0; i < numPlayers; i++) { String name = names.get(i); options.setOption(Options.playerName + i, name); String type = types.get(i); options.setOption(Options.playerType + i, type); } return true; } private void doLoadGame() { JFileChooser chooser = new JFileChooser(Constants.SAVE_DIR_NAME); chooser.setFileFilter(new XMLSnapshotFilter()); int returnVal = chooser.showOpenDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { // Set startObject to "load game" and value to filename. String filename = chooser.getSelectedFile().getPath(); stOptions.setOption(Options.loadGameFileName, filename); stOptions.setOption(Options.serveAtPort, serveAtPort); options.setOption(Options.serveAtPort, serveAtPort); whatNextManager.setWhatToDoNext(WhatToDoNext.LOAD_GAME, false, true); dispose(); } } private void doClientDialog() { whatNextManager.setWhatToDoNext(WhatToDoNext.NET_CLIENT_DIALOG, false); dispose(); } private void doRunWebClient() { whatNextManager.setWhatToDoNext(WhatToDoNext.START_WEB_CLIENT, false); dispose(); } private boolean quitAlreadyTried = false; private void doQuit() { // If "clean quit" fails (e.g. one modifies the Colossus.jar // while game is running;-), provide a way that on 2nd click // one can get rid of the application... if (quitAlreadyTried) { LOGGER.log(Level.SEVERE, "It seems the clean 'Quit' did fail - doing it now " + "the hard way using System.exit(1)", (Throwable)null); System.exit(1); } quitAlreadyTried = true; whatNextManager.setWhatToDoNext(WhatToDoNext.QUIT_ALL, true); dispose(); } /** package privat for unit test case */ void doNewGame() { boolean ok = validateInputs(); if (ok) { if (!byWebClient) { whatNextManager .setWhatToDoNext(WhatToDoNext.START_GAME, false); stOptions.setOption(Options.serveAtPort, serveAtPort); stOptions.setOption(Options.FORCE_BOARD, true); options.setOption(Options.serveAtPort, serveAtPort); } dispose(); } else { // ValidateInputs showed an error message box. } } static class varFileFilter extends javax.swing.filechooser.FileFilter { @Override public boolean accept(File f) { if (f.isDirectory()) { return (true); } if (f.getName().endsWith(Constants.varEnd)) { return (true); } return (false); } @Override public String getDescription() { return ("Colossus VARiant file"); } } private void doLoadVariant() { int maxPlayers = VariantSupport.getMaxPlayers(); javax.swing.JFileChooser varChooser = new JFileChooser( Constants.DEFAULT_COLOSSUS_HOME); varChooser.setFileFilter(new varFileFilter()); varChooser .setDialogTitle("Choose your variant (or cancel for default game)"); int returnVal = varChooser.showOpenDialog(this); if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) { File varFile = varChooser.getSelectedFile().getAbsoluteFile(); Variant variant = VariantSupport.loadVariantByFile(varFile, true); Document doc = variant.getReadme(); if (doc != null) { String name = varFile.getName(); String fullPath = varFile.getPath(); name = name.substring(0, name.lastIndexOf(Constants.varEnd)); options.setOption(Options.variant, name); options.setOption(Options.variantFileWithFullPath, fullPath); readme.setContentType((String)doc .getProperty(StaticResourceLoader.KEY_CONTENT_TYPE)); readme.setDocument(doc); if (maxPlayers != VariantSupport.getMaxPlayers()) { enablePlayers(); } } } } private String makeUniqueName(String baseName, int i) { StringBuilder tryName = new StringBuilder(baseName); boolean duplicate = true; while (duplicate) { duplicate = false; for (int j = 0; j < Constants.MAX_MAX_PLAYERS; j++) { if (j != i) { String otherBoxName = (String)playerNames[j] .getSelectedItem(); if (tryName.toString().equals(otherBoxName)) { duplicate = true; } } } if (duplicate) { int nr; // on first attempt, take row number if (tryName.toString().equals(Constants.username)) { nr = i; } // if that does not help, random until unique else { nr = Dice.rollDie(); } tryName.append(String.valueOf(nr)); } } return tryName.toString(); } private void addVariantToBoxIfNeeded(String varName) { int cnt = variantBox.getItemCount(); for (int i = 0; i < cnt; i++) { String item = variantBox.getItemAt(i); if (item.equals(varName)) { return; } } variantBox.addItem(varName); } /* * Eliminate dialog and notify mutex, allowing game to start, * or main to run web client or netclient dialog. */ @Override public void dispose() { if (!options.isEmpty()) { saveWindow.saveLocation(getLocation()); } options.saveOptions(); // some cleanup, to ensure proper GC: readme.getParent().remove(readme); tabbedPane.remove(readmeScrollPane); readmeScrollPane = null; readme = null; // Dispose dialog and notify main() so that game starts: super.dispose(); synchronized (mutex) { mutexNotified = true; mutex.notify(); } } /** Only meant for unit tests */ void setPlayerType(int i, String type) { playerTypes[i].setSelectedItem(type); } public boolean getMutexNotified() { return mutexNotified; } /** * Displays the warning if it hasn't been shown for REMINDER_INTERVAL_DAYS * (or never before at all), and if yes, write back today's date. * I abuse String here, because I don't really want to build the whole * set of getter, setter, trigger, ... for long just for this one case. */ /* * 27.11.2015: Not really relevant any more. The battleboard issue can't * actually be caused by it, because there has been at least one game where * both players used Java 8 and it still happened. * Now retrospectively, perhaps this "fixed in java 8 but not 7" was not * actually about this, but about the "do not click anything outside a * modal dialog, otherwise things hang (which impacted the inactivity- * autoplay warning dialog. * * TODO: if Options.lastJava7Warning option is found in options file, * remove it. */ /* private void warnAboutJava7() { long now = new Date().getTime(); String lastTimeString = options .getStringOption(Options.lastJava7Warning); long lastTime = -1; if (lastTimeString != null) { lastTime = Long.parseLong(lastTimeString); } if (lastTime == -1 || (now - lastTime >= REMINDER_INTERVAL_DAYS * 24 * 60 * 60)) { options.setOption(Options.lastJava7Warning, Long.toString(now)); String message = "Please note:\n" + "It looks you are using Oracle Java 7 (\"" + SystemInfo.getDisplayJavaInfo() + "\").\n\n" + "There are known issues with Colossus and Oracle Java version 7, which\n" + "might cause problems, in worst case crash the game.\n" + "For example, the 'no battleboard comes up' problem is caused by this.\n" + "\n" + "If possible, use a different Java version, for example Oracle Java 5, 6, or 8,\n" + "or a Java JRE from a different vendor (like OpenJDK/OpenJRE)." + "\n\nYou have been warned :-)"; JOptionPane.showMessageDialog(this, message, "Oracle Java 7", JOptionPane.WARNING_MESSAGE); } } */ }