mirror of https://github.com/FreeCol/freecol.git
1432 lines
53 KiB
Java
1432 lines
53 KiB
Java
/**
|
|
* Copyright (C) 2002-2015 The FreeCol Team
|
|
*
|
|
* This file is part of FreeCol.
|
|
*
|
|
* FreeCol is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* FreeCol is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package net.sf.freecol;
|
|
|
|
import java.awt.Dimension;
|
|
import java.io.File;
|
|
import java.io.FileFilter;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.InputStream;
|
|
import java.io.IOException;
|
|
import java.lang.Thread.UncaughtExceptionHandler;
|
|
import java.net.InetAddress;
|
|
import java.net.JarURLConnection;
|
|
import java.net.URL;
|
|
import java.net.JarURLConnection;
|
|
import java.util.Locale;
|
|
import java.util.jar.JarEntry;
|
|
import java.util.jar.JarFile;
|
|
import java.util.jar.Manifest;
|
|
import java.util.logging.Handler;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import java.util.zip.ZipEntry;
|
|
|
|
import net.sf.freecol.client.ClientOptions;
|
|
import net.sf.freecol.client.FreeColClient;
|
|
import net.sf.freecol.common.FreeColException;
|
|
import net.sf.freecol.common.FreeColSeed;
|
|
import net.sf.freecol.common.debug.FreeColDebugger;
|
|
import net.sf.freecol.common.i18n.Messages;
|
|
import net.sf.freecol.common.i18n.NameCache;
|
|
import net.sf.freecol.common.io.FreeColDirectories;
|
|
import net.sf.freecol.common.io.FreeColSavegameFile;
|
|
import net.sf.freecol.common.io.FreeColTcFile;
|
|
import net.sf.freecol.common.io.Mods;
|
|
import net.sf.freecol.common.logging.DefaultHandler;
|
|
import net.sf.freecol.common.model.NationOptions.Advantages;
|
|
import net.sf.freecol.common.model.Specification;
|
|
import net.sf.freecol.common.model.StringTemplate;
|
|
import net.sf.freecol.common.model.Turn;
|
|
import net.sf.freecol.common.networking.NoRouteToServerException;
|
|
import net.sf.freecol.common.option.OptionGroup;
|
|
import net.sf.freecol.server.FreeColServer;
|
|
|
|
import org.apache.commons.cli.CommandLine;
|
|
import org.apache.commons.cli.CommandLineParser;
|
|
import org.apache.commons.cli.HelpFormatter;
|
|
import org.apache.commons.cli.OptionBuilder;
|
|
import org.apache.commons.cli.Options;
|
|
import org.apache.commons.cli.ParseException;
|
|
import org.apache.commons.cli.PosixParser;
|
|
|
|
|
|
/**
|
|
* This class is responsible for handling the command-line arguments
|
|
* and starting either the stand-alone server or the client-GUI.
|
|
*
|
|
* @see net.sf.freecol.client.FreeColClient FreeColClient
|
|
* @see net.sf.freecol.server.FreeColServer FreeColServer
|
|
*/
|
|
public final class FreeCol {
|
|
|
|
private static final Logger logger = Logger.getLogger(FreeCol.class.getName());
|
|
|
|
/** The FreeCol release version number. */
|
|
private static final String FREECOL_VERSION = "0.11.4";
|
|
|
|
/** The difficulty levels. */
|
|
public static final String[] DIFFICULTIES = {
|
|
"veryEasy", "easy", "medium", "hard", "veryHard"
|
|
};
|
|
|
|
/** The extension for FreeCol saved games. */
|
|
public static final String FREECOL_SAVE_EXTENSION = ".fsg";
|
|
|
|
/** The Java version. */
|
|
private static final String JAVA_VERSION
|
|
= System.getProperty("java.version");
|
|
|
|
/** The maximum available memory. */
|
|
private static final long MEMORY_MAX = Runtime.getRuntime().maxMemory();
|
|
|
|
/** A file filter to select the saved game files. */
|
|
public static final FileFilter freeColSaveFileFilter
|
|
= new FileFilter() {
|
|
@Override
|
|
public boolean accept(File f) {
|
|
return f.isFile()
|
|
&& f.getName().endsWith(FREECOL_SAVE_EXTENSION)
|
|
&& f.getName().length() > FREECOL_SAVE_EXTENSION.length();
|
|
}
|
|
};
|
|
|
|
public static final String CLIENT_THREAD = "FreeColClient:";
|
|
public static final String SERVER_THREAD = "FreeColServer:";
|
|
public static final String METASERVER_THREAD = "FreeColMetaServer:";
|
|
|
|
public static final String META_SERVER_ADDRESS = "meta.freecol.org";
|
|
public static final int META_SERVER_PORT = 3540;
|
|
|
|
/** Specific revision number (currently the git tag of trunk at release) */
|
|
private static String freeColRevision = null;
|
|
|
|
/** The locale, either default or command-line specified. */
|
|
private static Locale locale = null;
|
|
|
|
|
|
// Cli defaults.
|
|
private static final Advantages ADVANTAGES_DEFAULT = Advantages.SELECTABLE;
|
|
private static final String DIFFICULTY_DEFAULT = "model.difficulty.medium";
|
|
private static final int EUROPEANS_DEFAULT = 4;
|
|
private static final int EUROPEANS_MIN = 1;
|
|
private static final Level LOGLEVEL_DEFAULT = Level.INFO;
|
|
private static final String JAVA_VERSION_MIN = "1.7";
|
|
private static final int MEMORY_MIN = 128; // Mbytes
|
|
private static final int PORT_DEFAULT = 3541;
|
|
private static final String SPLASH_DEFAULT = "splash.jpg";
|
|
private static final String TC_DEFAULT = "freecol";
|
|
public static final int TIMEOUT_DEFAULT = 60; // 1 minute
|
|
public static final int TIMEOUT_MIN = 10; // 10s
|
|
private static final int GUI_SCALE_MIN_PCT = 100;
|
|
private static final int GUI_SCALE_MAX_PCT = 200;
|
|
private static final int GUI_SCALE_STEP_PCT = 25;
|
|
public static final float GUI_SCALE_MIN = GUI_SCALE_MIN_PCT / 100.0f;
|
|
public static final float GUI_SCALE_MAX = GUI_SCALE_MAX_PCT / 100.0f;
|
|
public static final float GUI_SCALE_STEP = GUI_SCALE_STEP_PCT / 100.0f;
|
|
public static final float GUI_SCALE_DEFAULT = 1.0f;
|
|
|
|
|
|
// Cli values. Often set to null so the default can be applied in
|
|
// the accessor function.
|
|
private static boolean checkIntegrity = false,
|
|
consoleLogging = false,
|
|
debugStart = false,
|
|
fastStart = false,
|
|
headless = false,
|
|
introVideo = true,
|
|
javaCheck = true,
|
|
memoryCheck = true,
|
|
publicServer = true,
|
|
sound = true,
|
|
standAloneServer = false;
|
|
|
|
/** The type of advantages. */
|
|
private static Advantages advantages = null;
|
|
|
|
/** The difficulty level id. */
|
|
private static String difficulty = null;
|
|
|
|
/** The number of European nations to enable by default. */
|
|
private static int europeanCount = EUROPEANS_DEFAULT;
|
|
|
|
/** A font override. */
|
|
private static String fontName = null;
|
|
|
|
/** The level of logging in this game. */
|
|
private static Level logLevel = LOGLEVEL_DEFAULT;
|
|
|
|
/** The client player name. */
|
|
private static String name = null;
|
|
|
|
/** How to name and configure the server. */
|
|
private static int serverPort = -1;
|
|
private static String serverName = null;
|
|
|
|
/** A stream to get the splash image from. */
|
|
private static InputStream splashStream;
|
|
|
|
/** The TotalConversion / ruleset in play, defaults to "freecol". */
|
|
private static String tc = null;
|
|
|
|
/** The time out (seconds) for otherwise blocking commands. */
|
|
private static int timeout = -1;
|
|
|
|
/**
|
|
* The size of window to create, defaults to impossible dimensions
|
|
* to require windowed mode with best determined screen size.
|
|
*/
|
|
private static Dimension windowSize = new Dimension(-1, -1);
|
|
|
|
/** How much gui elements get scaled. */
|
|
private static float guiScale = GUI_SCALE_DEFAULT;
|
|
|
|
|
|
private FreeCol() {} // Hide constructor
|
|
|
|
/**
|
|
* The entrypoint.
|
|
*
|
|
* @param args The command-line arguments.
|
|
*/
|
|
public static void main(String[] args) {
|
|
freeColRevision = FREECOL_VERSION;
|
|
JarURLConnection juc;
|
|
try {
|
|
juc = getJarURLConnection(FreeCol.class);
|
|
} catch (IOException ioe) {
|
|
juc = null;
|
|
System.err.println("Unable to open class jar: "
|
|
+ ioe.getMessage());
|
|
}
|
|
if (juc != null) {
|
|
try {
|
|
String revision = readVersion(juc);
|
|
if (revision != null) {
|
|
freeColRevision += " (Revision: " + revision + ")";
|
|
}
|
|
} catch (Exception e) {
|
|
System.err.println("Unable to load Manifest: "
|
|
+ e.getMessage());
|
|
}
|
|
try {
|
|
splashStream = getDefaultSplashStream(juc);
|
|
} catch (Exception e) {
|
|
System.err.println("Unable to open default splash: "
|
|
+ e.getMessage());
|
|
}
|
|
}
|
|
|
|
// Java bug #7075600 causes BR#2554. The workaround is to set
|
|
// the following property. Remove if/when they fix Java.
|
|
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
|
|
|
|
// We can not even emit localized error messages until we find
|
|
// the data directory, which might have been specified on the
|
|
// command line.
|
|
String dataDirectoryArg = findArg("--freecol-data", args);
|
|
String err = FreeColDirectories.setDataDirectory(dataDirectoryArg);
|
|
if (err != null) fatal(err); // This must not fail.
|
|
|
|
// Now we have the data directory, establish the base locale.
|
|
// Beware, the locale may change!
|
|
String localeArg = findArg("--default-locale", args);
|
|
if (localeArg == null) {
|
|
locale = Locale.getDefault();
|
|
} else {
|
|
int index = localeArg.indexOf('.'); // Strip encoding if present
|
|
if (index > 0) localeArg = localeArg.substring(0, index);
|
|
locale = Messages.getLocale(localeArg);
|
|
}
|
|
Messages.loadMessageBundle(locale);
|
|
|
|
// Now that we can emit error messages, parse the other
|
|
// command line arguments.
|
|
handleArgs(args);
|
|
|
|
// Do the potentially fatal system checks as early as possible.
|
|
if (javaCheck && JAVA_VERSION_MIN.compareTo(JAVA_VERSION) > 0) {
|
|
fatal(StringTemplate.template("main.javaVersion")
|
|
.addName("%version%", JAVA_VERSION)
|
|
.addName("%minVersion%", JAVA_VERSION_MIN));
|
|
}
|
|
if (memoryCheck && MEMORY_MAX < MEMORY_MIN * 1000000) {
|
|
fatal(StringTemplate.template("main.memory")
|
|
.addAmount("%memory%", MEMORY_MAX)
|
|
.addAmount("%minMemory%", MEMORY_MIN));
|
|
}
|
|
|
|
// Having parsed the command line args, we know where the user
|
|
// directories should be, so we can set up the rest of the
|
|
// file/directory structure.
|
|
String userMsg = FreeColDirectories.setUserDirectories();
|
|
|
|
// Now we have the log file path, start logging.
|
|
final Logger baseLogger = Logger.getLogger("");
|
|
final Handler[] handlers = baseLogger.getHandlers();
|
|
for (Handler handler : handlers) {
|
|
baseLogger.removeHandler(handler);
|
|
}
|
|
String logFile = FreeColDirectories.getLogFilePath();
|
|
try {
|
|
baseLogger.addHandler(new DefaultHandler(consoleLogging, logFile));
|
|
Logger freecolLogger = Logger.getLogger("net.sf.freecol");
|
|
freecolLogger.setLevel(logLevel);
|
|
} catch (FreeColException e) {
|
|
System.err.println("Logging initialization failure: "
|
|
+ e.getMessage());
|
|
e.printStackTrace();
|
|
}
|
|
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
|
|
@Override
|
|
public void uncaughtException(Thread thread, Throwable e) {
|
|
baseLogger.log(Level.WARNING, "Uncaught exception from thread: " + thread, e);
|
|
}
|
|
});
|
|
|
|
// Now we can find the client options, allow the options
|
|
// setting to override the locale, if no command line option
|
|
// had been specified.
|
|
// We have users whose machines default to Finnish but play
|
|
// FreeCol in English.
|
|
// If the user has selected automatic language selection, do
|
|
// nothing, since we have already set up the default locale.
|
|
if (localeArg == null) {
|
|
String clientLanguage = ClientOptions.getLanguageOption();
|
|
Locale clientLocale;
|
|
if (clientLanguage != null
|
|
&& !Messages.AUTOMATIC.equalsIgnoreCase(clientLanguage)
|
|
&& (clientLocale = Messages.getLocale(clientLanguage)) != locale) {
|
|
locale = clientLocale;
|
|
Messages.loadMessageBundle(locale);
|
|
logger.info("Loaded messages for " + locale);
|
|
}
|
|
}
|
|
|
|
// Now we have the user mods directory and the locale is now
|
|
// stable, load the mods and their messages.
|
|
Mods.loadMods();
|
|
Messages.loadModMessageBundle(locale);
|
|
|
|
// Report on where we are.
|
|
if (userMsg != null) logger.info(Messages.message(userMsg));
|
|
logger.info(getConfiguration().toString());
|
|
|
|
// Ready to specialize into client or server.
|
|
if (standAloneServer) {
|
|
startServer();
|
|
} else {
|
|
startClient(userMsg);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the JarURLConnection from a class.
|
|
*
|
|
* @return The <code>JarURLConnection</code>.
|
|
*/
|
|
private static JarURLConnection getJarURLConnection(Class c) throws IOException {
|
|
String resourceName = "/" + c.getName().replace('.', '/') + ".class";
|
|
URL url = c.getResource(resourceName);
|
|
return (JarURLConnection)url.openConnection();
|
|
}
|
|
|
|
/**
|
|
* Extract the package version from the class.
|
|
*
|
|
* @param juc The <code>JarURLConnection</code> to extract from.
|
|
* @return A value of the package version attribute.
|
|
*/
|
|
private static String readVersion(JarURLConnection juc) throws IOException {
|
|
Manifest mf = juc.getManifest();
|
|
return (mf == null) ? null
|
|
: mf.getMainAttributes().getValue("Package-Version");
|
|
}
|
|
|
|
/**
|
|
* Get a stream for the default splash file.
|
|
*
|
|
* Note: Not bothering to check for nulls as this is called in try
|
|
* block that ignores all exceptions.
|
|
*
|
|
* @param juc The <code>JarURLConnection</code> to extract from.
|
|
* @return A suitable <code>InputStream</code>, or null on error.
|
|
*/
|
|
private static InputStream getDefaultSplashStream(JarURLConnection juc) throws IOException {
|
|
JarFile jf = juc.getJarFile();
|
|
ZipEntry ze = jf.getEntry(SPLASH_DEFAULT);
|
|
return jf.getInputStream(ze);
|
|
}
|
|
|
|
/**
|
|
* Exit printing fatal error message.
|
|
*
|
|
* @param template A <code>StringTemplate</code> to print.
|
|
*/
|
|
public static void fatal(StringTemplate template) {
|
|
fatal(Messages.message(template));
|
|
}
|
|
|
|
/**
|
|
* Exit printing fatal error message.
|
|
*
|
|
* @param err The error message to print.
|
|
*/
|
|
public static void fatal(String err) {
|
|
if (err == null || err.isEmpty()) {
|
|
err = "Bogus null fatal error message";
|
|
Thread.dumpStack();
|
|
}
|
|
System.err.println(err);
|
|
System.exit(1);
|
|
}
|
|
|
|
/**
|
|
* Just gripe to System.err.
|
|
*
|
|
* @param template A <code>StringTemplate</code> to print.
|
|
*/
|
|
public static void gripe(StringTemplate template) {
|
|
System.err.println(Messages.message(template));
|
|
}
|
|
|
|
/**
|
|
* Just gripe to System.err.
|
|
*
|
|
* @param key A message key.
|
|
*/
|
|
public static void gripe(String key) {
|
|
System.err.println(Messages.message(key));
|
|
}
|
|
|
|
/**
|
|
* Find an option before the real option handling can get started.
|
|
* Takes care to use the *last* instance.
|
|
*
|
|
* @param option The option to find.
|
|
* @param args The command-line arguments.
|
|
* @return The option's parameter.
|
|
*/
|
|
private static String findArg(String option, String[] args) {
|
|
for (int i = args.length - 2; i >= 0; i--) {
|
|
if (option.equals(args[i])) {
|
|
return args[i+1];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Processes the command-line arguments and takes appropriate
|
|
* actions for each of them.
|
|
*
|
|
* @param args The command-line arguments.
|
|
*/
|
|
private static void handleArgs(String[] args) {
|
|
Options options = new Options();
|
|
final String help = Messages.message("cli.help");
|
|
final File dummy = new File("dummy");
|
|
final String argDirectory = Messages.message("cli.arg.directory");
|
|
|
|
// Help options.
|
|
options.addOption(OptionBuilder.withLongOpt("usage")
|
|
.withDescription(help)
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("help")
|
|
.withDescription(help)
|
|
.create());
|
|
|
|
// Special options handled early.
|
|
options.addOption(OptionBuilder.withLongOpt("freecol-data")
|
|
.withDescription(Messages.message("cli.freecol-data"))
|
|
.withArgName(argDirectory)
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("default-locale")
|
|
.withDescription(Messages.message("cli.default-locale"))
|
|
.withArgName(Messages.message("cli.arg.locale"))
|
|
.hasArg()
|
|
.create());
|
|
|
|
// Ordinary options, handled here.
|
|
options.addOption(OptionBuilder.withLongOpt("advantages")
|
|
.withDescription(Messages.message(StringTemplate
|
|
.template("cli.advantages")
|
|
.addName("%advantages%", getValidAdvantages())))
|
|
.withArgName(Messages.message("cli.arg.advantages"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("check-savegame")
|
|
.withDescription(Messages.message("cli.check-savegame"))
|
|
.withArgName(Messages.message("cli.arg.file"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("clientOptions")
|
|
.withDescription(Messages.message("cli.clientOptions"))
|
|
.withArgName(Messages.message("cli.arg.clientOptions"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("debug")
|
|
.withDescription(Messages.message(StringTemplate
|
|
.template("cli.debug")
|
|
.addName("%modes%", FreeColDebugger.getDebugModes())))
|
|
.withArgName(Messages.message("cli.arg.debug"))
|
|
.hasOptionalArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("debug-run")
|
|
.withDescription(Messages.message("cli.debug-run"))
|
|
.withArgName(Messages.message("cli.arg.debugRun"))
|
|
.hasOptionalArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("debug-start")
|
|
.withDescription(Messages.message("cli.debug-start"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("difficulty")
|
|
.withDescription(Messages.message("cli.difficulty"))
|
|
.withArgName(Messages.message("cli.arg.difficulty"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("europeans")
|
|
.withDescription(Messages.message("cli.european-count"))
|
|
.withArgName(Messages.message("cli.arg.europeans"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("fast")
|
|
.withDescription(Messages.message("cli.fast"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("font")
|
|
.withDescription(Messages.message("cli.font"))
|
|
.withArgName(Messages.message("cli.arg.font"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("full-screen")
|
|
.withDescription(Messages.message("cli.full-screen"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("gui-scale")
|
|
.withDescription(Messages.message(StringTemplate
|
|
.template("cli.gui-scale")
|
|
.addName("%scales%", getValidGUIScales())))
|
|
.withArgName(Messages.message("cli.arg.gui-scale"))
|
|
.hasOptionalArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("headless")
|
|
.withDescription(Messages.message("cli.headless"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("load-savegame")
|
|
.withDescription(Messages.message("cli.load-savegame"))
|
|
.withArgName(Messages.message("cli.arg.file"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("log-console")
|
|
.withDescription(Messages.message("cli.log-console"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("log-file")
|
|
.withDescription(Messages.message("cli.log-file"))
|
|
.withArgName(Messages.message("cli.arg.name"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("log-level")
|
|
.withDescription(Messages.message("cli.log-level"))
|
|
.withArgName(Messages.message("cli.arg.loglevel"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("name")
|
|
.withDescription(Messages.message("cli.name"))
|
|
.withArgName(Messages.message("cli.arg.name"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("no-intro")
|
|
.withDescription(Messages.message("cli.no-intro"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("no-java-check")
|
|
.withDescription(Messages.message("cli.no-java-check"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("no-memory-check")
|
|
.withDescription(Messages.message("cli.no-memory-check"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("no-sound")
|
|
.withDescription(Messages.message("cli.no-sound"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("no-splash")
|
|
.withDescription(Messages.message("cli.no-splash"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("private")
|
|
.withDescription(Messages.message("cli.private"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("seed")
|
|
.withDescription(Messages.message("cli.seed"))
|
|
.withArgName(Messages.message("cli.arg.seed"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("server")
|
|
.withDescription(Messages.message("cli.server"))
|
|
.withArgName(Messages.message("cli.arg.port"))
|
|
.hasOptionalArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("server-name")
|
|
.withDescription(Messages.message("cli.server-name"))
|
|
.withArgName(Messages.message("cli.arg.name"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("splash")
|
|
.withDescription(Messages.message("cli.splash"))
|
|
.withArgName(Messages.message("cli.arg.file"))
|
|
.hasOptionalArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("tc")
|
|
.withDescription(Messages.message("cli.tc"))
|
|
.withArgName(Messages.message("cli.arg.name"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("timeout")
|
|
.withDescription(Messages.message("cli.timeout"))
|
|
.withArgName(Messages.message("cli.arg.timeout"))
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("user-cache-directory")
|
|
.withDescription(Messages.message("cli.user-cache-directory"))
|
|
.withArgName(argDirectory)
|
|
.withType(dummy)
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("user-config-directory")
|
|
.withDescription(Messages.message("cli.user-config-directory"))
|
|
.withArgName(argDirectory)
|
|
.withType(dummy)
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("user-data-directory")
|
|
.withDescription(Messages.message("cli.user-data-directory"))
|
|
.withArgName(argDirectory)
|
|
.withType(dummy)
|
|
.hasArg()
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("version")
|
|
.withDescription(Messages.message("cli.version"))
|
|
.create());
|
|
options.addOption(OptionBuilder.withLongOpt("windowed")
|
|
.withDescription(Messages.message("cli.windowed"))
|
|
.withArgName(Messages.message("cli.arg.dimensions"))
|
|
.hasOptionalArg()
|
|
.create());
|
|
|
|
CommandLineParser parser = new PosixParser();
|
|
boolean usageError = false;
|
|
try {
|
|
CommandLine line = parser.parse(options, args);
|
|
if (line.hasOption("help") || line.hasOption("usage")) {
|
|
printUsage(options, 0);
|
|
}
|
|
|
|
if (line.hasOption("default-locale")) {
|
|
; // Do nothing, already handled in main().
|
|
}
|
|
if (line.hasOption("freecol-data")) {
|
|
; // Do nothing, already handled in main().
|
|
}
|
|
|
|
if (line.hasOption("advantages")) {
|
|
String arg = line.getOptionValue("advantages");
|
|
Advantages a = selectAdvantages(arg);
|
|
if (a == null) {
|
|
fatal(StringTemplate.template("cli.error.advantages")
|
|
.addName("%advantages%", getValidAdvantages())
|
|
.addName("%arg%", arg));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("check-savegame")) {
|
|
String arg = line.getOptionValue("check-savegame");
|
|
if (!FreeColDirectories.setSavegameFile(arg)) {
|
|
fatal(StringTemplate.template("cli.err.save")
|
|
.addName("%string%", arg));
|
|
}
|
|
checkIntegrity = true;
|
|
standAloneServer = true;
|
|
}
|
|
|
|
if (line.hasOption("clientOptions")) {
|
|
String fileName = line.getOptionValue("clientOptions");
|
|
if (!FreeColDirectories.setClientOptionsFile(fileName)) {
|
|
// Not fatal.
|
|
gripe(StringTemplate.template("cli.error.clientOptions")
|
|
.addName("%string%", fileName));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("debug")) {
|
|
// If the optional argument is supplied use limited mode.
|
|
String arg = line.getOptionValue("debug");
|
|
if (arg == null || arg.isEmpty()) {
|
|
// Let empty argument default to menus functionality.
|
|
arg = FreeColDebugger.DebugMode.MENUS.toString();
|
|
}
|
|
if (!FreeColDebugger.setDebugModes(arg)) { // Not fatal.
|
|
gripe(StringTemplate.template("cli.error.debug")
|
|
.addName("%modes%", FreeColDebugger.getDebugModes()));
|
|
}
|
|
// user set log level has precedence
|
|
if (!line.hasOption("log-level")) logLevel = Level.FINEST;
|
|
}
|
|
if (line.hasOption("debug-run")) {
|
|
FreeColDebugger.enableDebugMode(FreeColDebugger.DebugMode.MENUS);
|
|
FreeColDebugger.configureDebugRun(line.getOptionValue("debug-run"));
|
|
}
|
|
if (line.hasOption("debug-start")) {
|
|
debugStart = true;
|
|
FreeColDebugger.enableDebugMode(FreeColDebugger.DebugMode.MENUS);
|
|
}
|
|
|
|
if (line.hasOption("difficulty")) {
|
|
String arg = line.getOptionValue("difficulty");
|
|
String difficulty = selectDifficulty(arg);
|
|
if (difficulty == null) {
|
|
fatal(StringTemplate.template("cli.error.difficulties")
|
|
.addName("%difficulties%", getValidDifficulties())
|
|
.addName("%arg%", arg));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("europeans")) {
|
|
int e = selectEuropeanCount(line.getOptionValue("europeans"));
|
|
if (e < 0) {
|
|
gripe(StringTemplate.template("cli.error.europeans")
|
|
.addAmount("%min%", EUROPEANS_MIN));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("fast")) {
|
|
fastStart = true;
|
|
introVideo = false;
|
|
}
|
|
|
|
if (line.hasOption("font")) {
|
|
fontName = line.getOptionValue("font");
|
|
}
|
|
|
|
if (line.hasOption("full-screen")) {
|
|
windowSize = null;
|
|
}
|
|
|
|
if (line.hasOption("gui-scale")) {
|
|
String arg = line.getOptionValue("gui-scale");
|
|
if(!setGUIScale(arg)) {
|
|
gripe(StringTemplate.template("cli.error.gui-scale")
|
|
.addName("%scales%", getValidGUIScales())
|
|
.addName("%arg%", arg));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("headless")) {
|
|
headless = true;
|
|
}
|
|
|
|
if (line.hasOption("load-savegame")) {
|
|
String arg = line.getOptionValue("load-savegame");
|
|
if (!FreeColDirectories.setSavegameFile(arg)) {
|
|
fatal(StringTemplate.template("cli.error.save")
|
|
.addName("%string%", arg));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("log-console")) {
|
|
consoleLogging = true;
|
|
}
|
|
if (line.hasOption("log-file")) {
|
|
FreeColDirectories.setLogFilePath(line.getOptionValue("log-file"));
|
|
}
|
|
if (line.hasOption("log-level")) {
|
|
setLogLevel(line.getOptionValue("log-level"));
|
|
}
|
|
|
|
if (line.hasOption("name")) {
|
|
setName(line.getOptionValue("name"));
|
|
}
|
|
|
|
if (line.hasOption("no-intro")) {
|
|
introVideo = false;
|
|
}
|
|
if (line.hasOption("no-java-check")) {
|
|
javaCheck = false;
|
|
}
|
|
if (line.hasOption("no-memory-check")) {
|
|
memoryCheck = false;
|
|
}
|
|
if (line.hasOption("no-sound")) {
|
|
sound = false;
|
|
}
|
|
if (line.hasOption("no-splash")) {
|
|
splashStream = null;
|
|
}
|
|
|
|
if (line.hasOption("private")) {
|
|
publicServer = false;
|
|
}
|
|
|
|
if (line.hasOption("server")) {
|
|
String arg = line.getOptionValue("server");
|
|
if (!setServerPort(arg)) {
|
|
fatal(StringTemplate.template("cli.error.serverPort")
|
|
.addName("%string%", arg));
|
|
}
|
|
standAloneServer = true;
|
|
}
|
|
if (line.hasOption("server-name")) {
|
|
serverName = line.getOptionValue("server-name");
|
|
}
|
|
|
|
if (line.hasOption("seed")) {
|
|
FreeColSeed.setFreeColSeed(line.getOptionValue("seed"));
|
|
}
|
|
|
|
if (line.hasOption("splash")) {
|
|
String splash = line.getOptionValue("splash");
|
|
try {
|
|
FileInputStream fis = new FileInputStream(splash);
|
|
splashStream = fis;
|
|
} catch (FileNotFoundException fnfe) {
|
|
gripe(StringTemplate.template("cli.error.splash")
|
|
.addName("%name%", splash));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("tc")) {
|
|
setTC(line.getOptionValue("tc")); // Failure is deferred.
|
|
}
|
|
|
|
if (line.hasOption("timeout")) {
|
|
String arg = line.getOptionValue("timeout");
|
|
if (!setTimeout(arg)) { // Not fatal
|
|
gripe(StringTemplate.template("cli.error.timeout")
|
|
.addName("%string%", arg)
|
|
.addName("%minimum%", Integer.toString(TIMEOUT_MIN)));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("user-cache-directory")) {
|
|
String arg = line.getOptionValue("user-cache-directory");
|
|
String errMsg = FreeColDirectories.setUserCacheDirectory(arg);
|
|
if (errMsg != null) { // Not fatal.
|
|
gripe(StringTemplate.template(errMsg)
|
|
.addName("%string%", arg));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("user-config-directory")) {
|
|
String arg = line.getOptionValue("user-config-directory");
|
|
String errMsg = FreeColDirectories.setUserConfigDirectory(arg);
|
|
if (errMsg != null) { // Not fatal.
|
|
gripe(StringTemplate.template(errMsg)
|
|
.addName("%string%", arg));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("user-data-directory")) {
|
|
String arg = line.getOptionValue("user-data-directory");
|
|
String errMsg = FreeColDirectories.setUserDataDirectory(arg);
|
|
if (errMsg != null) { // Fatal, unable to save.
|
|
fatal(StringTemplate.template(errMsg)
|
|
.addName("%string%", arg));
|
|
}
|
|
}
|
|
|
|
if (line.hasOption("version")) {
|
|
System.out.println("FreeCol " + getVersion());
|
|
System.exit(0);
|
|
}
|
|
|
|
if (line.hasOption("windowed")) {
|
|
String arg = line.getOptionValue("windowed");
|
|
setWindowSize(arg); // Does not fail
|
|
}
|
|
|
|
} catch (ParseException e) {
|
|
System.err.println("\n" + e.getMessage() + "\n");
|
|
usageError = true;
|
|
}
|
|
if (usageError) printUsage(options, 1);
|
|
}
|
|
|
|
/**
|
|
* Prints the usage message and exits.
|
|
*
|
|
* @param options The command line <code>Options</code>.
|
|
* @param status The status to exit with.
|
|
*/
|
|
private static void printUsage(Options options, int status) {
|
|
HelpFormatter formatter = new HelpFormatter();
|
|
formatter.printHelp("java -Xmx 256M -jar freecol.jar [OPTIONS]",
|
|
options);
|
|
System.exit(status);
|
|
}
|
|
|
|
/**
|
|
* Get the specification from a given TC file.
|
|
*
|
|
* @param tcf The <code>FreeColTcFile</code> to load.
|
|
* @param advantages An optional <code>Advantages</code> setting.
|
|
* @param difficulty An optional difficulty level.
|
|
* @return A <code>Specification</code>.
|
|
*/
|
|
public static Specification loadSpecification(FreeColTcFile tcf,
|
|
Advantages advantages,
|
|
String difficulty) {
|
|
Specification spec = null;
|
|
try {
|
|
if (tcf != null) spec = tcf.getSpecification();
|
|
} catch (IOException ioe) {
|
|
System.err.println("Spec read failed in " + tcf.getId()
|
|
+ ": " + ioe.getMessage() + "\n");
|
|
}
|
|
if (spec != null) spec.prepare(advantages, difficulty);
|
|
return spec;
|
|
}
|
|
|
|
/**
|
|
* Get the specification from the specified TC.
|
|
*
|
|
* @return A <code>Specification</code>, quits on error.
|
|
*/
|
|
private static Specification getTCSpecification() {
|
|
Specification spec = loadSpecification(getTCFile(), getAdvantages(),
|
|
getDifficulty());
|
|
if (spec == null) {
|
|
fatal(StringTemplate.template("cli.error.badTC")
|
|
.addName("%tc%", getTC()));
|
|
}
|
|
return spec;
|
|
}
|
|
|
|
// Accessors, mutators and support for the cli variables.
|
|
|
|
/**
|
|
* Gets the default advantages type.
|
|
*
|
|
* @return Usually Advantages.SELECTABLE, but can be overridden at the
|
|
* command line.
|
|
*/
|
|
public static Advantages getAdvantages() {
|
|
return (advantages == null) ? ADVANTAGES_DEFAULT
|
|
: advantages;
|
|
}
|
|
|
|
/**
|
|
* Sets the advantages type.
|
|
*
|
|
* Called from NewPanel when a selection is made.
|
|
*
|
|
* @param advantages The name of the new advantages type.
|
|
* @return The type of advantages set, or null if none.
|
|
*/
|
|
private static Advantages selectAdvantages(String advantages) {
|
|
for (Advantages a : Advantages.values()) {
|
|
String msg = Messages.getName(a);
|
|
if (msg.equals(advantages)) {
|
|
setAdvantages(a);
|
|
return a;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Sets the advantages type.
|
|
*
|
|
* @param advantages The new <code>Advantages</code> type.
|
|
*/
|
|
public static void setAdvantages(Advantages advantages) {
|
|
FreeCol.advantages = advantages;
|
|
}
|
|
|
|
/**
|
|
* Gets a comma separated list of localized advantage type names.
|
|
*
|
|
* @return A list of advantage types.
|
|
*/
|
|
private static String getValidAdvantages() {
|
|
String ret = "";
|
|
for (Advantages a : Advantages.values()) {
|
|
ret += "," + Messages.getName(a);
|
|
}
|
|
return ret.substring(1);
|
|
}
|
|
|
|
/**
|
|
* Gets the difficulty level.
|
|
*
|
|
* @return The name of a difficulty level.
|
|
*/
|
|
public static String getDifficulty() {
|
|
return (difficulty == null) ? DIFFICULTY_DEFAULT : difficulty;
|
|
}
|
|
|
|
/**
|
|
* Selects a difficulty level.
|
|
*
|
|
* @param arg The supplied difficulty argument.
|
|
* @return The name of the selected difficulty, or null if none.
|
|
*/
|
|
public static String selectDifficulty(String arg) {
|
|
for (String d : DIFFICULTIES) {
|
|
String key = "model.difficulty." + d;
|
|
String value = Messages.getName(key);
|
|
if (value.equals(arg)) {
|
|
setDifficulty(key);
|
|
return key;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Sets the difficulty level.
|
|
*
|
|
* @param difficulty The actual <code>OptionGroup</code>
|
|
* containing the difficulty level.
|
|
*/
|
|
public static void setDifficulty(OptionGroup difficulty) {
|
|
setDifficulty(difficulty.getId());
|
|
}
|
|
|
|
/**
|
|
* Sets the difficulty level.
|
|
*
|
|
* @param difficulty The new difficulty.
|
|
*/
|
|
public static void setDifficulty(String difficulty) {
|
|
FreeCol.difficulty = difficulty;
|
|
}
|
|
|
|
/**
|
|
* Gets the names of the valid difficulty levels.
|
|
*
|
|
* @return The valid difficulty levels, comma separated.
|
|
*/
|
|
public static String getValidDifficulties() {
|
|
String s = "";
|
|
for (String d : DIFFICULTIES) {
|
|
String key = "model.difficulty." + d;
|
|
String value = Messages.getName(key);
|
|
s += "," + value;
|
|
}
|
|
return s.substring(1);
|
|
}
|
|
|
|
/**
|
|
* Get the number of European nations to enable by default.
|
|
*/
|
|
public static int getEuropeanCount() {
|
|
return europeanCount;
|
|
}
|
|
|
|
/**
|
|
* Sets the number of enabled European nations.
|
|
*
|
|
* @param n The number of nations to enable.
|
|
*/
|
|
public static void setEuropeanCount(int n) {
|
|
europeanCount = n;
|
|
}
|
|
|
|
/**
|
|
* Sets the scale for GUI elements.
|
|
*
|
|
* @param arg The optional command line argument to be parsed.
|
|
* @return If the argument was correctly formatted.
|
|
*/
|
|
public static boolean setGUIScale(String arg) {
|
|
boolean valid = true;
|
|
if(arg == null) {
|
|
guiScale = GUI_SCALE_MAX;
|
|
} else {
|
|
try {
|
|
int n = Integer.parseInt(arg);
|
|
if (n < GUI_SCALE_MIN_PCT) {
|
|
valid = false;
|
|
n = GUI_SCALE_MIN_PCT;
|
|
} else if(n > GUI_SCALE_MAX_PCT) {
|
|
valid = false;
|
|
n = GUI_SCALE_MAX_PCT;
|
|
} else if(n % GUI_SCALE_STEP_PCT != 0) {
|
|
valid = false;
|
|
}
|
|
guiScale = ((float)(n / GUI_SCALE_STEP_PCT)) * GUI_SCALE_STEP;
|
|
} catch (NumberFormatException nfe) {
|
|
valid = false;
|
|
guiScale = GUI_SCALE_MAX;
|
|
}
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* Gets the valid scale factors for the GUI.
|
|
*
|
|
* @return A string containing these.
|
|
*/
|
|
public static String getValidGUIScales() {
|
|
String result = "";
|
|
for(int i=GUI_SCALE_MIN_PCT; i<GUI_SCALE_MAX_PCT; i+=GUI_SCALE_STEP_PCT)
|
|
result += i + ",";
|
|
result += GUI_SCALE_MAX_PCT;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Selects a European nation count.
|
|
*
|
|
* @param arg The supplied count argument.
|
|
* @return A valid nation number, or negative on error.
|
|
*/
|
|
public static int selectEuropeanCount(String arg) {
|
|
try {
|
|
int n = Integer.parseInt(arg);
|
|
if (n >= EUROPEANS_MIN) {
|
|
setEuropeanCount(n);
|
|
return n;
|
|
}
|
|
} catch (NumberFormatException nfe) {}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Sets the log level.
|
|
*
|
|
* @param arg The log level to set.
|
|
*/
|
|
private static void setLogLevel(String arg) {
|
|
logLevel = Level.parse(arg.toUpperCase());
|
|
}
|
|
|
|
/**
|
|
* Gets the user name.
|
|
*
|
|
* @return The user name, defaults to the user.name property, then to
|
|
* the "main.defaultPlayerName" message value.
|
|
*/
|
|
public static String getName() {
|
|
return (name != null) ? name
|
|
: System.getProperty("user.name",
|
|
Messages.message("main.defaultPlayerName"));
|
|
}
|
|
|
|
/**
|
|
* Sets the user name.
|
|
*
|
|
* @param name The new user name.
|
|
*/
|
|
public static void setName(String name) {
|
|
FreeCol.name = name;
|
|
logger.info("Set FreeCol.name = " + name);
|
|
}
|
|
|
|
/**
|
|
* Get the selected locale.
|
|
*
|
|
* @return The <code>Locale</code> currently in use.
|
|
*/
|
|
public static Locale getLocale() {
|
|
return FreeCol.locale;
|
|
}
|
|
|
|
/**
|
|
* Gets the current revision of game.
|
|
*
|
|
* @return The current version and SVN Revision of the game.
|
|
*/
|
|
public static String getRevision() {
|
|
return freeColRevision;
|
|
}
|
|
|
|
/**
|
|
* Get the default server host name.
|
|
*
|
|
* @return The host name.
|
|
*/
|
|
public static String getServerHost() {
|
|
return InetAddress.getLoopbackAddress().getHostAddress();
|
|
}
|
|
|
|
/**
|
|
* Gets the server network port.
|
|
*
|
|
* @return The port number.
|
|
*/
|
|
public static int getServerPort() {
|
|
return (serverPort < 0) ? PORT_DEFAULT : serverPort;
|
|
}
|
|
|
|
/**
|
|
* Sets the server port.
|
|
*
|
|
* @param arg The server port number.
|
|
* @return True if the port was set.
|
|
*/
|
|
public static boolean setServerPort(String arg) {
|
|
if (arg == null) return false;
|
|
try {
|
|
serverPort = Integer.parseInt(arg);
|
|
} catch (NumberFormatException nfe) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Gets the current Total-Conversion.
|
|
*
|
|
* @return Usually TC_DEFAULT, but can be overridden at the command line.
|
|
*/
|
|
public static String getTC() {
|
|
return (tc == null) ? TC_DEFAULT : tc;
|
|
}
|
|
|
|
/**
|
|
* Sets the Total-Conversion.
|
|
*
|
|
* Called from NewPanel when a selection is made.
|
|
*
|
|
* @param tc The name of the new total conversion.
|
|
*/
|
|
public static void setTC(String tc) {
|
|
FreeCol.tc = tc;
|
|
}
|
|
|
|
/**
|
|
* Gets the FreeColTcFile for the current TC.
|
|
*
|
|
* @return The <code>FreeColTcFile</code>.
|
|
*/
|
|
public static FreeColTcFile getTCFile() {
|
|
try {
|
|
return new FreeColTcFile(getTC());
|
|
} catch (IOException ioe) {}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets the timeout.
|
|
* Use the command line specified one if any, otherwise default
|
|
* to `infinite' in single player and the TIMEOUT_DEFAULT for
|
|
* multiplayer.
|
|
*
|
|
* @param singlePlayer True if this is a single player game.
|
|
* @return A suitable timeout value.
|
|
*/
|
|
public static int getTimeout(boolean singlePlayer) {
|
|
return (timeout >= TIMEOUT_MIN) ? timeout
|
|
: (singlePlayer) ? Integer.MAX_VALUE
|
|
: TIMEOUT_DEFAULT;
|
|
}
|
|
|
|
/**
|
|
* Sets the timeout.
|
|
*
|
|
* @param timeout A string containing the new timeout.
|
|
* @return True if the timeout was set.
|
|
*/
|
|
public static boolean setTimeout(String timeout) {
|
|
try {
|
|
int result = Integer.parseInt(timeout);
|
|
if (result >= TIMEOUT_MIN) {
|
|
FreeCol.timeout = result;
|
|
return true;
|
|
}
|
|
} catch (NumberFormatException nfe) {}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets the current version of game.
|
|
*
|
|
* @return The current version of the game using the format "x.y.z",
|
|
* where "x" is major, "y" is minor and "z" is revision.
|
|
*/
|
|
public static String getVersion() {
|
|
return FREECOL_VERSION;
|
|
}
|
|
|
|
/**
|
|
* Sets the window size.
|
|
*
|
|
* Does not fail because any empty or invalid value is interpreted as
|
|
* `windowed but use as much screen as possible'.
|
|
*
|
|
* @param arg The window size specification.
|
|
*/
|
|
private static void setWindowSize(String arg) {
|
|
String[] xy;
|
|
if (arg != null
|
|
&& (xy = arg.split("[^0-9]")) != null
|
|
&& xy.length == 2) {
|
|
try {
|
|
windowSize = new Dimension(Integer.parseInt(xy[0]),
|
|
Integer.parseInt(xy[1]));
|
|
} catch (NumberFormatException nfe) {}
|
|
}
|
|
if (windowSize == null) windowSize = new Dimension(-1, -1);
|
|
}
|
|
|
|
|
|
/**
|
|
* Utility to make a load failure message.
|
|
*
|
|
* @param file The <code>File</code> that failed to load.
|
|
* @return A <code>StringTemplate</code> with the error message.
|
|
*/
|
|
public static StringTemplate badLoad(File file) {
|
|
return StringTemplate.template("error.couldNotLoad")
|
|
.addName("%name%", file.getPath());
|
|
}
|
|
|
|
/**
|
|
* Utility to make a save failure message.
|
|
*
|
|
* @param file The <code>File</code> that failed to save.
|
|
* @return A <code>StringTemplate</code> with the error message.
|
|
*/
|
|
public static StringTemplate badSave(File file) {
|
|
return StringTemplate.template("error.couldNotSave")
|
|
.addName("%name%", file.getPath());
|
|
}
|
|
|
|
/**
|
|
* We get a lot of lame bug reports with insufficient configuration
|
|
* information. Get a buffer containing as much information as we can
|
|
* to embed in the log file and saved games.
|
|
*
|
|
* @return A <code>StringBuilder</code> full of configuration information.
|
|
*/
|
|
public static StringBuilder getConfiguration() {
|
|
File autosave = FreeColDirectories.getAutosaveDirectory();
|
|
File clientOptionsFile = FreeColDirectories.getClientOptionsFile();
|
|
File save = FreeColDirectories.getSaveDirectory();
|
|
File userConfig = FreeColDirectories.getUserConfigDirectory();
|
|
File userData = FreeColDirectories.getUserDataDirectory();
|
|
File userMods = FreeColDirectories.getUserModsDirectory();
|
|
StringBuilder sb = new StringBuilder(256);
|
|
sb.append("Configuration:")
|
|
.append("\n version ").append(getRevision())
|
|
.append("\n java: ").append(JAVA_VERSION)
|
|
.append("\n memory: ").append(MEMORY_MAX)
|
|
.append("\n locale: ").append(locale)
|
|
.append("\n data: ")
|
|
.append(FreeColDirectories.getDataDirectory().getPath())
|
|
.append("\n userConfig: ")
|
|
.append((userConfig == null) ? "NONE" : userConfig.getPath())
|
|
.append("\n userData: ")
|
|
.append((userData == null) ? "NONE" : userData.getPath())
|
|
.append("\n autosave: ")
|
|
.append((autosave == null) ? "NONE" : autosave.getPath())
|
|
.append("\n logFile: ")
|
|
.append(FreeColDirectories.getLogFilePath())
|
|
.append("\n options: ")
|
|
.append((clientOptionsFile == null) ? "NONE"
|
|
: clientOptionsFile.getPath())
|
|
.append("\n save: ")
|
|
.append((save == null) ? "NONE" : save.getPath())
|
|
.append("\n userMods: ")
|
|
.append((userMods == null) ? "NONE" : userMods.getPath());
|
|
return sb;
|
|
}
|
|
|
|
|
|
// The major final actions.
|
|
|
|
/**
|
|
* Start a client.
|
|
*
|
|
* @param userMsg An optional user message key.
|
|
*/
|
|
private static void startClient(String userMsg) {
|
|
Specification spec = null;
|
|
File savegame = FreeColDirectories.getSavegameFile();
|
|
if (debugStart) {
|
|
spec = FreeCol.getTCSpecification();
|
|
} else if (fastStart) {
|
|
if (savegame == null) {
|
|
// continue last saved game if possible,
|
|
// otherwise start a new one
|
|
savegame = FreeColDirectories.getLastSaveGameFile();
|
|
if (savegame == null) {
|
|
spec = FreeCol.getTCSpecification();
|
|
}
|
|
}
|
|
// savegame was specified on command line
|
|
}
|
|
final FreeColClient freeColClient
|
|
= new FreeColClient(splashStream, fontName, guiScale, headless);
|
|
freeColClient.startClient(windowSize, userMsg, sound, introVideo,
|
|
savegame, spec);
|
|
}
|
|
|
|
/**
|
|
* Start the server.
|
|
*/
|
|
private static void startServer() {
|
|
logger.info("Starting stand-alone server.");
|
|
final FreeColServer freeColServer;
|
|
File saveGame = FreeColDirectories.getSavegameFile();
|
|
if (saveGame != null) {
|
|
try {
|
|
final FreeColSavegameFile fis
|
|
= new FreeColSavegameFile(saveGame);
|
|
freeColServer = new FreeColServer(fis, (Specification)null,
|
|
serverPort, serverName);
|
|
if (checkIntegrity) {
|
|
boolean integrityOK = freeColServer.getIntegrity() > 0;
|
|
gripe((integrityOK)
|
|
? "cli.check-savegame.success"
|
|
: "cli.check-savegame.failure");
|
|
System.exit((integrityOK) ? 0 : 2);
|
|
}
|
|
} catch (Exception e) {
|
|
if (checkIntegrity) gripe("cli.check-savegame.failure");
|
|
fatal(Messages.message(badLoad(saveGame))
|
|
+ ": " + e.getMessage());
|
|
return;
|
|
}
|
|
} else {
|
|
Specification spec = FreeCol.getTCSpecification();
|
|
try {
|
|
freeColServer = new FreeColServer(publicServer, false, spec,
|
|
serverPort, serverName);
|
|
} catch (NoRouteToServerException nrtse) {
|
|
fatal(Messages.message("server.noRouteToServer"));
|
|
return;
|
|
} catch (Exception e) {
|
|
fatal(Messages.message("server.initialize")
|
|
+ ": " + e.getMessage());
|
|
return;
|
|
}
|
|
}
|
|
|
|
String quit = FreeCol.SERVER_THREAD + "Quit Game";
|
|
Runtime.getRuntime().addShutdownHook(new Thread(quit) {
|
|
@Override
|
|
public void run() {
|
|
freeColServer.getController().shutdown();
|
|
}
|
|
});
|
|
}
|
|
}
|