Skip to content

Commit

Permalink
Allow printing unit list from MM
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelbraginskiy committed Nov 9, 2024
1 parent 001bcc4 commit c4372d6
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 7 deletions.
6 changes: 6 additions & 0 deletions megamek/i18n/megamek/client/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,8 @@ ChatLounge.butGroundMap = Ground Map
ChatLounge.butNames=Random Names...
ChatLounge.butRemoveBot=Remove Bot
ChatLounge.butSaveList=Save Unit List...
ChatLounge.butPrintList=Print Unit List...
ChatLounge.butPrintList.printing=Loading print dialog
ChatLounge.butShrink=<
ChatLounge.butSkills=Random Skills...
ChatLounge.butShowUnitID=Show IDs
Expand Down Expand Up @@ -1270,6 +1272,10 @@ CommonSettingsDialog.logFileName=Game log filename:
CommonSettingsDialog.userDir=User Files Directory:
CommonSettingsDialog.userDir.tooltip=<html>Use this directory for resources you want to share between different installs or versions of MegaMek, MegaMekLab and MekHQ. Fonts, units, camos, portraits and fluff images will also be loaded from this directory.<BR>Note: Inside the user directory, use the directory structure of MM/MML/MHQ for camos, portraits and fluff images, i.e. data/images/camo, data/images/portraits and data/images/fluff/. <BR>Fonts and units can be placed anywhere in the user directory.
CommonSettingsDialog.userDir.chooser.title=Choose User Data Folder
CommonSettingsDialog.mmlPath=Path to MegaMekLab Executable:
CommonSettingsDialog.mmlPath.tooltip=<html>Used for printing unit lists.\
<br>MegaMek will try to autodetect this when the option is blank if MM and MML are installed together.</html>
CommonSettingsDialog.mmlPath.chooser.title=Select MegaMekLab Executable
CommonSettingsDialog.main=Main
CommonSettingsDialog.audio=Audio
CommonSettingsDialog.miniMap=Mini Map
Expand Down
65 changes: 65 additions & 0 deletions megamek/src/megamek/client/ui/swing/ClientGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,71 @@ public void saveListFile(ArrayList<Entity> unitList, String filename) {
}
}

public void printList(ArrayList<Entity> unitList, JButton button) {
if ((unitList == null) || unitList.isEmpty()) {
return;
}

var mmlPath = CP.getMmlPath();
var autodetect = false;
if (null == mmlPath || mmlPath.isBlank()) {
autodetect = true;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
mmlPath = "MegaMekLab.exe";
} else {
mmlPath = "MegaMekLab.sh";
}
}

var mml = new File(mmlPath);

if (!mml.canExecute()) {
if (autodetect) {
logger.error("Could not auto-detect MegaMekLab! Please configure the path to the MegaMekLab executable in the settings.", "Error printing unit list");
} else {
logger.error("%s does not appear to be an executable! Please configure the path to the MegaMekLab executable in the settings.".formatted(mml.getName()), "Error printing unit list");
}
return;
}

try {
var unitFile = File.createTempFile("MegaMekPrint", ".mul");

Check warning

Code scanning / CodeQL

Local information disclosure in a temporary directory Medium

Local information disclosure vulnerability due to use of file readable by other local users.
EntityListFile.saveTo(unitFile, unitList);
String[] command;
if (mml.getName().toLowerCase().contains("gradle")) {
command = new String[] {
mml.getAbsolutePath(),
"run",
"--args=%s --no-startup".formatted(unitFile.getAbsolutePath())
};
} else {
command = new String[] {
mml.getAbsolutePath(),
unitFile.getAbsolutePath(),
"--no-startup"
};
}
button.setText(Messages.getString("ChatLounge.butPrintList.printing"));
logger.info("Running command: {}", String.join(" ", command));
var p = new ProcessBuilder(command)
.directory(mml.getAbsoluteFile().getParentFile())
.inheritIO()
.start();
new Thread(() -> {
try {
p.waitFor();
} catch (InterruptedException e) {
logger.error(e);
} finally {
button.setText(Messages.getString("ChatLounge.butPrintList"));
}
}).start();

} catch (Exception e) {
logger.error(e, "Operation failed", "Error printing unit list");
}
}

protected void saveVictoryList() {
String filename = client.getLocalPlayer().getName();

Expand Down
36 changes: 31 additions & 5 deletions megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ private <T> void moveElement(DefaultListModel<T> srcModel, int srcIndex, int trg
private JTextField tfSoundMuteOthersFileName;

private JTextField userDir;
private JTextField mmlPath;
private final JCheckBox keepGameLog = new JCheckBox(Messages.getString("CommonSettingsDialog.keepGameLog"));
private JTextField gameLogFilename;
private final JCheckBox stampFilenames = new JCheckBox(Messages.getString("CommonSettingsDialog.stampFilenames"));
Expand Down Expand Up @@ -1724,6 +1725,23 @@ private JPanel getSettingsPanel() {

addLineSpacer(comps);

JLabel mmlPathLabel = new JLabel(Messages.getString("CommonSettingsDialog.mmlPath"));
mmlPathLabel.setToolTipText(Messages.getString("CommonSettingsDialog.mmlPath.tooltip"));
mmlPath = new JTextField(20);
mmlPath.setMaximumSize(new Dimension(250, 40));
mmlPath.setToolTipText(Messages.getString("CommonSettingsDialog.mmlPath.tooltip"));
JButton mmlPathChooser = new JButton("...");
mmlPathChooser.addActionListener(e ->
fileChoose(mmlPath, getFrame(), Messages.getString("CommonSettingsDialog.mmlPath.chooser.title"), false));
row = new ArrayList<>();
row.add(mmlPathLabel);
row.add(mmlPath);
row.add(Box.createHorizontalStrut(10));
row.add(mmlPathChooser);
comps.add(row);

addLineSpacer(comps);

// UI Theme
uiThemes = new JComboBox<>();
uiThemes.setMaximumSize(new Dimension(400, uiThemes.getMaximumSize().height));
Expand Down Expand Up @@ -1944,6 +1962,7 @@ public void setVisible(boolean visible) {
gameLogFilename.setEnabled(keepGameLog.isSelected());
gameLogFilename.setText(CP.getGameLogFilename());
userDir.setText(CP.getUserDir());
mmlPath.setText(CP.getMmlPath());
stampFilenames.setSelected(CP.stampFilenames());
stampFormat.setEnabled(stampFilenames.isSelected());
stampFormat.setText(CP.getStampFormat());
Expand Down Expand Up @@ -2421,6 +2440,7 @@ protected void okAction() {
CP.setKeepGameLog(keepGameLog.isSelected());
CP.setGameLogFilename(gameLogFilename.getText());
CP.setUserDir(userDir.getText());
CP.setMmlPath(mmlPath.getText());
CP.setStampFilenames(stampFilenames.isSelected());
CP.setStampFormat(stampFormat.getText());
CP.setReportKeywords(reportKeywordsTextPane.getText());
Expand Down Expand Up @@ -3452,13 +3472,19 @@ public static List<String> filteredFilesWithSubDirs(File path, String fileEnding
* @param parent The parent JFrame of the settings dialog
*/
public static void fileChooseUserDir(JTextField userDirTextField, JFrame parent) {
JFileChooser userDirChooser = new JFileChooser(userDirTextField.getText());
userDirChooser.setDialogTitle(Messages.getString("CommonSettingsDialog.userDir.chooser.title"));
userDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChoose(userDirTextField, parent, Messages.getString("CommonSettingsDialog.userDir.chooser.title"),true);
}

private static void fileChoose(JTextField textField, JFrame parent, String title, boolean directories) {
JFileChooser userDirChooser = new JFileChooser(textField.getText());
userDirChooser.setDialogTitle(title);
if (directories) {
userDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
int returnVal = userDirChooser.showOpenDialog(parent);
if ((returnVal == JFileChooser.APPROVE_OPTION) && (userDirChooser.getSelectedFile() != null)
&& userDirChooser.getSelectedFile().isDirectory()) {
userDirTextField.setText(userDirChooser.getSelectedFile().toString());
&& (directories ? userDirChooser.getSelectedFile().isDirectory() : userDirChooser.getSelectedFile().isFile())) {
textField.setText(userDirChooser.getSelectedFile().toString());
}
}
}
17 changes: 15 additions & 2 deletions megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public class ChatLounge extends AbstractPhaseDisplay implements
private JButton butNames = new JButton(Messages.getString("ChatLounge.butNames"));
private JButton butLoadList = new JButton(Messages.getString("ChatLounge.butLoadList"));
private JButton butSaveList = new JButton(Messages.getString("ChatLounge.butSaveList"));
private JButton butPrintList = new JButton(Messages.getString("ChatLounge.butPrintList"));

/* Unit Table */
private MekTable mekTable;
Expand Down Expand Up @@ -276,6 +277,7 @@ public class ChatLounge extends AbstractPhaseDisplay implements

private static final String CL_ACTIONCOMMAND_LOADLIST = "load_list";
private static final String CL_ACTIONCOMMAND_SAVELIST = "save_list";
private static final String CL_ACTIONCOMMAND_PRINTLIST = "print_list";
private static final String CL_ACTIONCOMMAND_LOADMEK = "load_mek";
private static final String CL_ACTIONCOMMAND_ADDBOT = "add_bot";
private static final String CL_ACTIONCOMMAND_REMOVEBOT = "remove_bot";
Expand Down Expand Up @@ -365,6 +367,7 @@ private void setupListeners() {
butRandomMap.addActionListener(lobbyListener);
butRemoveBot.addActionListener(lobbyListener);
butSaveList.addActionListener(lobbyListener);
butPrintList.addActionListener(lobbyListener);
butShowUnitID.addActionListener(lobbyListener);
butSkills.addActionListener(lobbyListener);
butSpaceSize.addActionListener(lobbyListener);
Expand Down Expand Up @@ -546,6 +549,8 @@ private void setupUnitConfig() {
butLoadList.setEnabled(mscLoaded);
butSaveList.setActionCommand(CL_ACTIONCOMMAND_SAVELIST);
butSaveList.setEnabled(false);
butPrintList.setActionCommand(CL_ACTIONCOMMAND_PRINTLIST);
butPrintList.setEnabled(false);
butAdd.setEnabled(mscLoaded);
butAdd.setActionCommand(CL_ACTIONCOMMAND_LOADMEK);
butArmy.setEnabled(mscLoaded);
Expand All @@ -561,6 +566,7 @@ private void setupUnitConfig() {
panUnitInfoGrid.add(butLoadList);
panUnitInfoGrid.add(butSaveList);
panUnitInfoGrid.add(butNames);
panUnitInfoGrid.add(butPrintList);

panUnitInfo.add(panUnitInfoAdd);
panUnitInfo.add(panUnitInfoGrid);
Expand Down Expand Up @@ -1739,7 +1745,7 @@ public void actionPerformed(ActionEvent ev) {
}
clientgui.loadListFile(c.getLocalPlayer());

} else if (ev.getSource().equals(butSaveList)) {
} else if (ev.getSource().equals(butSaveList) || ev.getSource().equals(butPrintList)) {
// Allow the player to save their current
// list of entities to a file.
Client c = getSelectedClient();
Expand All @@ -1752,7 +1758,11 @@ public void actionPerformed(ActionEvent ev) {
for (Entity entity : entities) {
entity.setForceString(game().getForces().forceStringFor(entity));
}
clientgui.saveListFile(entities, c.getLocalPlayer().getName());
if (ev.getSource().equals(butSaveList)) {
clientgui.saveListFile(entities, c.getLocalPlayer().getName());
} else {
clientgui.printList(entities, (JButton) ev.getSource());
}

} else if (ev.getSource().equals(butAddBot)) {
configAndCreateBot(null);
Expand Down Expand Up @@ -2285,6 +2295,7 @@ public void removeAllListeners() {
butRandomMap.removeActionListener(lobbyListener);
butRemoveBot.removeActionListener(lobbyListener);
butSaveList.removeActionListener(lobbyListener);
butPrintList.removeActionListener(lobbyListener);
butShowUnitID.removeActionListener(lobbyListener);
butSkills.removeActionListener(lobbyListener);
butSpaceSize.removeActionListener(lobbyListener);
Expand Down Expand Up @@ -2404,10 +2415,12 @@ private void refreshPlayerConfig() {
// Disable the Remove Bot button for the "player" of a "Connect As Bot" client
butRemoveBot.setEnabled(isSingleLocalBot);
butSaveList.setEnabled(false);
butPrintList.setEnabled(false);
if (isSinglePlayer) {
var selPlayer = theElement(selPlayers);
var hasUnits = !game().getPlayerEntities(selPlayer, false).isEmpty();
butSaveList.setEnabled(hasUnits && unitsVisible(selPlayer));
butPrintList.setEnabled(hasUnits && unitsVisible(selPlayer));
setTeamSelectedItem(selPlayer.getTeam());
}
}
Expand Down
11 changes: 11 additions & 0 deletions megamek/src/megamek/common/preference/ClientPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public class ClientPreferences extends PreferenceStoreProxy {
*/
public static final String USER_DIR = "UserDir";

public static final String MML_PATH = "MmlPath";

// endregion Variable Declarations

// region Constructors
Expand Down Expand Up @@ -103,6 +105,7 @@ public ClientPreferences(IPreferenceStore store) {
store.setDefault(IP_ADDRESSES_IN_CHAT, false);
store.setDefault(START_SEARCHLIGHTS_ON, true);
store.setDefault(USER_DIR, "");
store.setDefault(MML_PATH, "");
setLocale(store.getString(LOCALE));
setMekHitLocLog();
}
Expand Down Expand Up @@ -406,4 +409,12 @@ public void setUserDir(String userDir) {
}
store.setValue(USER_DIR, userDir);
}

public String getMmlPath() {
return store.getString(MML_PATH);
}

public void setMmlPath(String mmlPath) {
store.setValue(MML_PATH, mmlPath.isBlank() ? "" : new File(mmlPath).getAbsolutePath());
}
}

0 comments on commit c4372d6

Please sign in to comment.