/*
* This file is part of FTB Launcher.
*
* Copyright © 2012-2016, FTB Launcher Contributors <https://github.com/Slowpoke101/FTBLaunch/>
* FTB Launcher is licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.ftb.main;
import java.awt.EventQueue;
import java.awt.SystemTray;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import javax.swing.InputMap;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.DefaultEditorKit;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
import com.google.common.eventbus.EventBus;
import lombok.Getter;
import lombok.Setter;
import net.ftb.data.CommandLineSettings;
import net.ftb.data.Constants;
import net.ftb.data.Map;
import net.ftb.data.ModPack;
import net.ftb.data.Settings;
import net.ftb.data.TexturePack;
import net.ftb.data.UserManager;
import net.ftb.download.Locations;
import net.ftb.gui.LaunchFrame;
import net.ftb.gui.LauncherConsole;
import net.ftb.gui.dialogs.FirstRunDialog;
import net.ftb.gui.dialogs.LauncherUpdateDialog;
import net.ftb.gui.dialogs.LoadingDialog;
import net.ftb.locale.I18N;
import net.ftb.log.LogLevel;
import net.ftb.log.LogSource;
import net.ftb.log.LogWriter;
import net.ftb.log.Logger;
import net.ftb.log.OutputOverride;
import net.ftb.log.StdOutLogger;
import net.ftb.tracking.google.AnalyticsConfigData;
import net.ftb.tracking.google.JGoogleAnalyticsTracker;
import net.ftb.updater.UpdateChecker;
import net.ftb.util.Benchmark;
import net.ftb.util.CheckInstallPath;
import net.ftb.util.DownloadUtils;
import net.ftb.util.ErrorUtils;
import net.ftb.util.OSUtils;
import net.ftb.util.StyleUtil;
import net.ftb.util.TrackerUtils;
import net.ftb.util.winreg.JavaInfo;
import net.ftb.util.winreg.JavaVersion;
import net.ftb.workers.AuthlibDLWorker;
import net.ftb.workers.RetiredPacksLoader;
public class Main
{
public static JGoogleAnalyticsTracker tracker;
public static AnalyticsConfigData AnalyticsConfigData = new AnalyticsConfigData("UA-37330489-2");
@Getter
private static UserManager userManager;
@Getter
private static int beta;
@Setter
@Getter
private static boolean authlibReadyToUse = false;
private static JCommander jc;
/**
* @return FTB Launcher event bus
*/
@Getter
private static EventBus eventBus = new EventBus();
@Getter
private static boolean disableLaunchButton = false;
/**
* Launch the application.
* @param args - CLI arguments
*/
public static void main (String[] args)
{
Benchmark.start("main");
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException (Thread t, Throwable e)
{
Logger.logError("Unhandled exception in " + t.toString(), e);
}
});
try
{
jc = new JCommander(CommandLineSettings.getSettings(), args);
}
catch (ParameterException e)
{
System.out.println(e.getMessage());
System.exit(0);
}
if (CommandLineSettings.getSettings().isHelp())
{
jc.setProgramName("FTNT_Launcher.jar");
jc.usage();
System.exit(0);
}
/*
* Create dynamic storage location as soon as possible
*/
OSUtils.createStorageLocations();
// Use IPv4 when possible, only use IPv6 when connecting to IPv6 only addresses
System.setProperty("java.net.preferIPv4Stack", "true");
if (Settings.getSettings().getUseSystemProxy())
{
// Use system default proxy settings
System.setProperty("java.net.useSystemProxies", "true");
}
if (new File(Settings.getSettings().getInstallPath(), Locations.launcherLogFile).exists())
{
new File(Settings.getSettings().getInstallPath(), Locations.launcherLogFile).delete();
}
if (new File(Settings.getSettings().getInstallPath(), Locations.minecraftLogFile).exists())
{
new File(Settings.getSettings().getInstallPath(), Locations.minecraftLogFile).delete();
}
/*
* Create new StdoutLogger as soon as possible
*/
int logLevel = CommandLineSettings.getSettings().getVerbosity();
LogLevel stdoutLogLevel = LogLevel.values()[logLevel];
LogSource stdoutLogSource = CommandLineSettings.getSettings().isMcLogs() ? LogSource.ALL : LogSource.LAUNCHER;
Logger.addListener(new StdOutLogger(stdoutLogLevel, stdoutLogSource));
/*
* Setup System.out and System.err redirection as soon as possible
*/
System.setOut(new OutputOverride(System.out, LogLevel.INFO));
System.setErr(new OutputOverride(System.err, LogLevel.ERROR));
/*
* Setup LogWriters as soon as possible
* At first run log will be created same directory with launcher
*/
try
{
Logger.addListener(new LogWriter(new File(Settings.getSettings().getInstallPath(), Locations.launcherLogFile), LogSource.LAUNCHER));
Logger.addListener(new LogWriter(new File(Settings.getSettings().getInstallPath(), Locations.minecraftLogFile), LogSource.EXTERNAL));
}
catch (IOException e1)
{
if (!Settings.getSettings().isNoConfig())
{
Logger.logDebug("Could not create LogWriters.", e1);
Logger.logError("Check your FTB installation location's write access. Launch button is disabled until installation location is fixed.");
Main.disableLaunchButton = true;
}
}
Logger.logDebug("Launcher arguments: " + Arrays.toString(args));
Logger.logDebug("Launcher PID: " + OSUtils.getPID());
URL mf = LaunchFrame.class.getResource("/buildproperties.properties");
beta = 9999999;
String mfStr = "";
try
{
Properties props = new Properties();
props.load(mf.openStream());
mfStr = props.getProperty("LauncherJenkins");
if (!mfStr.equals("${LauncherJenkins}"))
{
beta = Integer.parseInt(mfStr);
}
Logger.logDebug("FTB Launcher CI Build #: " + beta + ", Git SHA: " + props.getProperty("Git-SHA"));
}
catch (Exception e)
{
Logger.logError("Check your launcher binary's path. It might contain unsupported characters");
Logger.logError("Error getting beta information, assuming beta channel not usable!", e);
beta = 9999999;
}
System.setProperty("http.agent", "FTB Launcher/" + Constants.version);
/*
* Posts information about OS, JVM and launcher version into Google Analytics
*/
AnalyticsConfigData.setUserAgent("Java/" + System.getProperty("java.version") + " (" + System.getProperty("os.name") + "; " + System.getProperty("os.arch") + ")");
tracker = new JGoogleAnalyticsTracker(AnalyticsConfigData, JGoogleAnalyticsTracker.GoogleAnalyticsVersion.V_4_7_2);
tracker.setEnabled(true);
TrackerUtils.sendPageView("net/ftb/gui/LaunchFrame.java", "Launcher Start / " + Constants.version + "." + beta);
if (!new File(OSUtils.getDynamicStorageLocation(), "FTBOSSent" + Constants.version + "." + beta + ".txt").exists())
{
TrackerUtils.sendPageView("net/ftb/gui/LaunchFrame.java", "Launcher " + Constants.version + "." + beta + " OS " + OSUtils.getOSString());
try
{
new File(OSUtils.getDynamicStorageLocation(), "FTBOSSent" + Constants.version + ".txt").createNewFile();
}
catch (IOException e)
{
Logger.logError("Error creating os cache text file");
}
}
MainHelpers.printInfo();
/*
* Resolves servers in background thread
*/
DownloadUtils thread = new DownloadUtils();
thread.start();
// later add other main()s for 100% headless and CLI clients
mainGUI(args);
}
private static void mainGUI (String[] args)
{
/*
* Setup GUI style & create and show Splash screen in EDT
* NEVER add code with Thread.sleep() or I/O blocking, including network usage in EDT
* => If this guideline is followed then GUI should work smoothly
*/
EventQueue.invokeLater(new Runnable()
{
@Override
public void run ()
{
try
{
String path = new File(LaunchFrame.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();
path = URLDecoder.decode(path, "UTF-8");
if (path.contains("!")) ErrorUtils.tossError("Warning current location of the launcher binary contains character: \"!\", \n" + "which is not supported. Please move launcher binary and try again");
}
catch (Exception e)
{
Logger.logError("Couldn't get path to current launcher jar/exe", e);
}
StyleUtil.loadUiStyles();
try
{
for(UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
{
if ("Nimbus".equals(info.getName()))
{
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
if (OSUtils.getCurrentOS() == OSUtils.OS.MACOSX)
{
InputMap im = (InputMap)UIManager.get("TextField.focusInputMap");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.META_DOWN_MASK), DefaultEditorKit.copyAction);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), DefaultEditorKit.pasteAction);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.META_DOWN_MASK), DefaultEditorKit.cutAction);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK), DefaultEditorKit.selectAllAction);
}
}
catch (Exception e)
{
try
{
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
}
catch (Exception e1)
{}
}
LaunchFrame.loader = new LoadingDialog();
LaunchFrame.loader.setVisible(true);
LaunchFrame.loader.toFront();
}
});
I18N.setupLocale();
I18N.setLocale(Settings.getSettings().getLocale());
if (Settings.getSettings().isNoConfig() && !CommandLineSettings.getSettings().isSkipFirst())
{
Logger.logDebug("FirstRunDialog");
try
{
EventQueue.invokeAndWait(new Runnable()
{
@Override
public void run ()
{
FirstRunDialog firstRunDialog = new FirstRunDialog();
firstRunDialog.setVisible(true);
}
});
}
catch (Exception e)
{
Logger.logDebug("failed", e.getCause());
}
}
else if (CommandLineSettings.getSettings().isSkipFirst())
{
String installDir = CommandLineSettings.getSettings().getInstallDir();
if (installDir == null)
{
Logger.logWarn("Bad command line argument combination. Please, use both --pack-dir and --skip-first");
}
else
{
Settings.getSettings().setInstallPath(installDir);
Settings.getSettings().save();
}
}
// NOTE: this messagage will be missed because laoder is not created when this is executed
// should we invokeAndWait when creating LoadingDialog?
// if we wait other things in main thread will be executed later
LoadingDialog.advance("Checking installation location");
File installDir = new File(Settings.getSettings().getInstallPath());
if (!installDir.exists())
{
installDir.mkdirs();
}
// CheckInstallPath() does Error/Warning logging in english
final CheckInstallPath checkResult = new CheckInstallPath(Settings.getSettings().getInstallPath(), true);
if (!CommandLineSettings.getSettings().isDisableInstallLocChecks() && (checkResult.action == CheckInstallPath.Action.BLOCK || checkResult.action == CheckInstallPath.Action.WARN))
{
try
{
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run ()
{
ErrorUtils.showClickableMessage(checkResult.localizedMessage, JOptionPane.ERROR_MESSAGE);
}
});
}
catch (Exception e)
{
Logger.logDebug("failed", e.getCause());
}
}
try
{
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run ()
{
// Same warnings are logged as errors in MainHelpers.printInfo()
if (!OSUtils.is64BitOS() && !CommandLineSettings.getSettings().isDisableJVMBitnessCheck())
{
ErrorUtils.showClickableMessage(I18N.getLocaleString("WARN_32BIT_OS"), JOptionPane.WARNING_MESSAGE);
}
if (OSUtils.is64BitOS() && !Settings.getSettings().getCurrentJava().is64bits && !CommandLineSettings.getSettings().isDisableJVMBitnessCheck())
{
ErrorUtils.showClickableMessage(I18N.getLocaleString("WARN_32BIT_JAVA"), JOptionPane.WARNING_MESSAGE);
}
JavaInfo java = Settings.getSettings().getCurrentJava();
JavaVersion java7 = JavaVersion.createJavaVersion("1.7.0");
if (java.isOlder(java7) && !CommandLineSettings.getSettings().isDisableJVMVersionCheck())
{
ErrorUtils.showClickableMessage(I18N.getLocaleString("WARN_JAVA6"), JOptionPane.WARNING_MESSAGE);
}
}
});
}
catch (Exception e)
{
Logger.logDebug("failed", e.getCause());
}
// NOTE: this is also missed
LoadingDialog.advance("Loading user data");
ModPack.loadXml(getXmls());
// not good location for this. Loader must wait until other packs are loaded....
try
{
RetiredPacksLoader retiredPacksLoader = new RetiredPacksLoader(new URL(Locations.masterRepo + "/FTB2/static/hiddenpacks.json"), OSUtils.getCacheStorageLocation(), Settings.getSettings().getInstallPath());
retiredPacksLoader.start();
}
catch (Exception e)
{
Logger.logDebug("RetiredPacksLoader failed", e);
}
// Store this in the cache (local) storage, since it's machine specific.
userManager = new UserManager(new File(OSUtils.getCacheStorageLocation(), "logindata"), new File(OSUtils.getDynamicStorageLocation(), "logindata"));
/*
* Execute AuthlibDLWorker swingworker. done() will enable launch button as soon as possible
*/
AuthlibDLWorker authworker = new AuthlibDLWorker(OSUtils.getDynamicStorageLocation() + File.separator + "authlib" + File.separator, "1.5.22")
{
@Override
protected void done ()
{
boolean workerSuccess = true;
try
{
workerSuccess = get();
}
catch (InterruptedException e)
{
Logger.logDebug("Swingworker Exception", e);
}
catch (ExecutionException e)
{
Logger.logDebug("Swingworker Exception", e.getCause());
}
if (!workerSuccess)
{
ErrorUtils.tossError("No usable authlib available. Please check your firewall rules and network connection. Can't start MC without working authlib. Launch button will be disabled.");
}
if (workerSuccess && disableLaunchButton == false)
{
LaunchFrame.getInstance().getLaunch().setEnabled(true);
}
}
};
authworker.execute();
LoadingDialog.advance("Creating Console window");
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run ()
{
if (!CommandLineSettings.getSettings().isNoConsole() && Settings.getSettings().getConsoleActive())
{
LaunchFrame.con = new LauncherConsole();
Logger.addListener(LaunchFrame.con);
LaunchFrame.con.refreshLogs();
LaunchFrame.con.setVisible(true);
}
}
});
MainHelpers.googleAnalytics();
LoadingDialog.advance("Creating main window");
try
{
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run ()
{
LaunchFrame frame = new LaunchFrame(2);
LaunchFrame.setInstance(frame);
// Set up System Tray
if (SystemTray.isSupported() && !CommandLineSettings.getSettings().isDisableTray())
{
LaunchFrame.getInstance().setUpSystemTray();
}
else
{
Logger.logDebug("System Tray not supported");
}
}
});
}
catch (InvocationTargetException e)
{
Logger.logDebug("failed", e.getCause());
}
catch (InterruptedException e)
{}
LoadingDialog.advance("Setting up Launcher");
/*
* Show the main form but hide it behind any active windows until
* loading is complete to prevent display issues.
*
* @TODO ModpacksPane has a display issue with packScroll if the
* main form is not visible when constructed.
*/
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run ()
{
// LaunchFrame.getInstance().setVisible(true);
// LaunchFrame.getInstance().toBack();
}
});
eventBus.register(LaunchFrame.getInstance().thirdPartyPane);
eventBus.register(LaunchFrame.getInstance().modPacksPane);
// ModPack.loadXml(getXmls());
Map.addListener(LaunchFrame.getInstance().mapsPane);
TexturePack.addListener(LaunchFrame.getInstance().tpPane);
/*
* Run UpdateChecker swingworker. done() will open LauncherUpdateDialog if needed
*/
int beta_ = beta;
int v = CommandLineSettings.getSettings().getManualVersion();
int b = CommandLineSettings.getSettings().getManualBuildNumber();
UpdateChecker updateChecker = new UpdateChecker((v == 0 ? Constants.buildNumber : v), LaunchFrame.getInstance().minUsable, (b == 0 ? beta_ : b))
{
@Override
protected void done ()
{
try
{
if (get())
{
LauncherUpdateDialog p = new LauncherUpdateDialog(this, LaunchFrame.getInstance().minUsable);
p.setVisible(true);
}
}
catch (InterruptedException e)
{
Logger.logDebug("Swingworker Exception", e);
}
catch (ExecutionException e)
{
Logger.logDebug("Swingworker Exception", e.getCause());
}
}
};
updateChecker.execute();
LoadingDialog.advance("Downloading pack data");
}
private static ArrayList<String> getXmls ()
{
ArrayList<String> s = Settings.getSettings().getPrivatePacks();
if (s == null)
{
s = new ArrayList<String>();
}
for(int i = 0; i < s.size(); i++)
{
if (s.get(i).isEmpty())
{
s.remove(i);
i--;
}
else
{
String temp = s.get(i);
if (!temp.endsWith(".xml"))
{
s.remove(i);
s.add(i, temp + ".xml");
}
}
}
s.add(0, "modpacks.xml");
s.add(1, "thirdparty.xml");
return s;
}
}