simplified loading of SlipstreamConfig

This commit is contained in:
Leyla Becker 2025-08-16 22:17:07 -05:00
parent 9e8e830ab1
commit ccd50b0275
3 changed files with 84 additions and 128 deletions

View file

@ -1,19 +1,14 @@
package net.vhati.modmanager;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Properties;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
@ -36,8 +31,8 @@ public class FTLModManager {
public static final String APP_NAME = "Slipstream Mod Manager";
public static final ComparableVersion APP_VERSION = new ComparableVersion( "1.9.1" );
public static final String APP_URL = "https://subsetgames.com/forum/viewtopic.php?f=12&t=17102";
public static final String APP_AUTHOR = "Vhati";
public static final String APP_URL = "TODO";
public static final String APP_AUTHOR = "jan-leila";
public static void main( String[] args ) {
@ -53,7 +48,7 @@ public class FTLModManager {
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext( lc );
encoder.setCharset( Charset.forName( "UTF-8" ) );
encoder.setCharset(StandardCharsets.UTF_8);
encoder.setPattern( "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" );
encoder.start();
@ -76,7 +71,7 @@ public class FTLModManager {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException( Thread t, Throwable e ) {
log.error( "Uncaught exception in thread: "+ t.toString(), e );
log.error("Uncaught exception in thread: {}", t.toString(), e);
}
});
@ -86,20 +81,15 @@ public class FTLModManager {
}
// Ensure all popups are triggered from the event dispatch thread.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
guiInit();
}
});
SwingUtilities.invokeLater(FTLModManager::guiInit);
}
private static void guiInit() {
try {
// TODO: get mods file from env var
// Nag if the jar was double-clicked.
if ( new File( "./mods/" ).exists() == false ) {
if (!new File("./mods/").exists()) {
String currentPath = new File( "." ).getAbsoluteFile().getParentFile().getAbsolutePath();
log.error( String.format( "Slipstream could not find its own folder (Currently in \"%s\"), exiting...", currentPath ) );
@ -108,46 +98,13 @@ public class FTLModManager {
throw new ExitException();
}
// TODO: get config file from env var
File configFile = new File( "modman.cfg" );
boolean writeConfig = false;
Properties props = new Properties();
props.setProperty( SlipstreamConfig.ALLOW_ZIP, "false" );
props.setProperty( SlipstreamConfig.FTL_DATS_PATH, "" ); // Prompt.
props.setProperty( SlipstreamConfig.STEAM_DISTRO, "" ); // Prompt.
props.setProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ); // Prompt.
props.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "" ); // Prompt.
props.setProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" );
props.setProperty( SlipstreamConfig.UPDATE_CATALOG, "" ); // Prompt.
props.setProperty( SlipstreamConfig.UPDATE_APP, "" ); // Prompt.
props.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" );
props.setProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" );
// "manager_geometry" doesn't have a default.
// Read the config file.
InputStream in = null;
try {
if ( configFile.exists() ) {
log.debug( "Loading config file" );
in = new FileInputStream( configFile );
props.load( new InputStreamReader( in, "UTF-8" ) );
} else {
writeConfig = true; // Create a new cfg, but only if necessary.
}
}
catch ( IOException e ) {
log.error( "Error loading config", e );
showErrorDialog( "Error loading config from "+ configFile.getPath() );
}
finally {
try {if ( in != null ) in.close();}
catch ( IOException e ) {}
}
SlipstreamConfig appConfig = new SlipstreamConfig( props, configFile );
SlipstreamConfig appConfig = new SlipstreamConfig(configFile);
// Look-and-Feel.
boolean useDefaultUI = "true".equals( appConfig.getProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" ) );
boolean useDefaultUI = Boolean.parseBoolean(appConfig.getProperty(SlipstreamConfig.USE_DEFAULT_UI, "false"));
if ( !useDefaultUI ) {
LookAndFeel defaultLaf = UIManager.getLookAndFeel();
@ -157,7 +114,7 @@ public class FTLModManager {
log.debug( "Setting system look and feel: "+ UIManager.getSystemLookAndFeelClassName() );
// SystemLaf is risky. It may throw an exception, or lead to graphical bugs.
// Problems are geneally caused by custom Windows themes.
// Problems are generally caused by custom Windows themes.
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
}
catch ( Exception e ) {
@ -165,7 +122,6 @@ public class FTLModManager {
log.info( "Setting "+ SlipstreamConfig.USE_DEFAULT_UI +"=true in the config file to prevent this error..." );
appConfig.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "true" );
writeConfig = true;
try {
UIManager.setLookAndFeel( defaultLaf );
@ -220,7 +176,6 @@ public class FTLModManager {
if ( datsDir != null ) {
appConfig.setProperty( SlipstreamConfig.FTL_DATS_PATH, datsDir.getAbsolutePath() );
writeConfig = true;
log.info( "FTL dats located at: "+ datsDir.getAbsolutePath() );
}
}
@ -237,11 +192,9 @@ public class FTLModManager {
int steamBasedResponse = JOptionPane.showConfirmDialog( null, "Was FTL installed via Steam?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
if ( steamBasedResponse == JOptionPane.YES_OPTION ) {
appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "true" );
writeConfig = true;
}
else {
appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "false" );
writeConfig = true;
}
}
@ -295,7 +248,6 @@ public class FTLModManager {
if ( steamExeFile != null ) {
appConfig.setProperty( SlipstreamConfig.STEAM_EXE_PATH, steamExeFile.getAbsolutePath() );
writeConfig = true;
log.info( "Steam located at: "+ steamExeFile.getAbsolutePath() );
}
}
@ -308,11 +260,9 @@ public class FTLModManager {
int launchResponse = JOptionPane.showOptionDialog( null, "Would you prefer to launch FTL directly, or via Steam?", "How to Launch?", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, launchOptions, launchOptions[1] );
if ( launchResponse == 0 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
writeConfig = true;
}
else if ( launchResponse == 1 ) {
appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "true" );
writeConfig = true;
}
}
}
@ -340,18 +290,6 @@ public class FTLModManager {
appConfig.setProperty( SlipstreamConfig.UPDATE_CATALOG, "0" );
appConfig.setProperty( SlipstreamConfig.UPDATE_APP, "0" );
}
writeConfig = true;
}
if ( writeConfig ) {
try {
appConfig.writeConfig();
}
catch ( IOException e ) {
String errorMsg = String.format( "Error writing config to \"%s\"", configFile.getPath() );
log.error( errorMsg, e );
showErrorDialog( errorMsg );
}
}
ManagerFrame frame = null;
@ -388,8 +326,6 @@ public class FTLModManager {
JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE );
}
private static class ExitException extends RuntimeException {
public ExitException() {
}

View file

@ -88,7 +88,7 @@ public class SlipstreamCLI {
}
File configFile = new File( "modman.cfg" );
SlipstreamConfig appConfig = getConfig( configFile );
SlipstreamConfig appConfig = new SlipstreamConfig( configFile );
if ( slipstreamCmd.listMods ) {
listMods(appConfig);
@ -353,48 +353,6 @@ public class SlipstreamCLI {
return true;
}
/**
* Loads settings from a config file.
*
* If an error occurs, it'll be logged,
* and default settings will be returned.
*/
private static SlipstreamConfig getConfig( File configFile ) {
Properties props = new Properties();
props.setProperty( SlipstreamConfig.ALLOW_ZIP, "false" );
props.setProperty( SlipstreamConfig.FTL_DATS_PATH, "" );
props.setProperty( SlipstreamConfig.STEAM_EXE_PATH, "" );
props.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
props.setProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" );
props.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" );
props.setProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" );
// "update_catalog" doesn't have a default.
// "update_app" doesn't have a default.
// "manager_geometry" doesn't have a default.
// Read the config file.
InputStream in = null;
try {
if ( configFile.exists() ) {
log.trace( "Loading properties from config file" );
in = new FileInputStream( configFile );
props.load( new InputStreamReader( in, "UTF-8" ) );
}
}
catch ( IOException e ) {
log.error( "Error loading config", e );
}
finally {
try {if ( in != null ) in.close();}
catch ( IOException e ) {}
}
SlipstreamConfig appConfig = new SlipstreamConfig( props, configFile );
return appConfig;
}
/**
* Checks the validity of the config's dats path and returns it.
* Or exits if the path is invalid.

View file

@ -1,18 +1,21 @@
package net.vhati.modmanager.core;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
public class SlipstreamConfig {
private static final Logger log = LoggerFactory.getLogger( SlipstreamConfig.class );
public static final String ALLOW_ZIP = "allow_zip";
public static final String FTL_DATS_PATH = "ftl_dats_path";
@ -26,12 +29,35 @@ public class SlipstreamConfig {
public static final String REMEMBER_GEOMETRY = "remember_geometry";
public static final String MANAGER_GEOMETRY = "manager_geometry";
private Properties config;
private File configFile;
private final Properties config;
private final File configFile;
private final AtomicBoolean shutdownHookInitialized;
public SlipstreamConfig(File configFile) {
this.shutdownHookInitialized = new AtomicBoolean(false);
config = getProperties();
// Read the config file.
InputStream in = null;
try {
if ( configFile.exists() ) {
log.debug( "Loading config file" );
in = new FileInputStream( configFile );
config.load( new InputStreamReader( in, StandardCharsets.UTF_8) );
scheduleShutdown();
}
}
catch ( IOException e ) {
log.error( "Error loading config", e );
showErrorDialog( "Error loading config from "+ configFile.getPath() );
}
finally {
try {if ( in != null ) in.close();}
catch ( IOException ignored) {}
}
public SlipstreamConfig( Properties config, File configFile ) {
this.config = config;
this.configFile = configFile;
}
@ -42,8 +68,23 @@ public class SlipstreamConfig {
this.configFile = srcConfig.getConfigFile();
this.config = new Properties();
this.config.putAll( srcConfig.getConfig() );
this.shutdownHookInitialized = srcConfig.shutdownHookInitialized;
}
private static Properties getProperties() {
Properties props = new Properties();
props.setProperty( SlipstreamConfig.ALLOW_ZIP, "false" );
props.setProperty( SlipstreamConfig.FTL_DATS_PATH, "" ); // Prompt.
props.setProperty( SlipstreamConfig.STEAM_DISTRO, "" ); // Prompt.
props.setProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ); // Prompt.
props.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "" ); // Prompt.
props.setProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" );
props.setProperty( SlipstreamConfig.UPDATE_CATALOG, "" ); // Prompt.
props.setProperty( SlipstreamConfig.UPDATE_APP, "" ); // Prompt.
props.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" );
props.setProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" );
return props;
}
public Properties getConfig() { return config; }
@ -51,9 +92,30 @@ public class SlipstreamConfig {
public Object setProperty( String key, String value ) {
scheduleShutdown();
return config.setProperty( key, value );
}
private void scheduleShutdown() {
if (!shutdownHookInitialized.compareAndExchange(false, true)) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
this.writeConfig();
} catch (IOException e) {
String errorMsg = String.format( "Error writing config to \"%s\"", configFile.getPath() );
log.error( errorMsg, e );
// TODO: only show this error when in gui mode
showErrorDialog( errorMsg );
}
}));
}
}
private static void showErrorDialog( String message ) {
JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE );
}
public int getPropertyAsInt( String key, int defaultValue ) {
String s = config.getProperty( key );
if ( s != null && s.matches("^\\d+$") )