package installer;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.zip.GZIPInputStream;
import javax.swing.JOptionPane;
import lzma.sdk.lzma.Decoder;
import lzma.streams.LzmaInputStream;
/**
* This gets packaged together with install-data.zip.lzma,
* and run as the main class for the installer distributed to users.
*/
public class InstallerMain {
static class AlreadyHandledException extends Exception {
/* not serializable */
private static final long serialVersionUID = new Random().nextLong();
}
public static void main(String[] args) {
File tempDir = null;
ProgressDialog dlg = ProgressDialog.openModal(null, "MCF Installer");
File globalMCFCacheDir = new File(Utils.getMinecraftDirectory(), "minecraft-forkage-install-cache");
if(!globalMCFCacheDir.exists()) {
if(!globalMCFCacheDir.mkdirs())
globalMCFCacheDir = null;
}
try {
Map<String, byte[]> installData;
dlg.startIndeterminate("Unpacking installer");
{
InputStream embeddedInstallDataStream = InstallerMain.class.getResourceAsStream("/install-data.zip.lzma");
if(embeddedInstallDataStream != null)
embeddedInstallDataStream = new LzmaInputStream(embeddedInstallDataStream, new Decoder());
else {
embeddedInstallDataStream = InstallerMain.class.getResourceAsStream("/install-data.zip.gz");
if(embeddedInstallDataStream != null)
embeddedInstallDataStream = new GZIPInputStream(embeddedInstallDataStream);
else {
File devInstallDataFile = new File("../../build/install-data.zip.lzma");
if(devInstallDataFile.exists())
embeddedInstallDataStream = new LzmaInputStream(new FileInputStream(devInstallDataFile), new Decoder());
else {
devInstallDataFile = new File("../../build/install-data.zip.gz");
if(devInstallDataFile.exists()) {
embeddedInstallDataStream = new GZIPInputStream(new FileInputStream(devInstallDataFile));
} else {
JOptionPane.showMessageDialog(null, "The install data file is missing. Whoever created this installer screwed something up.", "MCF Installer Failure", JOptionPane.ERROR_MESSAGE);
throw new AlreadyHandledException();
}
}
}
}
try (InputStream in = embeddedInstallDataStream) {
installData = Utils.readZip(in);
}
}
Properties installProperties = new Properties();
if(!installData.containsKey("install.properties")) {
JOptionPane.showMessageDialog(null, "The install properties file is missing. Whoever created this installer screwed something up.", "MCF Installer Failure", JOptionPane.ERROR_MESSAGE);
throw new AlreadyHandledException();
}
try (Reader reader = new InputStreamReader(new ByteArrayInputStream(installData.get("install.properties")))) {
installProperties.load(reader);
}
String mcver = installProperties.getProperty("mcver");
String launcherVersionName = installProperties.getProperty("launcherVersionName");
if(mcver == null || launcherVersionName == null) {
JOptionPane.showMessageDialog(null, "The install properties file is corrupted. Whoever created this installer screwed something up.", "MCF Installer Failure", JOptionPane.ERROR_MESSAGE);
throw new AlreadyHandledException();
}
tempDir = File.createTempFile("MCF-INSTALLER-", ".tmp");
if(!tempDir.delete() || !tempDir.mkdirs())
throw new Exception("Failed to create directory "+tempDir.getAbsolutePath());
// create a README file in our temporary directory in case
// something stops us from deleting it when done
try (FileOutputStream out = new FileOutputStream(new File(tempDir, "AAA TEMP FOLDER README.TXT"))) {
out.write("This directory was created by the Minecraft Forkage installer. Unless the installer is still running, you can safely delete it at any time.".getBytes(StandardCharsets.UTF_8));
}
System.out.println("Temp dir: "+tempDir);
dlg.startIndeterminate("Downloading client");
byte[] mcClient = Utils.download(dlg, "http://s3.amazonaws.com/Minecraft.Download/versions/"+mcver+"/"+mcver+".jar", globalMCFCacheDir, "minecraft."+mcver+".jar", "minecraft.jar");
if(mcClient == null)
throw new AlreadyHandledException(); // Utils.download already displayed an error message
File clientFile = new File(tempDir, "client.jar");
try (FileOutputStream out = new FileOutputStream(clientFile)) {
out.write(mcClient);
}
dlg.startIndeterminate("Downloading server");
byte[] mcServer = Utils.download(dlg, "http://s3.amazonaws.com/Minecraft.Download/versions/"+mcver+"/minecraft_server."+mcver+".jar", globalMCFCacheDir, "minecraft_server."+mcver+".jar", "minecraft_server.jar");
if(mcServer == null)
throw new AlreadyHandledException();
File serverFile = new File(tempDir, "server.jar");
try (FileOutputStream out = new FileOutputStream(serverFile)) {
out.write(mcServer);
}
mcClient = null;
mcServer = null;
dlg.startIndeterminate("Installing");
File mainJarFile = Installer.install(clientFile, serverFile, tempDir, installData, dlg);
///////// INSTALL VERSION IN MINECRAFT LAUNCHER /////////
dlg.startIndeterminate("Installing in launcher");
File versionDir = new File(new File(Utils.getMinecraftDirectory(), "versions"), launcherVersionName);
if(versionDir.exists()) {
System.out.println(versionDir+" already exists, deleting...");
deleteRecursive(versionDir);
}
if(!versionDir.isDirectory() && !versionDir.mkdirs()) {
try {Thread.sleep(1000);} catch(InterruptedException e) {}
if(!versionDir.isDirectory() && !versionDir.mkdirs())
throw new Exception("Failed to create directory: "+versionDir);
}
// Install JSON file (by copying from install data)
try (FileOutputStream versionJsonOut = new FileOutputStream(new File(versionDir, launcherVersionName+".json"))) {
versionJsonOut.write(installData.get("install.json"));
}
// Install core JAR file (by copying from temp dir)
try (FileOutputStream coreJarOut = new FileOutputStream(new File(versionDir, launcherVersionName+"-core.jar"))) {
try (FileInputStream jarIn = new FileInputStream(mainJarFile)) {
Utils.copyStream(jarIn, coreJarOut);
}
}
// Install bootstrap JAR file (by copying from install data)
try (FileOutputStream bootstrapJarOut = new FileOutputStream(new File(versionDir, launcherVersionName+".jar"))) {
bootstrapJarOut.write(installData.get("packer.jar"));
}
// Done!
if(!Boolean.getBoolean("minecraftforkage.installer.skipDoneMessage"))
JOptionPane.showMessageDialog(dlg, "Minecraft Forkage was successfully installed.\nVersion name in launcher: "+launcherVersionName, "MCF Installer - Done!", JOptionPane.INFORMATION_MESSAGE);
} catch(AlreadyHandledException e) {
// do nothing
} catch(Throwable e) {
e.printStackTrace(); // in case someone is running this in a console
StringWriter sw = new StringWriter();
try (PrintWriter pw = new PrintWriter(sw)) {
e.printStackTrace(pw);
}
JOptionPane.showMessageDialog(null, sw.toString(), "MCF Installer - unexpected error", JOptionPane.ERROR_MESSAGE);
} finally {
dlg.setVisible(false);
}
if(tempDir != null) {
// Some antivirus programs (MSSE) seem to stop us deleting our temporary files?
// Therefore causing this process to stay in the background indefinitely.
// Avoid this by exiting after a fixed amount of time even if we're not done.
new Thread() {
{setName("Temporary folder deletion watchdog timer");}
public void run() {
try {Thread.sleep(10000);} catch(Exception e) {}
Runtime.getRuntime().halt(0);
};
}.start();
deleteRecursive(tempDir);
}
System.exit(0);
}
private static void deleteRecursive(File f) {
if(f.isDirectory())
for(File child : f.listFiles())
deleteRecursive(child);
f.delete();
}
}