diff --git a/.gitignore b/.gitignore
index 32858aad3..00eaba66d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,16 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+
+.gradle
+.nb-gradle
+
+*.map
+*.bat
+*.log
+.mine*
+
+HMCLAPI/build/
+HMCL/build/
+HMCSM/build/
+MetroLookAndFeel/build/
\ No newline at end of file
diff --git a/.nb-gradle-properties b/.nb-gradle-properties
new file mode 100644
index 000000000..f6410c777
--- /dev/null
+++ b/.nb-gradle-properties
@@ -0,0 +1,46 @@
+
+
+
+ j2se
+ 1.8
+ 1.8
+
+
+ launch4j
+ no
+
+ launch4j
+
+
+
+
+
+ launch4jpack
+ no
+
+ launch4jpack
+
+
+
+
+
+ publish
+ no
+
+ clean
+ build
+ launch4j
+
+
+
+
+
+
+ j2se
+ 1.8
+
+
+ GNU General Public License
+ huangyuhui
+
+
diff --git a/HMCL/build.gradle b/HMCL/build.gradle
new file mode 100644
index 000000000..31c8bcb19
--- /dev/null
+++ b/HMCL/build.gradle
@@ -0,0 +1,95 @@
+apply plugin: 'launch4j'
+apply plugin: 'me.tatarka.retrolambda'
+
+if (!hasProperty('mainClass')) {
+ ext.mainClass = 'org.jackhuang.hellominecraft.launcher.Main'
+}
+
+String mavenGroupId = 'HMCL'
+String mavenVersion = '2.3.2'
+String bundleName = "Hello Minecraft! Launcher"
+
+group = mavenGroupId
+version = mavenVersion
+
+String mavenArtifactId = name
+
+buildscript {
+ repositories {
+ mavenCentral();
+
+ dependencies {
+ classpath 'net.sf.proguard:proguard-gradle:4.10'
+ classpath 'edu.sc.seis.gradle:launch4j:1.0.6'
+ classpath 'me.tatarka:gradle-retrolambda:3.1.0'
+ }
+ }
+}
+
+configure(install.repositories.mavenInstaller) {
+ pom.project {
+ groupId = mavenGroupId
+ artifactId = mavenArtifactId
+ version = mavenVersion
+ }
+}
+
+dependencies {
+ compile project(":MetroLookAndFeel")
+ compile project(":HMCLAPI")
+}
+
+retrolambda {
+ javaVersion = JavaVersion.VERSION_1_7
+}
+
+jar {
+ jar.classifier = 'base'
+ from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+
+ manifest {
+ attributes 'Created-By' : 'Copyright(c) 2013-2014 huangyuhui.',
+ 'Main-Class' : mainClass
+ }
+}
+
+task proguard(type: proguard.gradle.ProGuardTask, dependsOn: jar) {
+ ext {
+ def re = jar.classifier
+ injar = jar.archivePath
+ jar.classifier = ''
+ outjar = jar.archivePath
+ jar.classifier = re
+ }
+
+ injars injar
+ outjars outjar
+ printusage "shrinking_" + version + ".map"
+ printmapping "obfuscate_" + version + ".map"
+
+ configuration 'proguard.pro'
+}
+
+launch4j {
+ launch4jCmd = 'D:\\Develop\\Java\\Launch4j\\launch4j.exe'
+ supportUrl = 'http://www.mcbbs.net/thread-142335-1-1.html'
+ jreMinVersion = '1.6.0'
+
+ mainClassName = mainClass
+ icon = new File(project.buildDir, '../icon.ico').absolutePath
+ version = mavenVersion + '.0'
+ downloadUrl = 'http://java.com/download'
+ copyright = "Copyright(c) 2013-2014 huangyuhui."
+
+ jar = new File(project.buildDir, 'libs/' + mavenGroupId + '-' + mavenVersion + '.jar').absolutePath
+ outfile = mavenGroupId + '-' + mavenVersion + '.exe'
+ messagesJreVersionError = 'This application requires a Java Runtime Environment installation, or the runtime is corrupted.\n\u6ca1\u6709\u627e\u5230\u004a\u0061\u0076\u0061\u8fd0\u884c\u65f6\uff0c\u8bf7\u4e0d\u8981\u4f7f\u7528\u7eff\u8272\u004a\u0061\u0076\u0061\uff0c\u8bf7\u4f7f\u7528\u5b89\u88c5\u7248\u7684\u004a\u0061\u0076\u0061\uff0c\u70b9\u51fb\u786e\u5b9a\u8fdb\u5165\u004a\u0061\u0076\u0061\u5b89\u88c5\u9875\u9762\u3002'
+}
+
+processResources {
+ from(sourceSets.main.resources.srcDirs) {
+ exclude 'icon.icns'
+ }
+}
+
+build.dependsOn proguard
\ No newline at end of file
diff --git a/HMCL/icon.ico b/HMCL/icon.ico
new file mode 100644
index 000000000..fe7ac703b
Binary files /dev/null and b/HMCL/icon.ico differ
diff --git a/HMCL/image/3J33V83MSXJ6FSO4%WBZR7A.png b/HMCL/image/3J33V83MSXJ6FSO4%WBZR7A.png
new file mode 100644
index 000000000..091fcdf14
Binary files /dev/null and b/HMCL/image/3J33V83MSXJ6FSO4%WBZR7A.png differ
diff --git a/HMCL/image/A.jpg b/HMCL/image/A.jpg
new file mode 100644
index 000000000..b20c78c8d
Binary files /dev/null and b/HMCL/image/A.jpg differ
diff --git a/HMCL/image/K0B_H3C~2_PSK_[SB)([))E.png b/HMCL/image/K0B_H3C~2_PSK_[SB)([))E.png
new file mode 100644
index 000000000..83b1e57f1
Binary files /dev/null and b/HMCL/image/K0B_H3C~2_PSK_[SB)([))E.png differ
diff --git a/HMCL/image/N9_Q[X2BQ{9$@S14RX%3}0M.png b/HMCL/image/N9_Q[X2BQ{9$@S14RX%3}0M.png
new file mode 100644
index 000000000..8c55c559c
Binary files /dev/null and b/HMCL/image/N9_Q[X2BQ{9$@S14RX%3}0M.png differ
diff --git a/HMCL/image/WO(NA%0}M$9BCRR62ZN{%87.png b/HMCL/image/WO(NA%0}M$9BCRR62ZN{%87.png
new file mode 100644
index 000000000..dae14da11
Binary files /dev/null and b/HMCL/image/WO(NA%0}M$9BCRR62ZN{%87.png differ
diff --git a/HMCL/image/background.jpg b/HMCL/image/background.jpg
new file mode 100644
index 000000000..972dc4e6e
Binary files /dev/null and b/HMCL/image/background.jpg differ
diff --git a/HMCL/launch4j.xml b/HMCL/launch4j.xml
new file mode 100644
index 000000000..b78c4bf25
--- /dev/null
+++ b/HMCL/launch4j.xml
@@ -0,0 +1,41 @@
+
+ false
+ gui
+ F:\Documents\NetBeansProjects\HMCL\HMCL\build\libs\HMCL-2.1.3.jar
+ F:\Documents\NetBeansProjects\HMCL\HMCL\build\libs\HMCL-2.1.3-Launch4j.exe
+
+
+
+ normal
+ http://java.com/download
+
+ false
+
+ F:\Documents\NetBeansProjects\HMCL\HMCL\icon.ico
+
+
+ false
+ 1.6.0
+
+ preferJre
+
+
+ 2.1.3.0
+ 2.1.3.0
+ Hello Minecraft! Launcher
+ Copyright(c) 2013~2014 huangyuhui
+ 2.1.3.0
+ 2.1.3.0
+ Hello Minecraft! Launcher
+
+ Hello Minecraft! Launcher
+ HMCL-2.1.3.exe
+
+
+ 启动程序时出现错误
+ 未找到内置Java
+ 此程序需要Java
+ 未找到Java
+ 已存在一个程序实例
+
+
\ No newline at end of file
diff --git a/HMCL/proguard.pro b/HMCL/proguard.pro
new file mode 100644
index 000000000..cda8d2319
--- /dev/null
+++ b/HMCL/proguard.pro
@@ -0,0 +1,77 @@
+-libraryjars D:\Develop\Java\jdk1.6.0_45\jre\lib\rt.jar
+-libraryjars D:\Develop\Java\jdk1.6.0_45\jre\lib\jce.jar
+-libraryjars D:\Develop\Java\jdk1.6.0_45\jre\lib\jsse.jar
+
+-dontoptimize
+-dontshrink
+-dontwarn java.lang.invoke.*
+
+-overloadaggressively
+-repackageclasses 'org.jackhuang.hellominecraft.launcher'
+-allowaccessmodification
+
+-renamesourcefileattribute SourceFile
+
+-keepattributes *Annotation*,SourceFile,LineNumberTable,Signature
+
+-keep class com.** { *; }
+-keep class org.jackhuang.hellominecraft.lookandfeel.* { *; }
+-keep class org.jackhuang.hellominecraft.lookandfeel.painters.* { *; }
+-keep class org.jackhuang.hellominecraft.lookandfeel.ui.* { *; }
+
+-keepclassmembers class org.jackhuang.mojang.authlib.Agent { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.UserType { *; }
+
+-keepclassmembers class org.jackhuang.mojang.authlib.properties.Property { *; }
+
+-keepclassmembers class org.jackhuang.mojang.authlib.minecraft.MinecraftProfileTexture$Type { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.minecraft.MinecraftProfileTexture { *; }
+
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.request.AuthenticationRequest { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.request.InvalidateRequest { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.request.JoinMinecraftServerRequest { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.request.RefreshRequest { *; }
+
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.AuthenticationResponse { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.HasJoinedMinecraftServerResponse { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.ProfileSearchResultsResponse { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.RefreshResponse { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.Response { *; }
+-keepclassmembers class org.jackhuang.mojang.authlib.yggdrasil.response.User { *; }
+
+-keep class org.jackhuang.hellominecraft.launcher.Main { public static void main(java.lang.String[]); }
+-keep class org.jackhuang.hellominecraft.launcher.Launcher { public static void main(java.lang.String[]); }
+
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.settings.Profile { private ; public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.settings.Config { public ; }
+
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.assets.AssetsObject { ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.assets.AssetsIndex { ; }
+
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.version.MinecraftLibrary { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.version.IMinecraftLibrary { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.version.Natives { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.version.OS { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.version.Rules { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion { public ; }
+
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.forge.InstallProfile { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.forge.Install { public ; }
+
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.forge.vanilla.MinecraftForgeVersionRoot { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.forge.vanilla.MinecraftForgeVersion { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.forge.bmcl.ForgeVersion { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.forge.bmcl.Downloads { public ; }
+
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.optifine.OptiFineVersion { public ; }
+
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderVersionsRoot { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderMCVersions { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderVersion { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderVersionsMeta { public ; }
+
+-keepclassmembers class org.jackhuang.hellominecraft.version.MinecraftRemoteLatestVersion { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.version.MinecraftRemoteVersion { public ; }
+-keepclassmembers class org.jackhuang.hellominecraft.version.MinecraftRemoteVersions { public ; }
diff --git a/HMCL/src/main/icon.icns b/HMCL/src/main/icon.icns
new file mode 100644
index 000000000..dc329d24b
Binary files /dev/null and b/HMCL/src/main/icon.icns differ
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/Launcher.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/Launcher.java
new file mode 100644
index 000000000..4cb62e5e3
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/Launcher.java
@@ -0,0 +1,129 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import javax.swing.SwingUtilities;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.utils.functions.TrueDoneListener;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.views.LogWindow;
+import org.jackhuang.hellominecraft.launcher.launch.MinecraftCrashAdvicer;
+import org.jackhuang.hellominecraft.utils.DoubleOutputStream;
+import org.jackhuang.hellominecraft.utils.JdkVersion;
+import org.jackhuang.hellominecraft.utils.LauncherPrintStream;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.OS;
+import org.jackhuang.hellominecraft.utils.Utils;
+
+/**
+ *
+ * @author hyh
+ */
+public final class Launcher {
+
+ private static final Launcher instance = new Launcher();
+
+ public static void println(String s) {
+ System.out.println(s);
+ }
+
+ public static void main(String[] args) {
+ println("*** " + Main.makeTitle() + " ***");
+
+ Thread.currentThread().setName("launcher");
+ try {
+ File logFile = new File("hmclmc.log");
+ if (!logFile.exists()) logFile.createNewFile();
+ FileOutputStream tc = new FileOutputStream(logFile);
+ DoubleOutputStream out = new DoubleOutputStream(tc, System.out);
+ System.setOut(new LauncherPrintStream(out));
+ DoubleOutputStream err = new DoubleOutputStream(tc, System.err);
+ System.setErr(new LauncherPrintStream(err));
+ } catch (Exception e) {
+ println("Failed to add log file appender.");
+ e.printStackTrace();
+ }
+
+ LogWindow.instance.setTerminateGame(Utils::shutdownForcely);
+
+ boolean showInfo = false;
+ String classPath = "";
+ String mainClass = "net.minecraft.client.Minecraft";
+
+ ArrayList cmdList = new ArrayList<>();
+
+ for (String s : args)
+ if (s.startsWith("-cp=")) classPath = classPath.concat(s.substring("-cp=".length()));
+ else if (s.startsWith("-mainClass=")) mainClass = s.substring("-mainClass=".length());
+ else if (s.equals("-debug")) showInfo = true;
+ else cmdList.add(s);
+
+ String[] cmds = (String[]) cmdList.toArray(new String[cmdList.size()]);
+
+ String[] tokenized = StrUtils.tokenize(classPath, File.pathSeparator);
+ int len = tokenized.length;
+
+ if (showInfo) {
+ println("Arguments: {\n" + StrUtils.parseParams(" ", args, "\n") + "\n}");
+ println("Main Class: " + mainClass);
+ println("Class Path: {\n" + StrUtils.parseParams(" ", tokenized, "\n") + "\n}");
+ SwingUtilities.invokeLater(() -> LogWindow.instance.setVisible(true));
+ }
+
+ URL[] urls = new URL[len];
+
+ try {
+ for (int j = 0; j < len; j++)
+ urls[j] = new File(tokenized[j]).toURI().toURL();
+ } catch (Throwable e) {
+ MessageBox.Show(C.i18n("crash.main_class_not_found"));
+ println("Failed to get classpath.");
+ e.printStackTrace();
+ return;
+ }
+
+ if (!JdkVersion.isJava64Bit() && OS.is64Bit())
+ MessageBox.Show(C.i18n("advice.os64butjdk32"));
+
+ Method minecraftMain;
+ try {
+ minecraftMain = new URLClassLoader(urls).loadClass(mainClass).getMethod("main", String[].class);
+ } catch (Throwable t) {
+ MessageBox.Show(C.i18n("crash.main_class_not_found"));
+ println("Minecraft main class not found.");
+ return;
+ }
+
+ println("*** Launching Game ***");
+
+ try {
+ minecraftMain.invoke(null, new Object[]{cmds});
+ } catch (Throwable throwable) {
+ HMCLog.err("Cought exception!");
+ final StringWriter trace = new StringWriter();
+ PrintWriter writer = new PrintWriter(trace);
+ throwable.printStackTrace(writer);
+ final String advice = MinecraftCrashAdvicer.getAdvice(throwable);
+ MessageBox.Show(C.i18n("crash.minecraft") + ": " + advice);
+
+ LogWindow.instance.log(C.i18n("crash.minecraft"));
+ LogWindow.instance.log(advice);
+ LogWindow.instance.log(trace.toString());
+ LogWindow.instance.setExit(TrueDoneListener.instance);
+ LogWindow.instance.setVisible(true);
+ }
+
+ println("*** Game Exited ***");
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/Main.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/Main.java
new file mode 100644
index 000000000..8e64bde4a
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/Main.java
@@ -0,0 +1,126 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import javax.swing.ImageIcon;
+import javax.swing.UIManager;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.utils.functions.DoneListener0;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.launch.GameLauncher;
+import org.jackhuang.hellominecraft.launcher.utils.CrashReport;
+import org.jackhuang.hellominecraft.logging.Configuration;
+import org.jackhuang.hellominecraft.logging.appender.ConsoleAppender;
+import org.jackhuang.hellominecraft.logging.layout.DefaultLayout;
+import org.jackhuang.hellominecraft.views.LogWindow;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.launcher.views.MainFrame;
+import org.jackhuang.hellominecraft.lookandfeel.HelloMinecraftLookAndFeel;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+
+/**
+ *
+ * @author hyh
+ */
+public final class Main implements DoneListener0 {
+
+ public static String launcherName = "Hello Minecraft! Launcher";
+ public static byte firstVer = 2, secondVer = 3, thirdVer = 2;
+ public static int minimumLauncherVersion = 16;
+
+ /**
+ * Make the version of HMCL.
+ *
+ * @return the version: firstVer.secondVer.thirdVer
+ */
+ public static String makeVersion() {
+ return "" + firstVer + '.' + secondVer + '.' + thirdVer;
+ }
+
+ /**
+ * Make the main window title.
+ *
+ * @return the MainWindow title.
+ */
+ public static String makeTitle() {
+ return launcherName + ' ' + makeVersion();
+ }
+
+ public static final Main instance = new Main();
+
+ public static void main(String[] args) {
+ {
+ Thread.setDefaultUncaughtExceptionHandler(new CrashReport(true));
+
+ try {
+ File file = new File("hmcl.log");
+ if (!file.exists())
+ file.createNewFile();
+ Configuration.DEFAULT.appenders.add(new ConsoleAppender("File", new DefaultLayout(), true, new FileOutputStream(file), true));
+ } catch (IOException ex) {
+ System.err.println("Failed to add log appender File because an error occurred while creating or opening hmcl.log");
+ ex.printStackTrace();
+ }
+
+ HMCLog.log("*** " + Main.makeTitle() + " ***");
+
+ LogWindow.instance.clean();
+ LogWindow.instance.setTerminateGame(GameLauncher.PROCESS_MANAGER::stopAllProcesses);
+
+ try {
+ UIManager.setLookAndFeel(new HelloMinecraftLookAndFeel());
+ } catch (Throwable ex) {
+ HMCLog.warn("Failed to set look and feel...", ex);
+ }
+
+ Settings.UPDATE_CHECKER.start();
+
+ MainFrame.showMainFrame(Settings.isFirstLoad());
+ }
+ }
+
+ @Override
+ public void onDone() {
+ GameLauncher.PROCESS_MANAGER.stopAllProcesses();
+ }
+
+ public static void update() {
+ if (MessageBox.Show(C.i18n("update.newest_version") + Settings.UPDATE_CHECKER.getNewVersion().firstVer + "." + Settings.UPDATE_CHECKER.getNewVersion().secondVer + "." + Settings.UPDATE_CHECKER.getNewVersion().thirdVer + "\n"
+ + C.i18n("update.should_open_link"),
+ MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
+ try {
+ java.awt.Desktop.getDesktop().browse(new URI(C.URL_PUBLISH));
+ } catch (Throwable e) {
+ HMCLog.warn("Failed to browse uri: " + C.URL_PUBLISH, e);
+
+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
+ cb.setContents(new StringSelection(C.URL_PUBLISH), null);
+ MessageBox.Show(C.i18n("update.no_browser"));
+ }
+ else
+ Settings.s().setCheckUpdate(false);
+ }
+
+ public static void invokeUpdate() {
+ if (Settings.s().isCheckUpdate()) update();
+ MainFrame.instance.invokeUpdate();
+ }
+
+ public static ImageIcon getIcon(String path) {
+ try {
+ return new ImageIcon(Main.class.getResource("/org/jackhuang/hellominecraft/launcher/" + path));
+ } catch (Exception e) {
+ HMCLog.err("Failed to load icon", e);
+ return null;
+ }
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/DefaultGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/DefaultGameLauncher.java
new file mode 100644
index 000000000..1b3f7ca5f
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/DefaultGameLauncher.java
@@ -0,0 +1,69 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.io.IOException;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.launch.GameLauncher.DownloadLibraryJob;
+import org.jackhuang.hellominecraft.launcher.utils.auth.IAuthenticator;
+import org.jackhuang.hellominecraft.launcher.utils.auth.LoginInfo;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.tasks.ParallelTask;
+import org.jackhuang.hellominecraft.tasks.TaskWindow;
+import org.jackhuang.hellominecraft.tasks.download.FileDownloadTask;
+import org.jackhuang.hellominecraft.utils.Compressor;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+
+/**
+ *
+ * @author hyh
+ */
+public class DefaultGameLauncher extends GameLauncher {
+
+ public DefaultGameLauncher(Profile version, LoginInfo info, IAuthenticator lg) {
+ super(version, info, lg);
+ register();
+ }
+
+ public DefaultGameLauncher(Profile version, LoginInfo info, IAuthenticator lg, DownloadType downloadType) {
+ super(version, info, lg, downloadType);
+ register();
+ }
+
+ private void register() {
+ downloadLibrariesEvent.register((sender, t) -> {
+ final TaskWindow dw = TaskWindow.getInstance();
+ ParallelTask parallelTask = new ParallelTask();
+ for (DownloadLibraryJob o : t) {
+ final DownloadLibraryJob s = (DownloadLibraryJob) o;
+ parallelTask.addDependsTask(new FileDownloadTask(s.url, s.path).setTag(s.name));
+ }
+ dw.addTask(parallelTask);
+ boolean flag = true;
+ if (t.size() > 0) flag = dw.start();
+ if (!flag && MessageBox.Show(C.i18n("launch.not_finished_downloading_libraries"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
+ flag = true;
+ return flag;
+ });
+ decompressNativesEvent.register((sender, value) -> {
+ //boolean flag = true;
+ for (int i = 0; i < value.decompressFiles.length; i++)
+ try {
+ Compressor.unzip(value.decompressFiles[i], value.decompressTo, value.extractRules[i]);
+ } catch (IOException ex) {
+ HMCLog.err("Unable to decompress library file: " + value.decompressFiles[i] + " to " + value.decompressTo, ex);
+ //flag = false;
+ }
+ /*if(!flag)
+ if(MessageBox.Show(C.i18n("launch.not_finished_decompressing_natives"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
+ flag = true;*/
+ return true;
+ });
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/GameLauncher.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/GameLauncher.java
new file mode 100644
index 000000000..ee5f61140
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/GameLauncher.java
@@ -0,0 +1,191 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.utils.auth.IAuthenticator;
+import org.jackhuang.hellominecraft.launcher.utils.auth.LoginInfo;
+import org.jackhuang.hellominecraft.launcher.utils.auth.UserProfileProvider;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.JavaProcess;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.OS;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.EventHandler;
+import org.jackhuang.hellominecraft.utils.ProcessManager;
+
+/**
+ *
+ * @author hyh
+ */
+public class GameLauncher {
+
+ public static final ProcessManager PROCESS_MANAGER = new ProcessManager();
+ Profile get;
+ IMinecraftProvider provider;
+ LoginInfo info;
+ UserProfileProvider result;
+ IAuthenticator login;
+ public final EventHandler failEvent = new EventHandler(this);
+ public final EventHandler> downloadLibrariesEvent = new EventHandler(this);
+ public final EventHandler> successEvent = new EventHandler(this);
+ public final EventHandler launchEvent = new EventHandler(this);
+ public final EventHandler decompressNativesEvent = new EventHandler(this);
+ DownloadType downloadType;
+
+ public GameLauncher(Profile version, LoginInfo info, IAuthenticator lg) {
+ this(version, info, lg, DownloadType.Mojang);
+ }
+
+ public GameLauncher(Profile version, LoginInfo info, IAuthenticator lg, DownloadType downloadType) {
+ this.get = version;
+ this.provider = get.getMinecraftProvider();
+ this.info = info;
+ this.login = lg;
+ this.downloadType = downloadType;
+ }
+
+ public Profile getProfile() {
+ return get;
+ }
+
+ public IMinecraftLoader makeLaunchCommand() {
+ IMinecraftLoader loader;
+ try {
+ if (info != null) result = login.login(info);
+ else result = login.loginBySettings();
+ } catch (Exception e) {
+ HMCLog.err("An exception has thrown when logging in.", e);
+ result = new UserProfileProvider();
+ result.setSuccess(false);
+ result.setErrorReason(e.getLocalizedMessage());
+ }
+ if (result == null || result.isSuccessful() == false) {
+ String error;
+ if (result == null || result.getErrorReason() == null)
+ error = C.i18n("login.failed");
+ else {
+ error = C.i18n("login.failed") + result.getErrorReason();
+ HMCLog.warn("Login failed by method: " + login.getName() + ", state: " + result.isSuccessful() + ", error reason: " + result.getErrorReason());
+ }
+ failEvent.execute(error);
+ return null;
+ }
+
+ try {
+ loader = provider.provideMinecraftLoader(result, downloadType);
+ } catch (IllegalStateException e) {
+ HMCLog.err("Failed to get minecraft loader", e);
+ failEvent.execute(C.i18n("launch.circular_dependency_versions"));
+ return null;
+ }
+
+ File file = provider.getDecompressNativesToLocation();
+ if (file != null) FileUtils.cleanDirectoryQuietly(file);
+
+ if(!downloadLibrariesEvent.execute(provider.getDownloadLibraries(downloadType))) { failEvent.execute(C.i18n("launch.failed")); return null; }
+ if(!decompressNativesEvent.execute(provider.getDecompressLibraries())) { failEvent.execute(C.i18n("launch.failed")); return null; }
+ successEvent.execute(loader.makeLaunchingCommand());
+ return loader;
+ }
+
+ /**
+ * Launch the game "as soon as possible".
+ *
+ * @param str launch command
+ */
+ public void launch(List str) {
+ try {
+ provider.onLaunch();
+ ProcessBuilder builder = new ProcessBuilder(str);
+ builder.directory(provider.getRunDirectory(get.getSelectedMinecraftVersion().id))
+ .environment().put("APPDATA", get.getCanonicalGameDirFile().getParent());
+ JavaProcess jp = new JavaProcess(str, builder.start(), PROCESS_MANAGER);
+ launchEvent.execute(jp);
+ } catch (IOException e) {
+ failEvent.execute(C.i18n("launch.failed_creating_process") + "\n" + e.getMessage());
+ HMCLog.err("Failed to launch when creating a new process.", e);
+ }
+ }
+
+ /**
+ * According to the name...
+ *
+ * @param launcherName the name of launch bat/sh
+ * @param str launch command
+ * @return launcher location
+ * @throws java.io.IOException write contents failed.
+ */
+ public File makeLauncher(String launcherName, List str) throws IOException {
+ provider.onLaunch();
+ boolean isWin = OS.os() == OS.WINDOWS;
+ File f = new File(launcherName + (isWin ? ".bat" : ".sh"));
+ if (!f.exists()) f.createNewFile();
+ BufferedWriter writer;
+ try {
+ writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), System.getProperty("sun.jnu.encoding", "UTF-8")));
+ } catch (UnsupportedEncodingException ex) {
+ HMCLog.warn("Failed to create writer, will try again.", ex);
+ writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)));
+ }
+ if (isWin) {
+ writer.write("@echo off");
+ writer.newLine();
+ String appdata = IOUtils.tryGetCanonicalFilePath(get.getCanonicalGameDirFile().getParentFile());
+ if (appdata != null) {
+ writer.write("set appdata=" + appdata);
+ writer.newLine();
+ }
+ }
+ writer.write(StrUtils.makeCommand(str));
+ writer.close();
+ if (!isWin)
+ try {
+ Runtime.getRuntime().exec("chmod +x " + IOUtils.tryGetCanonicalFilePath(f));
+ } catch (IOException e) {
+ HMCLog.warn("Failed to give sh file permission.", e);
+ MessageBox.Show(C.i18n("launch.failed_sh_permission"));
+ }
+
+ HMCLog.log("Command: " + StrUtils.parseParams("", str, " "));
+ return f;
+ }
+
+ public static class DownloadLibraryJob {
+
+ String url, name;
+ File path;
+
+ public DownloadLibraryJob(String n, String u, File p) {
+ url = u;
+ name = n;
+ path = IOUtils.tryGetCanonicalFile(p);
+ }
+ }
+
+ public static class DecompressLibraryJob {
+ File[] decompressFiles;
+ String[][] extractRules;
+ File decompressTo;
+
+ public DecompressLibraryJob(File[] decompressFiles, String[][] extractRules, File decompressTo) {
+ this.decompressFiles = decompressFiles;
+ this.extractRules = extractRules;
+ this.decompressTo = decompressTo;
+ }
+
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/GameLauncherRequest.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/GameLauncherRequest.java
new file mode 100644
index 000000000..8be1580b2
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/GameLauncherRequest.java
@@ -0,0 +1,19 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+/**
+ *
+ * @author hyh
+ */
+public enum GameLauncherRequest {
+
+ ERROR,
+ DOWNLOAD_ASSETS,
+ DOWNLOAD_LIBRARIES,
+ UNZIP_LIBRARIES,
+ PROCESS_STARTED,
+ SUCCEED
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/IMinecraftLoader.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/IMinecraftLoader.java
new file mode 100644
index 000000000..301f574f2
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/IMinecraftLoader.java
@@ -0,0 +1,179 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.Launcher;
+import org.jackhuang.hellominecraft.launcher.utils.auth.UserProfileProvider;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.JdkVersion;
+import org.jackhuang.hellominecraft.utils.MathUtils;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.OS;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.Utils;
+
+/**
+ *
+ * @author hyh
+ */
+public abstract class IMinecraftLoader {
+
+ protected File minecraftJar;
+ protected Profile v;
+ protected UserProfileProvider lr;
+ protected File gameDir;
+ protected IMinecraftProvider provider;
+
+ public IMinecraftLoader(Profile ver, IMinecraftProvider provider, UserProfileProvider lr, File minecraftJar) {
+ this.lr = lr;
+
+ this.minecraftJar = minecraftJar;
+ v = ver;
+ this.provider = provider;
+ gameDir = v.getCanonicalGameDirFile();
+ }
+
+ public void makeHeadCommand(List res) {
+ HMCLog.log("On making head command.");
+
+ if (StrUtils.isNotBlank(v.getWrapperLauncher()))
+ res.addAll(Arrays.asList(v.getWrapperLauncher().split(" ")));
+
+ String str = v.getJavaDir();
+ JdkVersion jv = null;
+ File f = new File(str + ".hmc");
+ try {
+ String s = FileUtils.readFileToString(f);
+ String[] strs = s.split("\n");
+ if (str.length() >= 2)
+ jv = new JdkVersion(strs[0], MathUtils.parseInt(strs[1], -1));
+ else
+ throw new IllegalStateException("The format of file: " + f + " is wrong: " + s);
+ } catch (IOException | IllegalStateException e) {
+ try {
+ jv = JdkVersion.getJavaVersionFromExecutable(str);
+ jv.write(f);
+ if (!f.exists())
+ HMCLog.warn("Failed to load version from file " + f, e);
+ } catch (Exception ex) {
+ HMCLog.warn("Failed to read JDKVersion.", ex);
+ }
+ }
+ res.add(str);
+
+ if (v.hasJavaArgs())
+ res.addAll(Arrays.asList(StrUtils.tokenize(v.getJavaArgs())));
+
+ if (!v.isNoJVMArgs() && !(jv != null && jv.isEarlyAccess())) {
+ res.add("-Xincgc");
+ res.add("-XX:+UseConcMarkSweepGC");
+ res.add("-XX:+CMSIncrementalMode");
+ res.add("-XX:-UseAdaptiveSizePolicy");
+
+ res.add("-Xmn128m");
+ }
+
+ if (jv != null && jv.is64Bit == 0 && OS.is64Bit())
+ MessageBox.Show(C.i18n("advice.os64butjdk32"));
+
+ if (!StrUtils.isBlank(v.getMaxMemory())) {
+ int mem = MathUtils.parseMemory(v.getMaxMemory(), 2147483647);
+ if (jv != null && jv.is64Bit == 0 && mem > 1024)
+ MessageBox.Show(C.i18n("launch.too_big_memory_alloc_64bit"));
+ else {
+ long a = OS.getTotalPhysicalMemory() / 1024 / 1024;
+ HMCLog.log("System Physical Memory: " + a);
+ if (a > 0 && a < mem)
+ MessageBox.Show(C.i18n("launch.too_big_memory_alloc_free_space_too_low", a));
+ }
+ String a = "-Xmx" + v.getMaxMemory();
+ if (MathUtils.canParseInt(v.getMaxMemory())) a += "m";
+ res.add(a);
+ }
+
+ if (!StrUtils.isBlank(v.getPermSize()) && !v.isNoJVMArgs())
+ if (jv != null && jv.ver != null && (jv.ver.startsWith("1.8") || jv.ver.startsWith("1.9"))); else res.add("-XX:MaxPermSize=" + v.getPermSize() + "m");
+
+ if (!v.isNoJVMArgs())
+ appendJVMArgs(res);
+
+ HMCLog.log("On making java.library.path.");
+
+ res.add("-Djava.library.path=" + provider.getDecompressNativesToLocation().getPath());//v.getSelectedMinecraftVersion().getNatives(v.getCanonicalGameDirFile()));
+ res.add("-Dfml.ignoreInvalidMinecraftCertificates=true");
+ res.add("-Dfml.ignorePatchDiscrepancies=true");
+
+ if (OS.os() != OS.WINDOWS)
+ res.add("-Duser.home=" + gameDir.getParent());
+
+ if (!v.isCanceledWrapper()) {
+ res.add("-cp");
+ res.add(StrUtils.parseParams("", Utils.getURL(), File.pathSeparator));
+ res.add(Launcher.class.getCanonicalName());
+ }
+ }
+
+ public List makeLaunchingCommand() {
+ HMCLog.log("*** Make shell command ***");
+
+ ArrayList res = new ArrayList<>();
+
+ makeHeadCommand(res);
+ makeSelf(res);
+
+ HMCLog.log("On making launcher args.");
+
+ if (StrUtils.isNotBlank(v.getHeight()) && StrUtils.isNotBlank(v.getWidth())) {
+ res.add("--height");
+ res.add(v.getHeight());
+ res.add("--width");
+ res.add(v.getWidth());
+ }
+
+ if (StrUtils.isNotBlank(v.getServerIp())) {
+ String[] args = v.getServerIp().split(":");
+ res.add("--server");
+ res.add(args[0]);
+ res.add("--port");
+ res.add(args.length > 1 ? args[1] : "25565");
+ }
+
+ if (v.isFullscreen())
+ res.add("--fullscreen");
+
+ if (v.isDebug() && !v.isCanceledWrapper())
+ res.add("-debug");
+
+ if (StrUtils.isNotBlank(v.getMinecraftArgs()))
+ res.addAll(Arrays.asList(v.getMinecraftArgs().split(" ")));
+
+ return res;
+ }
+
+ /**
+ * You must do these things:
+ * 1 minecraft class path
+ * 2 main class
+ * 3 minecraft arguments
+ *
+ * @param list the command list you shoud edit.
+ */
+ protected abstract void makeSelf(List list);
+
+ protected void appendJVMArgs(List list) {
+ }
+
+ public Profile getUserVersion() {
+ return v;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/IMinecraftProvider.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/IMinecraftProvider.java
new file mode 100644
index 000000000..e4f52a374
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/IMinecraftProvider.java
@@ -0,0 +1,63 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import org.jackhuang.hellominecraft.launcher.utils.auth.UserProfileProvider;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public abstract class IMinecraftProvider {
+ Profile profile;
+
+ public IMinecraftProvider(Profile profile) {
+ this.profile = profile;
+ }
+
+ public abstract File getRunDirectory(String id);
+ public abstract List getDownloadLibraries(DownloadType type);
+ public abstract void openSelf(String version);
+ public abstract void open(String version, String folder);
+ public abstract File getAssets();
+ public abstract File getResourcePacks();
+ public abstract GameLauncher.DecompressLibraryJob getDecompressLibraries();
+ public abstract File getDecompressNativesToLocation();
+ public abstract File getMinecraftJar();
+ public abstract File getBaseFolder();
+
+ /**
+ * Launch
+ * @param p player informations, including username & auth_token
+ * @param type according to the class name 233
+ * @return what you want
+ * @throws IllegalStateException circular denpendency versions
+ */
+ public abstract IMinecraftLoader provideMinecraftLoader(UserProfileProvider p, DownloadType type) throws IllegalStateException;
+
+ // Versions
+ public abstract boolean renameVersion(String from, String to);
+ public abstract boolean removeVersionFromDisk(String a);
+ public abstract boolean refreshJson(String a);
+ public abstract boolean refreshAssetsIndex(String a);
+
+ public abstract MinecraftVersion getOneVersion();
+ public abstract Collection getVersions();
+ public abstract MinecraftVersion getVersionById(String id);
+ public abstract int getVersionCount();
+ public abstract void refreshVersions();
+
+ public abstract boolean install(String version, DownloadType type);
+
+ public abstract void onLaunch();
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/LaunchFinisher.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/LaunchFinisher.java
new file mode 100644
index 000000000..2daa0fad2
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/LaunchFinisher.java
@@ -0,0 +1,103 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.utils.functions.TrueDoneListener;
+import org.jackhuang.hellominecraft.launcher.views.MainFrame;
+import org.jackhuang.hellominecraft.utils.tinystream.CollectionUtils;
+import org.jackhuang.hellominecraft.utils.Event;
+import org.jackhuang.hellominecraft.utils.JavaProcess;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.functions.Predicate;
+import org.jackhuang.hellominecraft.utils.ProcessThread;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.views.LogWindow;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class LaunchFinisher implements Event> {
+
+ private final HashSet al = new HashSet();
+
+ @Override
+ public boolean call(Object sender, List str) {
+ final GameLauncher obj = (GameLauncher) sender;
+ obj.launchEvent.register(new Event() {
+ @Override
+ public boolean call(Object sender, JavaProcess p) {
+ if (obj.getProfile().getLauncherVisibility() == 0 && !LogWindow.instance.isVisible())
+ System.exit(0);
+ else if (obj.getProfile().getLauncherVisibility() == 2)
+ MainFrame.instance.closeMessage();
+ else {
+ if (LogWindow.instance.isVisible())
+ LogWindow.instance.setExit(TrueDoneListener.instance);
+ MainFrame.instance.dispose();
+ }
+ Event event = new Event() {
+ @Override
+ public boolean call(Object sender, JavaProcess t) {
+ processThreadStopped((ProcessThread) sender, obj, t, false);
+ return true;
+ }
+ };
+ ProcessThread a = new ProcessThread(p, true, true);
+ a.stopEvent.register(new Event() {
+ @Override
+ public boolean call(Object sender, JavaProcess p) {
+ if (p.getExitCode() != 0 && p.getStdErrLines().size() > 0 && StrUtils.containsOne(p.getStdErrLines(),
+ Arrays.asList("Could not create the Java Virtual Machine.",
+ "Error occurred during initialization of VM",
+ "A fatal exception has occurred. Program will exit.")))
+ MessageBox.Show(C.i18n("launch.cannot_create_jvm"));
+ processThreadStopped((ProcessThread) sender, obj, p, false);
+ return true;
+ }
+ });
+ a.start();
+ al.add(a);
+
+ a = new ProcessThread(p, false, true);
+ a.stopEvent.register(event);
+ a.start();
+ al.add(a);
+
+ a = new ProcessThread(p, false, false);
+ a.stopEvent.register(event);
+ a.start();
+ al.add(a);
+ return true;
+ }
+ });
+ obj.launch(str);
+ return true;
+ }
+
+ void processThreadStopped(ProcessThread t, GameLauncher obj, JavaProcess p, boolean forceTermintate) {
+ al.remove(t);
+ al.removeAll(CollectionUtils.sortOut(al, new Predicate() {
+
+ @Override
+ public boolean apply(Thread t) {
+ return !t.isAlive();
+ }
+
+ }));
+ if (al.isEmpty() || forceTermintate) {
+ for (Thread a : al) a.interrupt();
+ al.clear();
+ GameLauncher.PROCESS_MANAGER.onProcessStopped(p);
+ if (obj.getProfile().getLauncherVisibility() != 2 && !LogWindow.instance.isVisible())
+ System.exit(0);
+ }
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/LaunchScriptFinisher.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/LaunchScriptFinisher.java
new file mode 100644
index 000000000..3b47a2b86
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/LaunchScriptFinisher.java
@@ -0,0 +1,38 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.io.IOException;
+import java.util.List;
+import javax.swing.JOptionPane;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.views.MainFrame;
+import org.jackhuang.hellominecraft.utils.Event;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class LaunchScriptFinisher implements Event> {
+
+ @Override
+ public boolean call(Object sender, List str) {
+ boolean flag = false;
+ try {
+ String s = JOptionPane.showInputDialog(C.i18n("mainwindow.enter_script_name"));
+ if(s != null) MessageBox.Show(C.i18n("mainwindow.make_launch_succeed") + " " + ((GameLauncher)sender).makeLauncher(s, str).getAbsolutePath());
+ flag = true;
+ } catch (IOException ex) {
+ MessageBox.Show(C.i18n("mainwindow.make_launch_script_failed"));
+ HMCLog.err("Failed to create script file.", ex);
+ }
+ MainFrame.instance.closeMessage();
+ return flag;
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/MinecraftCrashAdvicer.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/MinecraftCrashAdvicer.java
new file mode 100644
index 000000000..9d7973821
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/MinecraftCrashAdvicer.java
@@ -0,0 +1,48 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.util.ConcurrentModificationException;
+import org.jackhuang.hellominecraft.C;
+
+/**
+ * Give the advice to solve the Minecraft crashing.
+ *
+ * @author hyh
+ */
+public final class MinecraftCrashAdvicer {
+
+ public static String getAdvice(Throwable t) {
+ return getAdvice(t, false);
+ }
+
+ public static String getAdvice(Throwable t, boolean selfCrash) {
+ if (t.getCause() instanceof UnsupportedClassVersionError) {
+ return C.i18n("crash.advice.UnsupportedClassVersionError");
+ } else if (t instanceof ConcurrentModificationException) {
+ return C.i18n("crash.advice.ConcurrentModificationException");
+ } else if (t instanceof SecurityException) {
+ return C.i18n("crash.advice.SecurityException");
+ } else if (t instanceof NoSuchFieldError || (t.getCause() != null && t.getCause() instanceof NoSuchFieldException)) {
+ return C.i18n("crash.advice.NoSuchFieldError");
+ } else if (t instanceof NoClassDefFoundError || (t.getCause() != null && t.getCause() instanceof ClassNotFoundException)) {
+ return C.i18n("crash.advice.ClassNotFondException");
+ }
+
+ if (t.getMessage() != null) {
+ if (t.getMessage().contains("OpenGL") || t.getMessage().contains("OpenAL")) {
+ return C.i18n("crash.advice.OpenGL");
+ }
+ }
+ if (t.getCause() != null && t.getCause().getMessage() != null) {
+ if (t.getCause().getMessage().contains("no lwjgl in java.library.path")) {
+ return C.i18n("crash.advice.no_lwjgl");
+ }
+ }
+ return selfCrash ? C.i18n("crash.advice.no") : C.i18n("crash.advice.otherwise");
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/MinecraftLoader.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/MinecraftLoader.java
new file mode 100644
index 000000000..8904379c8
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/launch/MinecraftLoader.java
@@ -0,0 +1,136 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.launch;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.utils.auth.UserProfileProvider;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.launcher.utils.MCUtils;
+import org.jackhuang.hellominecraft.launcher.utils.assets.AssetsIndex;
+import org.jackhuang.hellominecraft.launcher.utils.assets.AssetsObject;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.utils.OS;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftLibrary;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+
+/**
+ *
+ * @author hyh
+ */
+public class MinecraftLoader extends IMinecraftLoader {
+
+ private MinecraftVersion version;
+ String text;
+
+ public MinecraftLoader(Profile ver, IMinecraftProvider provider, UserProfileProvider lr, File minecraftJar) throws IllegalStateException {
+ this(ver, provider, lr, minecraftJar, DownloadType.Mojang);
+ }
+
+ public MinecraftLoader(Profile ver, IMinecraftProvider provider, UserProfileProvider lr, File minecraftJar, DownloadType downloadtype) throws IllegalStateException {
+ super(ver, provider, lr, minecraftJar);
+ version = ver.getSelectedMinecraftVersion().resolve(provider, downloadtype);
+ }
+
+ @Override
+ protected void makeSelf(List res) {
+ String library = v.isCanceledWrapper() ? "" : "-cp=";
+ for (MinecraftLibrary l : version.libraries) {
+ l.init();
+ if (l.allow())
+ library += l.getFilePath(gameDir).getAbsolutePath() + File.pathSeparator;
+ }
+ library += IOUtils.tryGetCanonicalFilePath(minecraftJar) + File.pathSeparator;
+ library = library.substring(0, library.length() - File.pathSeparator.length());
+ if (v.isCanceledWrapper()) res.add("-cp");
+ res.add(library);
+ String mainClass = version.mainClass;
+ res.add((v.isCanceledWrapper() ? "" : "-mainClass=") + mainClass);
+
+ String arg = v.getSelectedMinecraftVersion().minecraftArguments;
+ String[] splitted = org.jackhuang.hellominecraft.utils.StrUtils.tokenize(arg);
+
+ if (!new File(v.getGameDirFile(), "assets").exists())
+ MessageBox.Show(C.i18n("assets.no_assets"));
+
+ String game_assets = reconstructAssets().getAbsolutePath();
+
+ for (String t : splitted) {
+ t = t.replace("${auth_player_name}", lr.getUserName());
+ t = t.replace("${auth_session}", lr.getSession());
+ t = t.replace("${auth_uuid}", lr.getUserId());
+ t = t.replace("${version_name}", version.id);
+ t = t.replace("${profile_name}", provider.profile.getName());
+ t = t.replace("${game_directory}", provider.getRunDirectory(version.id).getAbsolutePath());
+ t = t.replace("${game_assets}", game_assets);
+ t = t.replace("${assets_root}", provider.getAssets().getAbsolutePath());
+ t = t.replace("${auth_access_token}", lr.getAccessToken());
+ t = t.replace("${user_type}", lr.getUserType());
+ t = t.replace("${assets_index_name}", version.assets == null ? "legacy" : version.assets);
+ t = t.replace("${user_properties}", lr.getUserProperties());
+ t = t.replace("${user_property_map}", lr.getUserPropertyMap());
+ res.add(t);
+ }
+
+ if (res.indexOf("--gameDir") != -1 && res.indexOf("--workDir") != -1) {
+ res.add("--workDir");
+ res.add(gameDir.getAbsolutePath());
+ }
+ }
+
+ @Override
+ protected void appendJVMArgs(List list) {
+ super.appendJVMArgs(list);
+
+ try {
+ if (OS.os() == OS.OSX) {
+ list.add("-Xdock:icon=" + MCUtils.getAssetObject(C.gson, v.getCanonicalGameDir(), version.assets, "icons/minecraft.icns").getAbsolutePath());
+ list.add("-Xdock:name=Minecraft");
+ }
+ } catch (IOException e) {
+ HMCLog.err("Failed to append jvm arguments when searching for asset objects.", e);
+ }
+ }
+
+ private File reconstructAssets() {
+ File assetsDir = new File(provider.getBaseFolder(), "assets");
+ File indexDir = new File(assetsDir, "indexes");
+ File objectDir = new File(assetsDir, "objects");
+ String assetVersion = version.assets == null ? "legacy" : version.assets;
+ File indexFile = new File(indexDir, assetVersion + ".json");
+ File virtualRoot = new File(new File(assetsDir, "virtual"), assetVersion);
+
+ if (!indexFile.isFile()) {
+ HMCLog.warn("No assets index file " + virtualRoot + "; can't reconstruct assets");
+ return virtualRoot;
+ }
+
+ try {
+ AssetsIndex index = (AssetsIndex) C.gson.fromJson(FileUtils.readFileToString(indexFile, "UTF-8"), AssetsIndex.class);
+
+ if (index.isVirtual()) {
+ HMCLog.log("Reconstructing virtual assets folder at " + virtualRoot);
+ for (Map.Entry entry : index.getFileMap().entrySet()) {
+ File target = new File(virtualRoot, (String) entry.getKey());
+ File original = new File(new File(objectDir, ((AssetsObject) entry.getValue()).getHash().substring(0, 2)), ((AssetsObject) entry.getValue()).getHash());
+
+ if (!target.isFile())
+ FileUtils.copyFile(original, target, false);
+ }
+ }
+ } catch (IOException e) {
+ HMCLog.warn("Failed to create virutal assets.", e);
+ }
+
+ return virtualRoot;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/CrashReport.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/CrashReport.java
new file mode 100644
index 000000000..52c5c4b90
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/CrashReport.java
@@ -0,0 +1,59 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils;
+
+import java.text.DateFormat;
+import java.util.Date;
+import javax.swing.SwingUtilities;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.Main;
+import org.jackhuang.hellominecraft.launcher.launch.MinecraftCrashAdvicer;
+import org.jackhuang.hellominecraft.utils.UpdateChecker;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.views.LogWindow;
+
+/**
+ *
+ * @author hyh
+ */
+public class CrashReport implements Thread.UncaughtExceptionHandler {
+
+ boolean enableLogger = false;
+
+ public CrashReport(boolean enableLogger) {
+ this.enableLogger = enableLogger;
+ }
+
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ try {
+ String text = "\n---- Hello Minecraft! Crash Report ----\n";
+ text += " Version: " + Main.makeVersion() + "\n";
+ text += " Time: " + DateFormat.getDateInstance().format(new Date()) + "\n";
+ text += " Thread: " + t.toString() + "\n";
+ text += "\n Advice: \n ";
+ text += MinecraftCrashAdvicer.getAdvice(e, true);
+ text += "\n Content: \n ";
+ text += StrUtils.getStackTrace(e) + "\n\n";
+ text += "-- System Details --\n";
+ text += " Operating System: " + System.getProperty("os.name") + " (" + System.getProperty("os.arch") + ") version " + System.getProperty("os.version") + "\n";
+ text += " Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor") + "\n";
+ text += " Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor") + "\n";
+ if (enableLogger) HMCLog.err(text);
+ else System.out.println(text);
+ SwingUtilities.invokeLater(() -> LogWindow.instance.showAsCrashWindow(UpdateChecker.OUT_DATED));
+ } catch (Throwable ex) {
+ try {
+ MessageBox.Show(e.getMessage() + "\n" + ex.getMessage(), "ERROR", MessageBox.ERROR_MESSAGE);
+ } catch (Throwable exx) {
+ System.out.println("Failed to catch exception thrown by " + t + " on " + Main.makeVersion() + ".");
+ exx.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/MCUtils.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/MCUtils.java
new file mode 100644
index 000000000..5a7c0dcf6
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/MCUtils.java
@@ -0,0 +1,306 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import java.io.File;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.utils.assets.AssetsIndex;
+import org.jackhuang.hellominecraft.launcher.utils.assets.AssetsObject;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+import org.jackhuang.hellominecraft.version.MinecraftRemoteVersions;
+import org.jackhuang.hellominecraft.tasks.TaskWindow;
+import org.jackhuang.hellominecraft.tasks.download.FileDownloadTask;
+import org.jackhuang.hellominecraft.utils.ArrayUtils;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.MinecraftVersionRequest;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+import org.jackhuang.hellominecraft.utils.OS;
+
+/**
+ *
+ * @author huang
+ */
+public final class MCUtils {
+
+ public static File getAssetObject(Gson gson, String dir, String assetVersion, String name) throws IOException {
+ File assetsDir = new File(dir, "assets");
+ File indexDir = new File(assetsDir, "indexes");
+ File objectsDir = new File(assetsDir, "objects");
+ File indexFile = new File(indexDir, assetVersion + ".json");
+ try {
+ AssetsIndex index = (AssetsIndex) gson.fromJson(FileUtils.readFileToString(indexFile, "UTF-8"), AssetsIndex.class);
+
+ String hash = ((AssetsObject) index.getFileMap().get(name)).getHash();
+ return new File(objectsDir, hash.substring(0, 2) + "/" + hash);
+ } catch(JsonSyntaxException e) {
+ throw new IOException("Assets file format malformed.", e);
+ }
+ }
+
+ private static int lessThan32(byte[] b, int x) {
+ for (; x < b.length; x++) {
+ if (b[x] < 32) {
+ return x;
+ }
+ }
+ return -1;
+ }
+
+ private static MinecraftVersionRequest getVersionOfOldMinecraft(ZipFile paramZipFile, ZipEntry paramZipEntry) throws IOException {
+ MinecraftVersionRequest r = new MinecraftVersionRequest();
+ byte[] tmp = NetUtils.getBytesFromStream(paramZipFile.getInputStream(paramZipEntry));
+
+ byte[] arrayOfByte = "Minecraft Minecraft ".getBytes("ASCII");
+ int j;
+ if ((j = ArrayUtils.matchArray(tmp, arrayOfByte)) < 0) {
+ r.type = MinecraftVersionRequest.Unkown;
+ return r;
+ }
+ int i = j + arrayOfByte.length;
+
+ if ((j = lessThan32(tmp, i)) < 0) {
+ r.type = MinecraftVersionRequest.Unkown;
+ return r;
+ }
+ String ver = new String(tmp, i, j - i, "ASCII");
+ r.version = ver;
+
+ if (paramZipFile.getEntry("META-INF/MANIFEST.MF") == null) {
+ r.type = MinecraftVersionRequest.Modified;
+ } else {
+ r.type = MinecraftVersionRequest.OK;
+ }
+ return r;
+ }
+
+ private static MinecraftVersionRequest getVersionOfNewMinecraft(ZipFile file, ZipEntry entry) throws IOException {
+ MinecraftVersionRequest r = new MinecraftVersionRequest();
+ byte[] tmp = NetUtils.getBytesFromStream(file.getInputStream(entry));
+
+ byte[] str = "-server.txt".getBytes("ASCII");
+ int j = ArrayUtils.matchArray(tmp, str);
+ if (j < 0) {
+ r.type = MinecraftVersionRequest.Unkown;
+ return r;
+ }
+ int i = j + str.length;
+ i += 11;
+ j = lessThan32(tmp, i);
+ if (j < 0) {
+ r.type = MinecraftVersionRequest.Unkown;
+ return r;
+ }
+ r.version = new String(tmp, i, j - i, "ASCII");
+
+ char ch = r.version.charAt(0);
+ // 1.8.1+
+ if(ch < '0' || ch > '9') {
+ str = "Can't keep up! Did the system time change, or is the server overloaded?".getBytes("ASCII");
+ j = ArrayUtils.matchArray(tmp, str);
+ if (j < 0) {
+ r.type = MinecraftVersionRequest.Unkown;
+ return r;
+ }
+ i = -1;
+ while (j > 0) {
+ if(tmp[j] >= 48 && tmp[j] <= 57) {
+ i = j;
+ break;
+ }
+ j--;
+ }
+ if (i == -1) {
+ r.type = MinecraftVersionRequest.Unkown;
+ return r;
+ }
+ int k = i;
+ while (tmp[k] >= 48 && tmp[k] <= 57 || tmp[k] == 46) k--;
+ k++;
+ r.version = new String(tmp, k, i - k + 1);
+ }
+
+ if (file.getEntry("META-INF/MANIFEST.MF") == null) {
+ r.type = MinecraftVersionRequest.Modified;
+ } else {
+ r.type = MinecraftVersionRequest.OK;
+ }
+ return r;
+ }
+
+ public static MinecraftVersionRequest minecraftVersion(File file) {
+ MinecraftVersionRequest r = new MinecraftVersionRequest();
+ if (!file.exists()) {
+ r.type = MinecraftVersionRequest.NotFound;
+ return r;
+ }
+ if (!file.isFile()) {
+ r.type = MinecraftVersionRequest.NotAFile;
+ return r;
+ }
+ if (!file.canRead()) {
+ r.type = MinecraftVersionRequest.NotReadable;
+ return r;
+ }
+ ZipFile localZipFile = null;
+ try {
+ localZipFile = new ZipFile(file);
+ ZipEntry minecraft = localZipFile
+ .getEntry("net/minecraft/client/Minecraft.class");
+ if (minecraft != null) {
+ return getVersionOfOldMinecraft(localZipFile, minecraft);
+ }
+ ZipEntry main = localZipFile.getEntry("net/minecraft/client/main/Main.class");
+ ZipEntry minecraftserver = localZipFile.getEntry("net/minecraft/server/MinecraftServer.class");
+ if ((main != null) && (minecraftserver != null)) {
+ return getVersionOfNewMinecraft(localZipFile, minecraftserver);
+ }
+ r.type = MinecraftVersionRequest.Invaild;
+ return r;
+ } catch (IOException localException) {
+ HMCLog.warn("Zip file is invalid", localException);
+ r.type = MinecraftVersionRequest.InvaildJar;
+ return r;
+ } finally {
+ if (localZipFile != null) {
+ try {
+ localZipFile.close();
+ } catch (IOException ex) {
+ HMCLog.warn("Failed to close zip file", ex);
+ }
+ }
+ }
+ }
+
+ public static File getLocation() {
+ String localObject = "minecraft";
+ String str1 = System.getProperty("user.home", ".");
+ File file;
+ OS os = OS.os();
+ if (os == OS.LINUX) {
+ file = new File(str1, '.' + (String) localObject + '/');
+ } else if (os == OS.WINDOWS) {
+ String str2;
+ if ((str2 = System.getenv("APPDATA")) != null) {
+ file = new File(str2, "." + (String) localObject + '/');
+ } else {
+ file = new File(str1, '.' + (String) localObject + '/');
+ }
+ } else if (os == OS.OSX) {
+ file = new File(str1, "Library/Application Support/" + localObject);
+ } else {
+ file = new File(str1, localObject + '/');
+ }
+ return file;
+ }
+
+ public static boolean is16Folder(String path) {
+ path = IOUtils.addSeparator(path);
+ return new File(path, "versions").exists();
+ }
+
+ public static String minecraft() {
+ String os = System.getProperty("os.name").trim().toLowerCase();
+ if (os.contains("mac")) {
+ return "minecraft";
+ }
+ return ".minecraft";
+ }
+
+ public static File getInitGameDir() {
+ File gameDir = IOUtils.currentDir();
+ if (gameDir.exists()) {
+ gameDir = new File(gameDir, MCUtils.minecraft());
+ if (!gameDir.exists()) {
+ File newFile = MCUtils.getLocation();
+ if(newFile.exists()) gameDir = newFile;
+ }
+ }
+ return gameDir;
+ }
+
+ public static MinecraftVersion downloadMinecraft(File gameDir, String id, DownloadType sourceType) {
+ String vurl = sourceType.getProvider().getVersionsDownloadURL() + id + "/";
+ File vpath = new File(gameDir, "versions/" + id);
+ File mvt = new File(vpath, id + ".json");
+ File mvj = new File(vpath, id + ".jar");
+ vpath.mkdirs();
+ mvt.delete();
+ mvj.delete();
+
+ if (TaskWindow.getInstance()
+ .addTask(new FileDownloadTask(vurl + id + ".json", IOUtils.tryGetCanonicalFile(mvt)).setTag(id + ".json"))
+ .addTask(new FileDownloadTask(vurl + id + ".jar", IOUtils.tryGetCanonicalFile(mvj)).setTag(id + ".jar"))
+ .start()) {
+ MinecraftVersion mv = new Gson().fromJson(FileUtils.readFileToStringQuietly(mvt), MinecraftVersion.class);
+ //File apath = new File(gameDir, "assets/indexes");
+ //downloadMinecraftAssetsIndex(apath, mv.assets, sourceType);
+ return mv;
+ }
+ return null;
+ }
+
+ public static boolean downloadMinecraftVersionJson(File gameDir, String id, DownloadType sourceType) {
+ String vurl = sourceType.getProvider().getVersionsDownloadURL() + id + "/";
+ File vpath = new File(gameDir, "versions/" + id);
+ File mvv = new File(vpath, id + ".json"), moved = null;
+ if (mvv.exists()) {
+ moved = new File(vpath, id + "-renamed.json");
+ mvv.renameTo(moved);
+ }
+ File mvt = new File(vpath, id + ".json");
+ vpath.mkdirs();
+ if (TaskWindow.getInstance()
+ .addTask(new FileDownloadTask(vurl + id + ".json", IOUtils.tryGetCanonicalFile(mvt)).setTag(id + ".json"))
+ .start()) {
+ if (moved != null) {
+ moved.delete();
+ }
+ return true;
+ } else {
+ mvt.delete();
+ if (moved != null) {
+ moved.renameTo(mvt);
+ }
+ return false;
+ }
+ }
+
+ public static boolean downloadMinecraftAssetsIndex(File assetsLocation, String assetsId, DownloadType sourceType) {
+ String aurl = sourceType.getProvider().getIndexesDownloadURL();
+
+ assetsLocation.mkdirs();
+ File assetsIndex = new File(assetsLocation, "indexes/" + assetsId + ".json");
+ File renamed = null;
+ if (assetsIndex.exists()) {
+ renamed = new File(assetsLocation, "indexes/" + assetsId + "-renamed.json");
+ assetsIndex.renameTo(renamed);
+ }
+ if (TaskWindow.getInstance()
+ .addTask(new FileDownloadTask(aurl + assetsId + ".json", IOUtils.tryGetCanonicalFile(assetsIndex)).setTag(assetsId + ".json"))
+ .start()) {
+ if (renamed != null) {
+ renamed.delete();
+ }
+ return true;
+ }
+ if (renamed != null) {
+ renamed.renameTo(assetsIndex);
+ }
+ return false;
+ }
+
+ public static MinecraftRemoteVersions getRemoteMinecraftVersions(DownloadType sourceType) throws IOException {
+ String result = NetUtils.doGet(sourceType.getProvider().getVersionsListDownloadURL());
+ return MinecraftRemoteVersions.fromJson(result);
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsIndex.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsIndex.java
new file mode 100644
index 000000000..249981e60
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsIndex.java
@@ -0,0 +1,38 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author hyh
+ */
+public class AssetsIndex {
+
+ public static final String DEFAULT_ASSET_NAME = "legacy";
+ private Map objects;
+ private boolean virtual;
+
+ public AssetsIndex() {
+ this.objects = new LinkedHashMap();
+ }
+
+ public Map getFileMap() {
+ return this.objects;
+ }
+
+ public Set getUniqueObjects() {
+ return new HashSet(this.objects.values());
+ }
+
+ public boolean isVirtual() {
+ return this.virtual;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsLoader.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsLoader.java
new file mode 100644
index 000000000..67d0ae0ee
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsLoader.java
@@ -0,0 +1,91 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.utils.EventHandler;
+import org.jackhuang.hellominecraft.utils.MathUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ *
+ * @author hyh
+ */
+public class AssetsLoader extends Thread {
+
+ Document doc;
+ Element root;
+ NodeList nodes;
+ public String uri;
+ ArrayList al;
+ public final EventHandler failedEvent = new EventHandler(this);
+ public final EventHandler> successEvent = new EventHandler>(this);
+
+ AssetsLoader(String uri) {
+ this.uri = uri;
+ }
+
+ private Contents modifyContents(NodeList contents) {
+ Contents ret = new Contents();
+ for (int i = 0; i < contents.getLength(); i++) {
+ Node result = contents.item(i);
+ if (result.getNodeType() == Node.ELEMENT_NODE) {
+ if (result.getNodeName().equalsIgnoreCase("Key")) {
+ ret.key = result.getTextContent();
+ }
+ if (result.getNodeName().equalsIgnoreCase("ETag")) {
+ ret.eTag = result.getTextContent();
+ }
+ if (result.getNodeName().equalsIgnoreCase("LastModified")) {
+ ret.lastModified = result.getTextContent();
+ }
+ if (result.getNodeName().equalsIgnoreCase("Size")) {
+ ret.size = MathUtils.parseInt(result.getTextContent(), 0);
+ }
+ if (result.getNodeName().equalsIgnoreCase("StorageClass")) {
+ ret.storageClass = result.getTextContent();
+ }
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName("AssetsLoader");
+ al = new ArrayList();
+ try {
+ HMCLog.log("AssetsLoader - Download begin.");
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = factory.newDocumentBuilder();
+ doc = db.parse(uri);
+ HMCLog.log("AssetsLoader - Download end and format begin.");
+ root = doc.getDocumentElement();
+ nodes = root.getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node result = nodes.item(i);
+ if (result.getNodeType() == Node.ELEMENT_NODE && result.getNodeName().equals("Contents")) {
+ Contents c = modifyContents(result.getChildNodes());
+ if(c.key != null)
+ al.add(c);
+ }
+ }
+ HMCLog.log("AssetsLoader - Format end.");
+
+ successEvent.execute(al);
+ } catch (Exception e) {
+ HMCLog.warn("AssetsLoader - Failed", e);
+ failedEvent.execute(e);
+ }
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsLoaderListener.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsLoaderListener.java
new file mode 100644
index 000000000..7f3877382
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsLoaderListener.java
@@ -0,0 +1,18 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+import java.util.ArrayList;
+
+/**
+ *
+ * @author hyh
+ */
+public interface AssetsLoaderListener {
+
+ void OnDone(ArrayList loader);
+
+ void OnFailed(Exception e);
+}
\ No newline at end of file
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsMojangLoader.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsMojangLoader.java
new file mode 100644
index 000000000..f2279826f
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsMojangLoader.java
@@ -0,0 +1,99 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.launcher.utils.MCUtils;
+import org.jackhuang.hellominecraft.launcher.utils.download.IDownloadProvider;
+import org.jackhuang.hellominecraft.utils.functions.Consumer;
+import org.jackhuang.hellominecraft.utils.VersionNumber;
+
+/**
+ *
+ * @author hyh
+ */
+public class AssetsMojangLoader extends IAssetsHandler {
+
+ public AssetsMojangLoader(String name) {
+ super(name);
+ }
+
+ @Override
+ public void getList(final Consumer dl) {
+ if (mv == null) {
+ dl.accept(null);
+ return;
+ }
+ String assetsId = mv.assets == null ? "legacy" : mv.assets;
+ File assets = mp.getAssets();
+ HMCLog.log("Get index: " + assetsId);
+ File f = IOUtils.tryGetCanonicalFile(new File(assets, "indexes/" + assetsId + ".json"));
+ if (!f.exists() && !MCUtils.downloadMinecraftAssetsIndex(assets, assetsId, Settings.s().getDownloadSource())) {
+ dl.accept(null);
+ return;
+ }
+
+ String result;
+ try {
+ result = FileUtils.readFileToString(f);
+ } catch (IOException ex) {
+ HMCLog.warn("Failed to read index json: " + f, ex);
+ dl.accept(null);
+ return;
+ }
+ if (StrUtils.isBlank(result)) {
+ HMCLog.err("Index json is empty, please redownload it!");
+ dl.accept(null);
+ return;
+ }
+ AssetsIndex o;
+ try {
+ o = C.gson.fromJson(result, AssetsIndex.class);
+ } catch (Exception e) {
+ HMCLog.err("Failed to parse index json, please redownload it!", e);
+ dl.accept(null);
+ return;
+ }
+ assetsDownloadURLs = new ArrayList<>();
+ assetsLocalNames = new ArrayList<>();
+ ArrayList al = new ArrayList<>();
+ contents = new ArrayList<>();
+ if (o != null && o.getFileMap() != null)
+ for (Map.Entry e : o.getFileMap().entrySet()) {
+ Contents c = new Contents();
+ c.key = e.getValue().getHash().substring(0, 2) + "/" + e.getValue().getHash();
+ c.eTag = e.getValue().getHash();
+ c.size = e.getValue().getSize();
+ contents.add(c);
+ assetsDownloadURLs.add(c.key);
+ assetsLocalNames.add(new File(assets, "objects" + File.separator + c.key.replace("/", File.separator)));
+ al.add(e.getKey());
+ }
+
+ dl.accept(al.toArray(new String[1]));
+ }
+
+ @Override
+ public Task getDownloadTask(IDownloadProvider sourceType) {
+ return new AssetsTask(sourceType.getAssetsDownloadURL());
+ }
+
+ @Override
+ public boolean isVersionAllowed(String formattedVersion) {
+ VersionNumber ur = VersionNumber.check(formattedVersion);
+ if (ur == null) return false;
+ return VersionNumber.check("1.6.0").compareTo(ur) <= 0;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsMojangOldLoader.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsMojangOldLoader.java
new file mode 100644
index 000000000..a14651c3e
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsMojangOldLoader.java
@@ -0,0 +1,61 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+import java.io.File;
+import java.util.ArrayList;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.utils.download.IDownloadProvider;
+import org.jackhuang.hellominecraft.utils.VersionNumber;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.utils.functions.Consumer;
+
+/**
+ *
+ * @author hyh
+ */
+public class AssetsMojangOldLoader extends IAssetsHandler {
+
+ private static final String URL = "http://bmclapi.bangbang93.com/resources/";
+
+ public AssetsMojangOldLoader(String name) {
+ super(name);
+ }
+
+ @Override
+ public void getList(final Consumer dl) {
+ AssetsLoader al = new AssetsLoader(URL);
+ al.failedEvent.register((sender, e) -> {
+ HMCLog.warn("Failed to get assets list.", e);
+ dl.accept(null);
+ return true;
+ });
+ al.successEvent.register((sender, t) -> {
+ assetsDownloadURLs = new ArrayList<>();
+ assetsLocalNames = new ArrayList<>();
+ contents = t;
+ for (Contents c : t) {
+ assetsDownloadURLs.add(c.key);
+ assetsLocalNames.add(new File(mp.getAssets(), c.key.replace("/", File.separator)));
+ }
+ dl.accept(assetsDownloadURLs.toArray(new String[1]));
+ return true;
+ });
+ new Thread(al).start();
+ }
+
+ @Override
+ public boolean isVersionAllowed(String formattedVersion) {
+ VersionNumber r = VersionNumber.check(formattedVersion);
+ if(r == null) return false;
+ return VersionNumber.check("1.7.2").compareTo(r) >= 0 &&
+ VersionNumber.check("1.6.0").compareTo(r) <= 0;
+ }
+
+ @Override
+ public Task getDownloadTask(IDownloadProvider sourceType) {
+ return new AssetsTask(URL);
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsObject.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsObject.java
new file mode 100644
index 000000000..421e4d1ab
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/AssetsObject.java
@@ -0,0 +1,42 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+/**
+ *
+ * @author hyh
+ */
+public class AssetsObject {
+ private String hash;
+ private long size;
+
+ public AssetsObject() {
+ }
+
+ public String getHash() {
+ return this.hash;
+ }
+
+ public long getSize() {
+ return this.size;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AssetsObject that = (AssetsObject) o;
+ if (this.size != that.size) return false;
+ return this.hash.equals(that.hash);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = this.hash.hashCode();
+ result = 31 * result + (int) (this.size ^ this.size >>> 32);
+ return result;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/Contents.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/Contents.java
new file mode 100644
index 000000000..51384eebc
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/Contents.java
@@ -0,0 +1,16 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+/**
+ *
+ * @author hyh
+ */
+public class Contents {
+
+ public String key, eTag, lastModified, storageClass;
+ public long size;
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/IAssetsHandler.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/IAssetsHandler.java
new file mode 100644
index 000000000..64e95dff3
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/assets/IAssetsHandler.java
@@ -0,0 +1,152 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.assets;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.launch.IMinecraftProvider;
+import org.jackhuang.hellominecraft.launcher.utils.download.IDownloadProvider;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.tasks.download.FileDownloadTask;
+import org.jackhuang.hellominecraft.utils.functions.Consumer;
+import org.jackhuang.hellominecraft.utils.DigestUtils;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+
+/**
+ * Assets
+ *
+ * @author hyh
+ */
+public abstract class IAssetsHandler {
+
+ protected ArrayList assetsDownloadURLs;
+ protected ArrayList assetsLocalNames;
+ protected final String name;
+ protected List contents;
+
+ public IAssetsHandler(String name) {
+ this.name = name;
+ }
+
+ private static final List assetsHandlers = new ArrayList<>();
+
+ public static IAssetsHandler getAssetsHandler(int i) {
+ return assetsHandlers.get(i);
+ }
+
+ public static List getAssetsHandlers() {
+ return assetsHandlers;
+ }
+
+ static {
+ assetsHandlers.add(new AssetsMojangLoader(C.i18n("assets.list.1_7_3_after")));
+ //assetsHandlers.add(new AssetsMojangOldLoader(C.i18n("assets.list.1_6")));
+ }
+
+ /**
+ * interface name
+ *
+ * @return
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * All the files assets needed
+ *
+ * @param x finished event
+ */
+ public abstract void getList(Consumer x);
+
+ /**
+ * Will be invoked when the user invoked "Download all assets".
+ *
+ * @param sourceType Download Source
+ * @return Download File Task
+ */
+ public abstract Task getDownloadTask(IDownloadProvider sourceType);
+
+ /**
+ * assets path
+ */
+ protected MinecraftVersion mv;
+ protected IMinecraftProvider mp;
+
+ /**
+ * @param mp
+ * @param mv
+ */
+ public void setAssets(IMinecraftProvider mp, MinecraftVersion mv) {
+ this.mp = mp;
+ this.mv = mv;
+ }
+
+ public abstract boolean isVersionAllowed(String formattedVersion);
+
+ protected class AssetsTask extends Task {
+
+ ArrayList al;
+ String u;
+ int progress, max;
+
+ public AssetsTask(String url) {
+ this.u = url;
+ }
+
+ @Override
+ public boolean executeTask() {
+ if (mv == null || assetsDownloadURLs == null) {
+ setFailReason(new RuntimeException(C.i18n("assets.not_refreshed")));
+ return false;
+ }
+ progress = 0;
+ max = assetsDownloadURLs.size();
+ al = new ArrayList<>();
+ for (int i = 0; i < max; i++) {
+ String mark = assetsDownloadURLs.get(i);
+ String url = u + mark;
+ File location = assetsLocalNames.get(i);
+ if (!location.getParentFile().exists()) location.getParentFile().mkdirs();
+ if (location.isDirectory()) continue;
+ boolean need = true;
+ try {
+ if (location.exists()) {
+ FileInputStream fis = new FileInputStream(location);
+ String sha = DigestUtils.sha1Hex(NetUtils.getBytesFromStream(fis));
+ IOUtils.closeQuietly(fis);
+ if (contents.get(i).eTag.equals(sha)) {
+ HMCLog.log("File " + assetsLocalNames.get(i) + " has downloaded successfully, skipped downloading.");
+ continue;
+ }
+ }
+ } catch (IOException e) {
+ HMCLog.warn("Failed to get hash: " + location, e);
+ need = !location.exists();
+ }
+ if (need) al.add(new FileDownloadTask(url, location).setTag(mark));
+ }
+ return true;
+ }
+
+ @Override
+ public Collection getAfterTasks() {
+ return al;
+ }
+
+ @Override
+ public String getInfo() {
+ return C.i18n("assets.download");
+ }
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/BestLogin.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/BestLogin.java
new file mode 100644
index 000000000..1a0bed932
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/BestLogin.java
@@ -0,0 +1,95 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.auth;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import org.jackhuang.hellominecraft.utils.DigestUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public final class BestLogin extends IAuthenticator {
+
+ public BestLogin(String clientToken) {
+ super(clientToken);
+ }
+
+ @Override
+ public UserProfileProvider login(LoginInfo info) {
+ try {
+ String request = "bl:l:" + info.username + ":" + DigestUtils.sha1Hex(info.password);
+
+ Socket socket = new Socket("auth.zhh0000zhh.com", 8);
+ OutputStream os = socket.getOutputStream();
+ os.write(request.length());
+ os.write(request.getBytes());
+
+ UserProfileProvider lr = new UserProfileProvider();
+ lr.setSuccess(false);
+
+ InputStream is = socket.getInputStream();
+ int code = is.read();
+ switch (code) {
+ case -1:
+ lr.setErrorReason("internet error.");
+ break;
+ case 200:
+ lr.setErrorReason("server restarting.");
+ break;
+ case 255:
+ lr.setErrorReason("unknown error");
+ break;
+ case 3:
+ lr.setErrorReason("unregistered.");
+ break;
+ case 50:
+ lr.setErrorReason("please update your launcher and act your account.");
+ break;
+ case 2:
+ lr.setErrorReason("wrong password.");
+ break;
+ case 100:
+ lr.setErrorReason("server reloading.");
+ break;
+ case 0:
+ lr.setSuccess(true);
+ byte[] b = new byte[64];
+ is.read(b, 0, b.length);
+ String[] ss = new String(b).split(":");
+ lr.setUserName(info.username);
+ lr.setUserId(ss[1]);
+ lr.setSession(ss[0]);
+ lr.setAccessToken(ss[0]);
+ break;
+ }
+ lr.setUserType("Legacy");
+ return lr;
+ } catch (Throwable t) {
+ UserProfileProvider lr = new UserProfileProvider();
+ lr.setSuccess(false);
+ lr.setErrorReason(t.getMessage());
+ return lr;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "BestLogin";
+ }
+
+ @Override
+ public UserProfileProvider loginBySettings() {
+ return null;
+ }
+
+ @Override
+ public void logout() {
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/IAuthenticator.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/IAuthenticator.java
new file mode 100644
index 000000000..852005385
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/IAuthenticator.java
@@ -0,0 +1,83 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.auth;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+
+/**
+ * Login interface
+ *
+ * @author hyh
+ */
+public abstract class IAuthenticator {
+
+ public static final YggdrasilAuthenticator yggdrasilLogin;
+ public static final OfflineAuthenticator offlineLogin;
+ public static final SkinmeAuthenticator skinmeLogin;
+ //public static final BestLogin bestLogin;
+
+ public static final List logins;
+
+ static {
+ String clientToken = Settings.s().getClientToken();
+ logins = new ArrayList<>();
+ logins.add(offlineLogin = new OfflineAuthenticator(clientToken));
+ logins.add(yggdrasilLogin = new YggdrasilAuthenticator(clientToken));
+ logins.add(skinmeLogin = new SkinmeAuthenticator(clientToken));
+ //logins.add(bestLogin = new BestLogin(clientToken));
+ yggdrasilLogin.onLoadSettings(Settings.s().getYggdrasilConfig());
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+
+ @Override
+ public void run() {
+ Settings.s().setYggdrasilConfig(yggdrasilLogin.onSaveSettings());
+ }
+ });
+ }
+
+ protected String clientToken;
+
+ public IAuthenticator(String clientToken) {
+ this.clientToken = clientToken;
+ }
+
+ /**
+ * Login Method
+ *
+ * @param info username & password
+ * @return login result
+ */
+ public abstract UserProfileProvider login(LoginInfo info);
+
+ /**
+ *
+ * @return the name of login method.
+ */
+ public abstract String getName();
+
+ /**
+ * Has password?
+ *
+ * @return Will I hide password box?
+ */
+ public boolean isHidePasswordBox() {
+ return false;
+ }
+
+ public boolean isLoggedIn() {
+ return false;
+ }
+
+ public void setRememberMe(boolean is) {
+
+ }
+
+ public abstract UserProfileProvider loginBySettings();
+
+ public abstract void logout();
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/LoginInfo.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/LoginInfo.java
new file mode 100644
index 000000000..ff1537882
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/LoginInfo.java
@@ -0,0 +1,18 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.auth;
+
+/**
+ *
+ * @author hyh
+ */
+public final class LoginInfo {
+ public String username, password;
+
+ public LoginInfo(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/OfflineAuthenticator.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/OfflineAuthenticator.java
new file mode 100644
index 000000000..15b025651
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/OfflineAuthenticator.java
@@ -0,0 +1,55 @@
+package org.jackhuang.hellominecraft.launcher.utils.auth;
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.DigestUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public final class OfflineAuthenticator extends IAuthenticator {
+
+ public OfflineAuthenticator(String clientToken) {
+ super(clientToken);
+ }
+
+ @Override
+ public UserProfileProvider login(LoginInfo info) {
+ UserProfileProvider result = new UserProfileProvider();
+ result.setSuccess(StrUtils.isNotBlank(info.username));
+ result.setUserName(info.username);
+ String md5 = DigestUtils.md5Hex(info.username);
+ String uuid = md5.substring(0, 8) + '-' + md5.substring(8, 12) + '-' + md5.substring(12, 16) + '-' + md5.substring(16, 21) + md5.substring(21);
+ result.setSession(uuid);
+ result.setUserId(uuid);
+ result.setAccessToken("${auth_access_token}");
+ result.setUserType("Legacy");
+ result.setErrorReason(C.i18n("login.no_Player007"));
+ return result;
+ }
+
+ @Override
+ public String getName() {
+ return C.i18n("login.methods.offline");
+ }
+
+ @Override
+ public boolean isHidePasswordBox() {
+ return true;
+ }
+
+ @Override
+ public UserProfileProvider loginBySettings() {
+ return null;
+ }
+
+ @Override
+ public void logout() {
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/SkinmeAuthenticator.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/SkinmeAuthenticator.java
new file mode 100644
index 000000000..e8e9443de
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/SkinmeAuthenticator.java
@@ -0,0 +1,125 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.auth;
+
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.utils.DigestUtils;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+import org.jackhuang.hellominecraft.views.Selector;
+
+/**
+ *
+ * @author hyh
+ */
+public final class SkinmeAuthenticator extends IAuthenticator {
+
+ public SkinmeAuthenticator(String clientToken) {
+ super(clientToken);
+ }
+
+ public String[] parseType1(String s) {
+ return s.split(",");
+ }
+
+ public String getCharacter(String user, String hash, String $char) throws Exception {
+ if ($char == null)
+ return NetUtils.doGet(
+ "http://www.skinme.cc/api/login.php?user=" + user + "&hash=" + hash);
+ else
+ return NetUtils.doGet(
+ "http://www.skinme.cc/api/login.php?user=" + user + "&hash=" + hash + "&char=" + $char);
+ }
+
+ @Override
+ public UserProfileProvider login(LoginInfo info) {
+ UserProfileProvider req = new UserProfileProvider();
+ if (info.username == null || !info.username.contains("@")) {
+ req.setSuccess(false);
+ req.setErrorReason(C.i18n("login.not_email"));
+ return req;
+ }
+ try {
+ String usr = info.username.toLowerCase();
+ String pwd = info.password;
+
+ String str = DigestUtils.sha1Hex(usr);
+ String hashCode = DigestUtils.sha1Hex(DigestUtils.md5Hex(DigestUtils.sha1Hex(pwd) + pwd) + str);
+ String data = getCharacter(usr, hashCode, null);
+ String[] sl = data.split(":");
+ if (null != sl[0]) switch (sl[0]) {
+ case "0":
+ req.setSuccess(false);
+ req.setErrorReason(sl[1]);
+ break;
+ case "1": {
+ req.setSuccess(true);
+ String[] s = parseType1(sl[1]);
+ req.setUserName(s[0]);
+ req.setSession(s[1]);
+ req.setUserId(s[1]);
+ req.setAccessToken(s[1]);
+ break;
+ }
+ case "2": {
+ req.setSuccess(true);
+ String[] charators = sl[1].split(";");
+ int len = charators.length;
+ String[] $char = new String[len];
+ String[] user = new String[len];
+ System.out.println(sl[1]);
+ for (int i = 0; i < len; i++) {
+ String[] charator = charators[i].split(",");
+ $char[i] = charator[0];
+ user[i] = charator[1];
+ }
+ Selector s = new Selector(null, user, C.i18n("login.choose_charactor"));
+ s.setVisible(true);
+ if (s.sel == Selector.failedToSel) {
+ req.setSuccess(false);
+ req.setErrorReason(C.i18n("message.cancelled"));
+ } else {
+ int index = s.sel;
+ String character = $char[index];
+ sl = getCharacter(usr, hashCode, character).split(":");
+ String[] s2 = parseType1(sl[1]);
+ req.setUserName(s2[0]);
+ req.setSession(s2[1]);
+ req.setUserId(s2[1]);
+ req.setAccessToken(s2[1]);
+ }
+ break;
+ }
+ }
+
+ req.setUserType("Legacy");
+ return req;
+ } catch (Exception e) {
+ HMCLog.warn("Failed to login skinme.", e);
+
+ req.setUserName(info.username);
+ req.setSuccess(false);
+ req.setUserType("Legacy");
+ req.setErrorReason(e.getMessage());
+ return req;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "Skinme";
+ }
+
+ @Override
+ public UserProfileProvider loginBySettings() {
+ return null;
+ }
+
+ @Override
+ public void logout() {
+
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/UserProfileProvider.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/UserProfileProvider.java
new file mode 100644
index 000000000..9e91b2ea0
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/UserProfileProvider.java
@@ -0,0 +1,112 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.auth;
+
+/**
+ *
+ * @author hyh
+ */
+public final class UserProfileProvider {
+
+ public String getUserName() {
+ return username;
+ }
+
+ public void setUserName(String username) {
+ this.username = username;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getSession() {
+ return session;
+ }
+
+ public void setSession(String session) {
+ this.session = session;
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ public boolean isSuccessful() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getErrorReason() {
+ return error;
+ }
+
+ public void setErrorReason(String error) {
+ this.error = error;
+ }
+
+ public String getUserProperties() {
+ return userProperties;
+ }
+
+ public void setUserProperties(String userProperties) {
+ this.userProperties = userProperties;
+ }
+
+ public String getUserPropertyMap() {
+ return userPropertyMap;
+ }
+
+ public void setUserPropertyMap(String userPropertyMap) {
+ this.userPropertyMap = userPropertyMap;
+ }
+
+ public String getOtherInfo() {
+ return otherInfo;
+ }
+
+ public void setOtherInfo(String otherInfo) {
+ this.otherInfo = otherInfo;
+ }
+
+ public String getClientIdentifier() {
+ return clientIdentifier;
+ }
+
+ public void setClientIdentifier(String clientIdentifier) {
+ this.clientIdentifier = clientIdentifier;
+ }
+
+ public String getUserType() {
+ return userType;
+ }
+
+ public void setUserType(String userType) {
+ this.userType = userType;
+ }
+
+ private String username = "";
+ private String userId = "";
+ private String session = "";
+ private String accessToken = "";
+ private boolean success = false;
+ private String error = "";
+ private String userProperties = "{}";
+ private String userPropertyMap = "{}";
+ private String otherInfo = "";
+ private String clientIdentifier = "";
+ private String userType = "Offline";
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/YggdrasilAuthenticator.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/YggdrasilAuthenticator.java
new file mode 100644
index 000000000..7057caae4
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/auth/YggdrasilAuthenticator.java
@@ -0,0 +1,145 @@
+package org.jackhuang.hellominecraft.launcher.utils.auth;
+
+import com.google.gson.GsonBuilder;
+import java.net.Proxy;
+import java.util.Map;
+import javax.swing.JOptionPane;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.utils.ArrayUtils;
+import org.jackhuang.hellominecraft.views.Selector;
+import org.jackhuang.mojang.authlib.Agent;
+import org.jackhuang.mojang.authlib.GameProfile;
+import org.jackhuang.mojang.authlib.UserType;
+import org.jackhuang.mojang.authlib.properties.PropertyMap;
+import org.jackhuang.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
+import org.jackhuang.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
+import org.jackhuang.mojang.util.LegacyPropertyMapSerializer;
+import org.jackhuang.mojang.util.UUIDTypeAdapter;
+
+/**
+ *
+ * @author hyh
+ */
+public final class YggdrasilAuthenticator extends IAuthenticator {
+
+ YggdrasilAuthenticationService service;
+ YggdrasilUserAuthentication ua;
+
+ public YggdrasilAuthenticator(String clientToken) {
+ super(clientToken);
+ service = new YggdrasilAuthenticationService(Proxy.NO_PROXY, clientToken);
+ ua = (YggdrasilUserAuthentication)service.createUserAuthentication(Agent.MINECRAFT);
+ }
+
+ @Override
+ public UserProfileProvider login(LoginInfo info) {
+ if(ua.canPlayOnline()) {
+ UserProfileProvider result = new UserProfileProvider();
+ result.setUserName(info.username);
+ result.setSuccess(true);
+ result.setUserId(UUIDTypeAdapter.fromUUID(ua.getSelectedProfile().getId()));
+ result.setUserProperties(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new LegacyPropertyMapSerializer()).create().toJson(ua.getUserProperties()));
+ result.setUserPropertyMap(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create().toJson(ua.getUserProperties()));
+ result.setAccessToken(ua.getAuthenticatedToken());
+ result.setSession(ua.getAuthenticatedToken());
+ result.setUserType(ua.getUserType().getName());
+ return result;
+ }
+ UserProfileProvider result = new UserProfileProvider();
+ String usr = info.username;
+ if(info.username == null || !info.username.contains("@")) {
+ result.setSuccess(false);
+ result.setErrorReason(C.i18n("login.not_email"));
+ return result;
+ }
+ String pwd = info.password;
+
+ if(!ua.isLoggedIn())
+ ua.setPassword(pwd);
+ ua.setUsername(usr);
+ try {
+ ua.logIn();
+ if(!ua.isLoggedIn()) throw new Exception(C.i18n("login.wrong_password"));
+ GameProfile selectedProfile = ua.getSelectedProfile();
+ GameProfile[] profiles = ua.getAvailableProfiles();
+ String[] names;
+ String username;
+ if(selectedProfile == null) {
+ if(ArrayUtils.isNotEmpty(profiles)) {
+ names = new String[profiles.length];
+ for(int i = 0; i < profiles.length; i++) {
+ names[i] = profiles[i].getName();
+ }
+ Selector s = new Selector(null, names, C.i18n("login.choose_charactor"));
+ s.setVisible(true);
+ selectedProfile = profiles[s.sel];
+ username = names[s.sel];
+ } else {
+ username = JOptionPane.showInputDialog(C.i18n("login.no_charactor"));
+ }
+ } else {
+ username = selectedProfile.getName();
+ }
+ result.setUserName(username);
+ result.setSuccess(true);
+ result.setUserId(UUIDTypeAdapter.fromUUID(selectedProfile.getId()));
+ result.setUserProperties(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new LegacyPropertyMapSerializer()).create().toJson(ua.getUserProperties()));
+ result.setUserPropertyMap(new GsonBuilder().registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create().toJson(ua.getUserProperties()));
+ result.setAccessToken(ua.getAuthenticatedToken());
+ result.setSession(ua.getAuthenticatedToken());
+ result.setUserType(ua.getUserType().getName());
+ } catch (Exception ex) {
+ result.setErrorReason(ex.getMessage());
+ result.setSuccess(false);
+ result.setUserName(ua.getUserID());
+ result.setUserType(UserType.MOJANG.getName());
+
+ HMCLog.err("Failed to login by yggdrasil authentication.", ex);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ return ua.isLoggedIn();
+ }
+
+ @Override
+ public String getName() {
+ return C.i18n("login.methods.yggdrasil");
+ }
+
+ public Map onSaveSettings() {
+ return ua.saveForStorage();
+ }
+
+ public void onLoadSettings(Map settings) {
+ if(settings == null) return;
+ ua.loadFromStorage(settings);
+ }
+
+ @Override
+ public UserProfileProvider loginBySettings() {
+ UserProfileProvider info = new UserProfileProvider();
+ try {
+ ua.logIn();
+ if(!ua.isLoggedIn()) throw new Exception(C.i18n("WrongPassword"));
+ GameProfile profile = ua.getSelectedProfile();
+ info.setUserName(profile.getName());
+ info.setSuccess(true);
+ info.setUserId(profile.getId().toString());
+ info.setAccessToken(ua.getAuthenticatedToken());
+ } catch (Exception ex) {
+ info.setErrorReason(ex.getMessage());
+ info.setSuccess(false);
+ info.setUserName(ua.getUserID());
+ }
+ return info;
+ }
+
+ @Override
+ public void logout() {
+ ua.logOut();
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/BMCLAPIDownloadProvider.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/BMCLAPIDownloadProvider.java
new file mode 100644
index 000000000..be7fafdc8
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/BMCLAPIDownloadProvider.java
@@ -0,0 +1,70 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.download;
+
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class BMCLAPIDownloadProvider implements IDownloadProvider {
+
+ @Override
+ public InstallerVersionList getForgeInstaller() {
+ return org.jackhuang.hellominecraft.launcher.utils.installers.forge.bmcl.ForgeBMCLVersionList.getInstance();
+ }
+
+ @Override
+ public InstallerVersionList getLiteLoaderInstaller() {
+ return org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderVersionList.getInstance();
+ }
+
+ @Override
+ public InstallerVersionList getOptiFineInstaller() {
+ return org.jackhuang.hellominecraft.launcher.utils.installers.optifine.bmcl.OptiFineBMCLVersionList.getInstance();
+ }
+
+ @Override
+ public InstallerVersionList getInstallerByType(String type) {
+ if(type.equalsIgnoreCase("forge")) return getForgeInstaller();
+ if(type.equalsIgnoreCase("liteloader")) return getLiteLoaderInstaller();
+ if(type.equalsIgnoreCase("optifine")) return getOptiFineInstaller();
+ return null;
+ }
+
+ @Override
+ public String getLibraryDownloadURL() {
+ return "http://bmclapi2.bangbang93.com/libraries";
+ }
+
+ @Override
+ public String getVersionsDownloadURL() {
+ return "http://bmclapi2.bangbang93.com/versions/";
+ }
+
+ @Override
+ public String getIndexesDownloadURL() {
+ return "http://bmclapi2.bangbang93.com/indexes/";
+ }
+
+ @Override
+ public String getVersionsListDownloadURL() {
+ return "http://bmclapi2.bangbang93.com/versions/versions.json";
+ }
+
+ @Override
+ public String getAssetsDownloadURL() {
+ return "http://bmclapi2.bangbang93.com/assets/";
+ }
+
+ @Override
+ public boolean isAllowedToUseSelfURL() {
+ return false;
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/DownloadType.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/DownloadType.java
new file mode 100644
index 000000000..81a0a529a
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/DownloadType.java
@@ -0,0 +1,33 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.download;
+
+import org.jackhuang.hellominecraft.C;
+
+/**
+ *
+ * @author hyh
+ */
+public enum DownloadType {
+ Mojang(C.i18n("download.mojang"), new MojangDownloadProvider()),
+ BMCL(C.i18n("download.BMCL"), new BMCLAPIDownloadProvider());
+
+ private final String name;
+ private final IDownloadProvider provider;
+
+ DownloadType(String a, IDownloadProvider provider) {
+ name = a;
+ this.provider = provider;
+ }
+
+ public IDownloadProvider getProvider() {
+ return provider;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/IDownloadProvider.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/IDownloadProvider.java
new file mode 100644
index 000000000..9f5015ccc
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/IDownloadProvider.java
@@ -0,0 +1,25 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.download;
+
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public interface IDownloadProvider {
+ InstallerVersionList getInstallerByType(String type);
+ InstallerVersionList getForgeInstaller();
+ InstallerVersionList getLiteLoaderInstaller();
+ InstallerVersionList getOptiFineInstaller();
+ String getLibraryDownloadURL();
+ String getVersionsDownloadURL();
+ String getIndexesDownloadURL();
+ String getVersionsListDownloadURL();
+ String getAssetsDownloadURL();
+ boolean isAllowedToUseSelfURL();
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/MojangDownloadProvider.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/MojangDownloadProvider.java
new file mode 100644
index 000000000..4c0c4b72f
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/download/MojangDownloadProvider.java
@@ -0,0 +1,70 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.download;
+
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class MojangDownloadProvider implements IDownloadProvider {
+
+ @Override
+ public InstallerVersionList getForgeInstaller() {
+ return org.jackhuang.hellominecraft.launcher.utils.installers.forge.vanilla.MinecraftForgeVersionList.getInstance();
+ }
+
+ @Override
+ public InstallerVersionList getLiteLoaderInstaller() {
+ return org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderVersionList.getInstance();
+ }
+
+ @Override
+ public InstallerVersionList getOptiFineInstaller() {
+ return org.jackhuang.hellominecraft.launcher.utils.installers.optifine.vanilla.OptiFineVersionList.getInstance();
+ }
+
+ @Override
+ public InstallerVersionList getInstallerByType(String type) {
+ if(type.equalsIgnoreCase("forge")) return getForgeInstaller();
+ if(type.equalsIgnoreCase("liteloader")) return getLiteLoaderInstaller();
+ if(type.equalsIgnoreCase("optifine")) return getOptiFineInstaller();
+ return null;
+ }
+
+ @Override
+ public String getLibraryDownloadURL() {
+ return "https://libraries.minecraft.net";
+ }
+
+ @Override
+ public String getVersionsDownloadURL() {
+ return "https://s3.amazonaws.com/Minecraft.Download/versions/";
+ }
+
+ @Override
+ public String getIndexesDownloadURL() {
+ return "https://s3.amazonaws.com/Minecraft.Download/indexes/";
+ }
+
+ @Override
+ public String getVersionsListDownloadURL() {
+ return "https://s3.amazonaws.com/Minecraft.Download/versions/versions.json";
+ }
+
+ @Override
+ public String getAssetsDownloadURL() {
+ return "http://resources.download.minecraft.net/";
+ }
+
+ @Override
+ public boolean isAllowedToUseSelfURL() {
+ return true;
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallProfile.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallProfile.java
new file mode 100644
index 000000000..125c118ba
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallProfile.java
@@ -0,0 +1,22 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.jackhuang.hellominecraft.launcher.utils.installers;
+
+import com.google.gson.annotations.SerializedName;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+import org.jackhuang.hellominecraft.launcher.utils.installers.forge.Install;
+
+/**
+ *
+ * @author hyh
+ */
+public class InstallProfile {
+ @SerializedName("install")
+ public Install install;
+ @SerializedName("versionInfo")
+ public MinecraftVersion versionInfo;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallerVersionList.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallerVersionList.java
new file mode 100644
index 000000000..0ecb7ac20
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallerVersionList.java
@@ -0,0 +1,56 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers;
+
+import java.util.Comparator;
+import java.util.List;
+import org.jackhuang.hellominecraft.utils.functions.Consumer;
+
+/**
+ *
+ * @author hyh
+ */
+public abstract class InstallerVersionList implements Consumer {
+ /**
+ * Refresh installer versions list from the downloaded content.
+ */
+ public abstract void refreshList(String[] versions) throws Exception;
+ public abstract String getName();
+ public abstract List getVersions(String mcVersion);
+
+ public static class InstallerVersion implements Comparable {
+ public String selfVersion, mcVersion;
+ public String installer, universal;
+ public String changelog;
+
+ public InstallerVersion(String selfVersion, String mcVersion) {
+ this.selfVersion = selfVersion;
+ this.mcVersion = mcVersion;
+ }
+
+ @Override
+ public int compareTo(InstallerVersion o) {
+ return selfVersion.compareTo(o.selfVersion);
+ }
+ }
+
+ public static class InstallerVersionComparator implements Comparator {
+ public static final InstallerVersionComparator INSTANCE = new InstallerVersionComparator();
+ @Override
+ public int compare(InstallerVersion o1, InstallerVersion o2) {
+ return o2.compareTo(o1);
+ }
+ }
+
+ @Override
+ public void accept(String[] v) {
+ try {
+ refreshList(v);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallerVersionNewerComparator.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallerVersionNewerComparator.java
new file mode 100644
index 000000000..45e5ba6f8
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/InstallerVersionNewerComparator.java
@@ -0,0 +1,21 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers;
+
+import java.util.Comparator;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList.InstallerVersion;
+
+/**
+ *
+ * @author hyh
+ */
+public class InstallerVersionNewerComparator implements Comparator {
+
+ @Override
+ public int compare(InstallerVersion o1, InstallerVersion o2) {
+ return -o1.compareTo(o2);
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/PackMinecraftInstaller.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/PackMinecraftInstaller.java
new file mode 100644
index 000000000..31c33d684
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/PackMinecraftInstaller.java
@@ -0,0 +1,35 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import org.jackhuang.hellominecraft.utils.Compressor;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class PackMinecraftInstaller {
+
+ File dest;
+ ArrayList src;
+
+ public PackMinecraftInstaller(ArrayList src, File dest) {
+ this.dest = dest;
+ this.src = src;
+ }
+
+ public void install() throws IOException {
+ File file = new File(IOUtils.currentDir(), "HMCL-MERGE-TEMP");
+ file.mkdirs();
+ for (String src1 : src) Compressor.unzip(new File(src1), file);
+ Compressor.zip(file, dest);
+ FileUtils.deleteDirectory(file);
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/ForgeInstaller.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/ForgeInstaller.java
new file mode 100644
index 000000000..35d587a21
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/ForgeInstaller.java
@@ -0,0 +1,103 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge;
+
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallProfile;
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.launch.IMinecraftProvider;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftLibrary;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+
+/**
+ *
+ * @author hyh
+ */
+public class ForgeInstaller extends Task {
+
+ private final Gson gson = new Gson();
+ public File gameDir;
+ public File forgeInstaller;
+ public IMinecraftProvider mp;
+
+ public ForgeInstaller(IMinecraftProvider mp, File forgeInstaller) {
+ this.gameDir = mp.getBaseFolder();
+ this.forgeInstaller = forgeInstaller;
+ this.mp = mp;
+ }
+
+ @Override
+ public boolean executeTask() {
+ try {
+ HMCLog.log("Extracting install profiles...");
+
+ ZipFile zipFile = new ZipFile(forgeInstaller);
+ ZipEntry entry = zipFile.getEntry("install_profile.json");
+ String content = NetUtils.getStreamContent(zipFile.getInputStream(entry));
+ InstallProfile profile = gson.fromJson(content, InstallProfile.class);
+
+ File from = new File(gameDir, "versions" + File.separator + profile.install.minecraft);
+ if(!from.exists()) {
+ if(MessageBox.Show(C.i18n("install.no_version_if_intall")) == MessageBox.YES_OPTION) {
+ if(!mp.install(profile.install.minecraft, Settings.s().getDownloadSource())) {
+ setFailReason(new RuntimeException(C.i18n("install.no_version")));
+ }
+ } else {
+ setFailReason(new RuntimeException(C.i18n("install.no_version")));
+ }
+ return false;
+ }
+
+ File to = new File(gameDir, "versions" + File.separator + profile.install.target);
+ to.mkdirs();
+
+ HMCLog.log("Copying jar..." + profile.install.minecraft + ".jar to " + profile.install.target + ".jar");
+ FileUtils.copyFile(new File(from, profile.install.minecraft + ".jar"),
+ new File(to, profile.install.target + ".jar"));
+ HMCLog.log("Creating new version profile..." + profile.install.target + ".json");
+ FileUtils.write(new File(to, profile.install.target + ".json"), gson.toJson(profile.versionInfo));
+
+ HMCLog.log("Extracting universal forge pack..." + profile.install.filePath);
+
+ entry = zipFile.getEntry(profile.install.filePath);
+ InputStream is = zipFile.getInputStream(entry);
+
+ MinecraftLibrary forge = new MinecraftLibrary(profile.install.path);
+ forge.init();
+ File file = new File(gameDir, "libraries/" + forge.formatted);
+ file.getParentFile().mkdirs();
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ BufferedOutputStream bos = new BufferedOutputStream(fos);
+ int c;
+ while ((c = is.read()) != -1) bos.write((byte) c);
+ bos.close();
+ }
+ return true;
+ } catch(IOException | JsonSyntaxException e) {
+ setFailReason(e);
+ return false;
+ }
+ }
+
+ @Override
+ public String getInfo() {
+ return C.i18n("install.forge.install");
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/ForgeOldInstaller.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/ForgeOldInstaller.java
new file mode 100644
index 000000000..a0017f1fc
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/ForgeOldInstaller.java
@@ -0,0 +1,25 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import org.jackhuang.hellominecraft.launcher.utils.installers.PackMinecraftInstaller;
+
+/**
+ *
+ * @author hyh
+ */
+public class ForgeOldInstaller {
+
+ public static void install(String destMinecraftJar, String srcMinecraftJar, String forgeUniversal) throws IOException {
+ ArrayList al = new ArrayList();
+ al.add(srcMinecraftJar);
+ al.add(forgeUniversal);
+ new PackMinecraftInstaller(al, new File(destMinecraftJar)).install();
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/Install.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/Install.java
new file mode 100644
index 000000000..82598700b
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/Install.java
@@ -0,0 +1,23 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge;
+
+/**
+ *
+ * @author hyh
+ */
+public class Install {
+ public String profileName;
+ public String target;
+ public String path;
+ public String version;
+ public String filePath;
+ public String welcome;
+ public String minecraft;
+ public String mirrorList;
+ public String logo;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/Downloads.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/Downloads.java
new file mode 100644
index 000000000..ce6e91d9c
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/Downloads.java
@@ -0,0 +1,16 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge.bmcl;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class Downloads {
+
+ public String changelog;
+ public String[] universal, src, javadoc, installer;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/ForgeBMCLVersionList.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/ForgeBMCLVersionList.java
new file mode 100644
index 000000000..72b5f7450
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/ForgeBMCLVersionList.java
@@ -0,0 +1,87 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge.bmcl;
+
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.utils.ArrayUtils;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class ForgeBMCLVersionList extends InstallerVersionList {
+
+ private static ForgeBMCLVersionList instance;
+
+ public static ForgeBMCLVersionList getInstance() {
+ if (instance == null) instance = new ForgeBMCLVersionList();
+ return instance;
+ }
+
+ public ArrayList root;
+ public Map> versionMap;
+ public List versions;
+
+ @Override
+ public void refreshList(String[] neededVersions) throws Exception {
+ if(versionMap == null) {
+ versionMap = new HashMap>();
+ versions = new ArrayList();
+ }
+
+ for (String x : neededVersions) {
+ if(versionMap.containsKey(x)) continue;
+ String s = NetUtils.doGet("http://bmclapi2.bangbang93.com/forge/minecraft/" + x);
+
+ if (s == null) {
+ continue;
+ }
+
+ try {
+ root = C.gson.fromJson(s, new TypeToken>(){}.getType());
+ for (ForgeVersion v : root) {
+ InstallerVersion iv = new InstallerVersion(v.version, StrUtils.formatVersion(v.minecraft));
+
+ List al = ArrayUtils.tryGetMapWithList(versionMap, StrUtils.formatVersion(v.minecraft));
+ iv.changelog = v.downloads.changelog;
+ iv.installer = "http://bmclapi.bangbang93.com/forge/getforge/" + iv.mcVersion + "/" + iv.selfVersion;
+ iv.universal = ArrayUtils.getEnd(v.downloads.universal);
+ al.add(iv);
+ versions.add(iv);
+ }
+ } catch(JsonSyntaxException e) {
+ HMCLog.warn("Failed to parse BMCLAPI response.", e);
+ }
+ }
+ Collections.sort(versions, new InstallerVersionComparator());
+ }
+
+ @Override
+ public List getVersions(String mcVersion) {
+ if (versions == null || versionMap == null) return null;
+ if (StrUtils.isBlank(mcVersion)) return versions;
+ List c = versionMap.get(mcVersion);
+ if (c == null) return versions;
+ Collections.sort(c, InstallerVersionComparator.INSTANCE);
+ return c;
+ }
+
+ @Override
+ public String getName() {
+ return "Forge - BMCLAPI (By: bangbang93)";
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/ForgeVersion.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/ForgeVersion.java
new file mode 100644
index 000000000..a8d7a45d9
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/bmcl/ForgeVersion.java
@@ -0,0 +1,14 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge.bmcl;
+
+/**
+ *
+ * @author hyh
+ */
+public class ForgeVersion {
+ public String time, minecraft, version, _id, __v;
+ public Downloads downloads;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersion.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersion.java
new file mode 100644
index 000000000..9ad018d19
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersion.java
@@ -0,0 +1,17 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge.vanilla;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class MinecraftForgeVersion {
+ public String branch, mcversion, jobver, version;
+ public int build;
+ public double modified;
+ public String[][] files;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersionList.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersionList.java
new file mode 100644
index 000000000..b86212373
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersionList.java
@@ -0,0 +1,95 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge.vanilla;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionNewerComparator;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class MinecraftForgeVersionList extends InstallerVersionList {
+ private static MinecraftForgeVersionList instance;
+ public static MinecraftForgeVersionList getInstance() {
+ if(instance == null)
+ instance = new MinecraftForgeVersionList();
+ return instance;
+ }
+
+ public MinecraftForgeVersionRoot root;
+ public Map> versionMap;
+ public List versions;
+
+ @Override
+ public void refreshList(String[] needed) throws Exception {
+ String s = NetUtils.doGet(C.URL_FORGE_LIST);
+ if(root!=null) return;
+
+ root = C.gson.fromJson(s, MinecraftForgeVersionRoot.class);
+
+ versionMap = new HashMap>();
+ versions = new ArrayList();
+
+ for(Map.Entry arr : root.mcversion.entrySet()) {
+ String mcver = StrUtils.formatVersion(arr.getKey());
+ ArrayList al = new ArrayList();
+ for(int num : arr.getValue()) {
+ MinecraftForgeVersion v = root.number.get(num);
+ InstallerVersion iv = new InstallerVersion(v.version, StrUtils.formatVersion(v.mcversion));
+ for(String[] f : v.files) {
+
+ String ver = v.mcversion + "-" + v.version;
+ if(!StrUtils.isBlank(v.branch)) {
+ ver = ver + "-" + v.branch;
+ }
+ String filename = root.artifact + "-" + ver + "-" + f[1] + "." + f[0];
+ String url = root.webpath + "/" + ver + "/" + filename;
+ if(f[1].equals("installer")) {
+ iv.installer = url;
+ } else if(f[1].equals("universal")) {
+ iv.universal = url;
+ } else if(f[1].equals("changelog")) {
+ iv.changelog = url;
+ }
+ }
+ if(StrUtils.isBlank(iv.installer) || StrUtils.isBlank(iv.universal)) {
+ continue;
+ }
+ Collections.sort(al, new InstallerVersionNewerComparator());
+ al.add(iv);
+ versions.add(iv);
+ }
+
+ versionMap.put(StrUtils.formatVersion(mcver), al);
+ }
+
+ Collections.sort(versions, new InstallerVersionComparator());
+ }
+
+ @Override
+ public List getVersions(String mcVersion) {
+ if (versions == null || versionMap == null) return null;
+ if(StrUtils.isBlank(mcVersion)) return versions;
+ List c = versionMap.get(mcVersion);
+ if(c == null) return versions;
+ Collections.sort(c, InstallerVersionComparator.INSTANCE);
+ return c;
+ }
+
+ @Override
+ public String getName() {
+ return "Forge - MinecraftForge Offical Site";
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersionRoot.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersionRoot.java
new file mode 100644
index 000000000..db55d564e
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/forge/vanilla/MinecraftForgeVersionRoot.java
@@ -0,0 +1,19 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.forge.vanilla;
+
+import java.util.Map;
+
+/**
+ *
+ * @author hyh
+ */
+public class MinecraftForgeVersionRoot {
+ public String artifact, webpath, adfly, homepage, name;
+ public Map branches, mcversion;
+ public Map promos;
+ public Map number;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderInstaller.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderInstaller.java
new file mode 100644
index 000000000..03a7122cc
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderInstaller.java
@@ -0,0 +1,90 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.liteloader;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.tasks.communication.PreviousResult;
+import org.jackhuang.hellominecraft.tasks.communication.PreviousResultRegistrator;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftLibrary;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+
+/**
+ *
+ * @author hyh
+ */
+public class LiteLoaderInstaller extends Task implements PreviousResultRegistrator {
+
+ public LiteLoaderVersionList.LiteLoaderInstallerVersion version;
+ public File installer;
+ public Profile profile;
+
+ public LiteLoaderInstaller(Profile profile, LiteLoaderVersionList.LiteLoaderInstallerVersion v) {
+ this(profile, v, null);
+ }
+
+ public LiteLoaderInstaller(Profile profile, LiteLoaderVersionList.LiteLoaderInstallerVersion v, File installer) {
+ this.profile = profile;
+ this.version = v;
+ this.installer = installer;
+ }
+
+ @Override
+ public boolean executeTask() {
+ if(profile == null || profile.getSelectedMinecraftVersion() == null) {
+ setFailReason(new RuntimeException(C.i18n("install.no_version")));
+ return false;
+ }
+ if(pre.size() != 1 && installer == null) throw new IllegalStateException("No registered previous task.");
+ if(installer == null)
+ installer = pre.get(pre.size() - 1).getResult();
+ try {
+ MinecraftVersion mv = (MinecraftVersion) profile.getSelectedMinecraftVersion().clone();
+ mv.inheritsFrom = mv.id;
+ mv.jar = mv.jar == null ? mv.id : mv.jar;
+ mv.libraries = new ArrayList(Arrays.asList(version.libraries));
+
+ mv.libraries.add(0, new MinecraftLibrary("com.mumfrey:liteloader:" + version.selfVersion));
+ FileUtils.copyFile(installer, new File(profile.getCanonicalGameDir(), "libraries/com/mumfrey/liteloader/" + version.selfVersion + "/liteloader-" + version.selfVersion + ".jar"));
+
+ mv.id += "-LiteLoader" + version.selfVersion;
+
+ if(!mv.mainClass.startsWith("net.minecraft.launchwrapper.")) {
+ mv.mainClass = "net.minecraft.launchwrapper.Launch";
+ mv.minecraftArguments += " --tweakClass " + version.tweakClass;
+ }
+ File folder = new File(profile.getCanonicalGameDir(), "versions/" + mv.id);
+ folder.mkdirs();
+ File json = new File(folder, mv.id + ".json");
+ HMCLog.log("Creating new version profile..." + mv.id + ".json");
+ FileUtils.write(json, C.gsonPrettyPrinting.toJson(mv));
+
+ return true;
+ } catch (Exception e) {
+ setFailReason(e);
+ return false;
+ }
+ }
+
+ @Override
+ public String getInfo() {
+ return C.i18n("install.liteloader.install");
+ }
+
+ ArrayList> pre = new ArrayList();
+ @Override
+ public Task registerPreviousResult(PreviousResult pr) {
+ pre.add(pr);
+ return this;
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderMCVersions.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderMCVersions.java
new file mode 100644
index 000000000..d1b9e9bfa
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderMCVersions.java
@@ -0,0 +1,16 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.liteloader;
+
+import java.util.Map;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class LiteLoaderMCVersions {
+ public Map> artefacts;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersion.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersion.java
new file mode 100644
index 000000000..9b18bc18d
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersion.java
@@ -0,0 +1,17 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.liteloader;
+
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftLibrary;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class LiteLoaderVersion {
+ public String tweakClass, file, version, md5, timestamp;
+ public MinecraftLibrary[] libraries;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionList.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionList.java
new file mode 100644
index 000000000..a8b2f6c87
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionList.java
@@ -0,0 +1,94 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.liteloader;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftLibrary;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList.InstallerVersion;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionNewerComparator;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class LiteLoaderVersionList extends InstallerVersionList {
+ private static LiteLoaderVersionList instance;
+ public static LiteLoaderVersionList getInstance() {
+ if(instance == null) {
+ instance = new LiteLoaderVersionList();
+ }
+ return instance;
+ }
+
+ public LiteLoaderVersionsRoot root;
+ public Map> versionMap;
+ public List versions;
+
+ @Override
+ public void refreshList(String[] needed) throws Exception {
+ String s = NetUtils.doGet(C.URL_LITELOADER_LIST);
+ if(root != null) return;
+
+ root = C.gson.fromJson(s, LiteLoaderVersionsRoot.class);
+
+ versionMap = new HashMap>();
+ versions = new ArrayList();
+
+ for(Map.Entry arr : root.versions.entrySet()) {
+ ArrayList al = new ArrayList();
+ LiteLoaderMCVersions mcv = arr.getValue();
+ for(Map.Entry entry : mcv.artefacts.get("com.mumfrey:liteloader").entrySet()) {
+ if("latest".equals(entry.getKey())) continue;
+ LiteLoaderVersion v = entry.getValue();
+ LiteLoaderInstallerVersion iv = new LiteLoaderInstallerVersion(v.version, StrUtils.formatVersion(arr.getKey()));
+ iv.universal = "http://dl.liteloader.com/versions/com/mumfrey/liteloader/" + arr.getKey() + "/" + v.file;
+ iv.tweakClass = v.tweakClass;
+ iv.libraries = Arrays.copyOf(v.libraries, v.libraries.length);
+ iv.installer = "http://dl.liteloader.com/redist/" + iv.mcVersion + "/liteloader-installer-" + iv.selfVersion.replace("_", "-") + ".jar";
+ al.add(iv);
+ versions.add(iv);
+ }
+ Collections.sort(al, new InstallerVersionNewerComparator());
+ versionMap.put(StrUtils.formatVersion(arr.getKey()), al);
+ }
+
+ Collections.sort(versions, InstallerVersionComparator.INSTANCE);
+ }
+
+ @Override
+ public List getVersions(String mcVersion) {
+ if (versions == null || versionMap == null) return null;
+ if(StrUtils.isBlank(mcVersion)) return versions;
+ List c = versionMap.get(mcVersion);
+ if(c == null) return versions;
+ Collections.sort(c, InstallerVersionComparator.INSTANCE);
+ return c;
+ }
+
+ @Override
+ public String getName() {
+ return "LiteLoader - LiteLoader Official Site(By: Mumfrey)";
+ }
+
+ public static class LiteLoaderInstallerVersion extends InstallerVersion {
+ public MinecraftLibrary[] libraries;
+ public String tweakClass;
+
+ public LiteLoaderInstallerVersion(String selfVersion, String mcVersion) {
+ super(selfVersion, mcVersion);
+ }
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionsMeta.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionsMeta.java
new file mode 100644
index 000000000..1da5f165e
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionsMeta.java
@@ -0,0 +1,14 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.liteloader;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class LiteLoaderVersionsMeta {
+ public String description, authors, url;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionsRoot.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionsRoot.java
new file mode 100644
index 000000000..17dc78118
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/liteloader/LiteLoaderVersionsRoot.java
@@ -0,0 +1,17 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.liteloader;
+
+import java.util.Map;
+
+/**
+ *
+ * @author hyh
+ */
+public class LiteLoaderVersionsRoot {
+ public Map versions;
+ public LiteLoaderVersionsMeta meta;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/OptiFineInstaller.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/OptiFineInstaller.java
new file mode 100644
index 000000000..d71d43765
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/OptiFineInstaller.java
@@ -0,0 +1,87 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.optifine;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.zip.ZipFile;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.tasks.communication.PreviousResult;
+import org.jackhuang.hellominecraft.tasks.communication.PreviousResultRegistrator;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftLibrary;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+
+/**
+ *
+ * @author hyh
+ */
+public class OptiFineInstaller extends Task implements PreviousResultRegistrator {
+
+ public File installer;
+ public Profile profile;
+ public String version;
+
+ public OptiFineInstaller(Profile profile, String version) {
+ this(profile, version, null);
+ }
+
+ public OptiFineInstaller(Profile profile, String version, File installer) {
+ this.profile = profile;
+ this.installer = installer;
+ this.version = version;
+ }
+
+ @Override
+ public boolean executeTask() {
+ if(profile == null || profile.getSelectedMinecraftVersion() == null) {
+ setFailReason(new RuntimeException(C.i18n("install.no_version")));
+ return false;
+ }
+ MinecraftVersion mv = (MinecraftVersion)profile.getSelectedMinecraftVersion().clone();
+
+ try {
+ mv.inheritsFrom = mv.id;
+ mv.jar = mv.jar == null ? mv.id : mv.jar;
+ mv.libraries.clear();
+ mv.libraries.add(0, new MinecraftLibrary("optifine:OptiFine:" + version));
+ FileUtils.copyFile(installer, new File(profile.getCanonicalGameDir(), "libraries/optifine/OptiFine/" + version + "/OptiFine-" + version + ".jar"));
+
+ mv.id += "-" + version;
+ if(new ZipFile(installer).getEntry("optifine/OptiFineTweaker.class") != null) {
+ if(!mv.mainClass.startsWith("net.minecraft.launchwrapper.")) {
+ mv.mainClass = "net.minecraft.launchwrapper.Launch";
+ mv.minecraftArguments += " --tweakClass optifine.OptiFineTweaker";
+ mv.libraries.add(1, new MinecraftLibrary("net.minecraft:launchwrapper:1.7"));
+ }
+ }
+ File loc = new File(profile.getCanonicalGameDir(), "versions/" + mv.id);
+ loc.mkdirs();
+ File json = new File(loc, mv.id + ".json");
+ FileUtils.writeStringToFile(json, C.gsonPrettyPrinting.toJson(mv, MinecraftVersion.class));
+ } catch (IOException ex) {
+ setFailReason(ex);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String getInfo() {
+ return "Optifine Installer";
+ }
+
+ ArrayList> pre = new ArrayList();
+ @Override
+ public Task registerPreviousResult(PreviousResult pr) {
+ pre.add(pr);
+ return this;
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/OptiFineVersion.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/OptiFineVersion.java
new file mode 100644
index 000000000..d72e02e2f
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/OptiFineVersion.java
@@ -0,0 +1,13 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.optifine;
+
+/**
+ *
+ * @author hyh
+ */
+public class OptiFineVersion {
+ public String dl, ver, date, mirror, mcver;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/bmcl/OptiFineBMCLVersionList.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/bmcl/OptiFineBMCLVersionList.java
new file mode 100644
index 000000000..226551497
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/bmcl/OptiFineBMCLVersionList.java
@@ -0,0 +1,87 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.optifine.bmcl;
+
+import com.google.gson.reflect.TypeToken;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.utils.ArrayUtils;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+import org.jackhuang.hellominecraft.launcher.utils.installers.optifine.OptiFineVersion;
+import org.jackhuang.hellominecraft.utils.tinystream.CollectionUtils;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class OptiFineBMCLVersionList extends InstallerVersionList {
+
+ private static OptiFineBMCLVersionList instance;
+
+ public static OptiFineBMCLVersionList getInstance() {
+ if (null == instance)
+ instance = new OptiFineBMCLVersionList();
+ return instance;
+ }
+
+ public ArrayList root;
+ public Map> versionMap;
+ public List versions;
+
+ @Override
+ public void refreshList(String[] needed) throws Exception {
+ String s = NetUtils.doGet("http://bmclapi.bangbang93.com/optifine/versionlist");
+
+ versionMap = new HashMap<>();
+ versions = new ArrayList<>();
+
+ if (s == null) return;
+ root = C.gson.fromJson(s, new TypeToken>() {
+ }.getType());
+ for(OptiFineVersion v : root) {
+ v.mirror = v.mirror.replace("http://optifine.net/http://optifine.net/", "http://optifine.net/");
+
+ if (StrUtils.isBlank(v.mcver)) {
+ Pattern p = Pattern.compile("OptiFine (.*) HD");
+ Matcher m = p.matcher(v.ver);
+ while (m.find()) v.mcver = m.group(1);
+ }
+ InstallerVersion iv = new InstallerVersion(v.ver, StrUtils.formatVersion(v.mcver));
+
+ List al = ArrayUtils.tryGetMapWithList(versionMap, StrUtils.formatVersion(v.mcver));
+ String url = "http://bmclapi.bangbang93.com/optifine/" + iv.selfVersion.replace(" ", "%20");
+ iv.installer = iv.universal = v.mirror;
+ al.add(iv);
+ versions.add(iv);
+ }
+
+ Collections.sort(versions, InstallerVersionComparator.INSTANCE);
+ }
+
+ @Override
+ public List getVersions(String mcVersion) {
+ if (versions == null || versionMap == null) return null;
+ if (StrUtils.isBlank(mcVersion)) return versions;
+ List c = versionMap.get(mcVersion);
+ if (c == null) return versions;
+ Collections.sort(c, InstallerVersionComparator.INSTANCE);
+ return c;
+ }
+
+ @Override
+ public String getName() {
+ return "OptiFine - BMCLAPI(By: bangbang93)";
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/vanilla/OptiFineDownloadFormatter.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/vanilla/OptiFineDownloadFormatter.java
new file mode 100644
index 000000000..09833cac0
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/vanilla/OptiFineDownloadFormatter.java
@@ -0,0 +1,49 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.optifine.vanilla;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.tasks.communication.PreviousResult;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class OptiFineDownloadFormatter extends Task implements PreviousResult {
+ String url, result;
+
+ public OptiFineDownloadFormatter(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public boolean executeTask() {
+ try {
+ String content = NetUtils.doGet(url);
+ Pattern p = Pattern.compile("\"downloadx\\?f=OptiFine(.*)\"");
+ Matcher m = p.matcher(content);
+ while(m.find()) result = m.group(1);
+ result = "http://optifine.net/downloadx?f=OptiFine" + result;
+ return true;
+ } catch (Exception ex) {
+ setFailReason(ex);
+ return false;
+ }
+ }
+
+ @Override
+ public String getInfo() {
+ return "Get OptiFine Download Link.";
+ }
+
+ @Override
+ public String getResult() {
+ return result;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/vanilla/OptiFineVersionList.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/vanilla/OptiFineVersionList.java
new file mode 100644
index 000000000..5bd3f8295
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/installers/optifine/vanilla/OptiFineVersionList.java
@@ -0,0 +1,119 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.installers.optifine.vanilla;
+
+import java.io.StringBufferInputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+import org.jackhuang.hellominecraft.launcher.utils.installers.optifine.OptiFineVersion;
+import org.jackhuang.hellominecraft.utils.ArrayUtils;
+import org.jackhuang.hellominecraft.utils.NetUtils;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ *
+ * @author hyh
+ */
+public class OptiFineVersionList extends InstallerVersionList {
+ private static OptiFineVersionList instance;
+ public static OptiFineVersionList getInstance() {
+ if(null == instance)
+ instance = new OptiFineVersionList();
+ return instance;
+ }
+
+
+ public ArrayList root = new ArrayList();
+ public Map> versionMap;
+ public List versions;
+
+ @Override
+ public void refreshList(String[] sss) throws Exception {
+ String content = NetUtils.doGet("http://optifine.net/downloads");
+ if(versions != null) return;
+ versionMap = new HashMap>();
+ versions = new ArrayList();
+
+ content = content.replace(" ", " ").replace(">", ">").replace("<", "<");
+
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = factory.newDocumentBuilder();
+ Document doc = db.parse(new StringBufferInputStream(content));
+ Element r = doc.getDocumentElement();
+ NodeList tables = r.getElementsByTagName("table");
+ for(int i = 0; i < tables.getLength(); i++) {
+ Element e = (Element)tables.item(i);
+ if("downloadTable".equals(e.getAttribute("class"))) {
+ NodeList tr = e.getElementsByTagName("tr");
+ for(int k = 0; k < tr.getLength(); k++) {
+ NodeList downloadLine = ((Element)tr.item(k)).getElementsByTagName("td");
+ OptiFineVersion v = new OptiFineVersion();
+ for(int j = 0; j < downloadLine.getLength(); j++) {
+ Element td = (Element)downloadLine.item(j);
+ if(StrUtils.startsWith(td.getAttribute("class"), "downloadLineMirror")) {
+ v.mirror = ((Element)td.getElementsByTagName("a").item(0)).getAttribute("href");
+ }
+ if(StrUtils.startsWith(td.getAttribute("class"), "downloadLineDownload")) {
+ v.dl = ((Element)td.getElementsByTagName("a").item(0)).getAttribute("href");
+ }
+ if(StrUtils.startsWith(td.getAttribute("class"), "downloadLineDate")) {
+ v.date = td.getTextContent();
+ }
+ if(StrUtils.startsWith(td.getAttribute("class"), "downloadLineFile")) {
+ v.ver = td.getTextContent();
+ }
+ }
+ if(StrUtils.isBlank(v.mcver)) {
+ Pattern p = Pattern.compile("OptiFine (.*?) ");
+ Matcher m = p.matcher(v.ver);
+ while(m.find()) v.mcver = StrUtils.formatVersion(m.group(1));
+ }
+ InstallerVersion iv = new InstallerVersion(v.ver, StrUtils.formatVersion(v.mcver));
+ iv.installer = iv.universal = v.mirror;
+ root.add(v);
+ versions.add(iv);
+
+
+ List ivl = ArrayUtils.tryGetMapWithList(versionMap, StrUtils.formatVersion(v.mcver));
+ ivl.add(iv);
+ }
+ }
+ }
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+
+ Collections.sort(versions, InstallerVersionComparator.INSTANCE);
+ }
+
+ @Override
+ public String getName() {
+ return "OptiFine - OptiFine Official Site";
+ }
+
+ @Override
+ public List getVersions(String mcVersion) {
+ if (versions == null || versionMap == null) return null;
+ if(StrUtils.isBlank(mcVersion)) return versions;
+ List c = versionMap.get(mcVersion);
+ if(c == null) return versions;
+ Collections.sort(c, InstallerVersionComparator.INSTANCE);
+ return c;
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Config.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Config.java
new file mode 100644
index 000000000..5fee69978
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Config.java
@@ -0,0 +1,151 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.settings;
+
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import com.google.gson.annotations.SerializedName;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+import org.jackhuang.hellominecraft.utils.OS;
+
+/**
+ *
+ * @author hyh
+ */
+public final class Config {
+ @SerializedName("last")
+ private String last;
+ @SerializedName("bgpath")
+ private String bgpath;
+ @SerializedName("username")
+ private String username;
+ @SerializedName("clientToken")
+ private String clientToken;
+ @SerializedName("enableShadow")
+ private boolean enableShadow;
+ @SerializedName("theme")
+ private int theme;
+
+ public int getTheme() {
+ return theme;
+ }
+
+ public void setTheme(int theme) {
+ this.theme = theme;
+ Settings.save();
+ }
+
+ public boolean isEnableShadow() {
+ return enableShadow;
+ }
+
+ public void setEnableShadow(boolean enableShadow) {
+ this.enableShadow = enableShadow;
+ Settings.save();
+ }
+
+ public String getLast() {
+ return last;
+ }
+
+ public void setLast(String last) {
+ this.last = last;
+ Settings.save();
+ }
+
+ public String getBgpath() {
+ return bgpath;
+ }
+
+ public void setBgpath(String bgpath) {
+ this.bgpath = bgpath;
+ Settings.save();
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ Settings.save();
+ }
+
+ public String getClientToken() {
+ return clientToken;
+ }
+
+ public int getLoginType() {
+ return logintype;
+ }
+
+ public void setLoginType(int logintype) {
+ this.logintype = logintype;
+ Settings.save();
+ }
+
+ public int getDownloadType() {
+ return downloadtype;
+ }
+
+ public void setDownloadType(int downloadtype) {
+ this.downloadtype = downloadtype;
+ Settings.save();
+ }
+
+ public TreeMap getConfigurations() {
+ if(configurations == null)
+ configurations = new TreeMap();
+ return configurations;
+ }
+
+ public boolean isCheckUpdate() {
+ return checkUpdate;
+ }
+
+ public void setCheckUpdate(boolean checkUpdate) {
+ this.checkUpdate = checkUpdate;
+ Settings.save();
+ }
+
+ public Map getYggdrasilConfig() {
+ return yggdrasil;
+ }
+
+ public void setYggdrasilConfig(Map yggdrasil) {
+ this.yggdrasil = yggdrasil;
+ Settings.save();
+ }
+
+ @SerializedName("logintype")
+ private int logintype;
+ @SerializedName("downloadtype")
+ private int downloadtype;
+ @SerializedName("configurations")
+ private TreeMap configurations;
+ @SerializedName("checkUpdate")
+ private boolean checkUpdate;
+ @SerializedName("yggdrasil")
+ private Map yggdrasil;
+
+ public Config()
+ {
+ clientToken = UUID.randomUUID().toString();
+ username = "";
+ logintype = downloadtype = 0;
+ checkUpdate = true;
+ configurations = new TreeMap();
+ Profile profile = new Profile();
+ configurations.put(profile.getName(), profile);
+ enableShadow = OS.os() == OS.WINDOWS;
+ theme = 0;
+ }
+
+ public DownloadType getDownloadSource() {
+ if(downloadtype >= DownloadType.values().length || downloadtype < 0) return null;
+ return DownloadType.values()[downloadtype];
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Profile.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Profile.java
new file mode 100644
index 000000000..9ad0470ff
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Profile.java
@@ -0,0 +1,297 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.settings;
+
+import java.io.File;
+import org.jackhuang.hellominecraft.launcher.launch.IMinecraftProvider;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.launcher.utils.MCUtils;
+import org.jackhuang.hellominecraft.launcher.utils.version.GameDirType;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.Utils;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersionManager;
+
+/**
+ *
+ * @author hyh
+ */
+public final class Profile {
+
+ private String name, selectedMinecraftVersion = "", javaArgs, minecraftArgs, maxMemory, permSize, width, height, userProperties;
+ private String gameDir, javaDir, wrapperLauncher, serverIp;
+ private boolean fullscreen, debug, noJVMArgs, canceledWrapper;
+
+ /**
+ * 0 - Close the launcher when the game starts.
+ * 1 - Hide the launcher when the game starts.
+ * 2 - Keep the launcher open.
+ */
+ private int launcherVisibility;
+
+ /**
+ * 0 - .minecraft
+ * 1 - .minecraft/versions/<version>/
+ */
+ private int gameDirType;
+
+ protected transient IMinecraftProvider minecraftProvider;
+
+ public Profile() {
+ this("Default");
+ }
+
+ public Profile(String name) {
+ this.name = name;
+ gameDir = MCUtils.getInitGameDir().getPath();
+ debug = fullscreen = canceledWrapper = false;
+ javaDir = IOUtils.getJavaDir();
+ launcherVisibility = gameDirType = 0;
+ minecraftArgs = serverIp = "";
+ }
+
+ public Profile(Profile v) {
+ this();
+ if (v == null) {
+ return;
+ }
+ name = v.name;
+ gameDir = v.gameDir;
+ maxMemory = v.maxMemory;
+ width = v.width;
+ height = v.height;
+ fullscreen = v.fullscreen;
+ javaArgs = v.javaArgs;
+ javaDir = v.javaDir;
+ debug = v.debug;
+ minecraftArgs = v.minecraftArgs;
+ permSize = v.permSize;
+ gameDirType = v.gameDirType;
+ canceledWrapper = v.canceledWrapper;
+ noJVMArgs = v.noJVMArgs;
+ launcherVisibility = v.launcherVisibility;
+ wrapperLauncher = v.wrapperLauncher;
+ serverIp = v.serverIp;
+ }
+
+ public IMinecraftProvider getMinecraftProvider() {
+ if(minecraftProvider == null) minecraftProvider = new MinecraftVersionManager(this);
+ return minecraftProvider;
+ }
+
+ public MinecraftVersion getSelectedMinecraftVersion() {
+ if (StrUtils.isBlank(selectedMinecraftVersion)) {
+ MinecraftVersion v = getMinecraftProvider().getOneVersion();
+ if (v == null) {
+ return null;
+ }
+ selectedMinecraftVersion = v.id;
+ return v;
+ }
+ MinecraftVersion v = getMinecraftProvider().getVersionById(selectedMinecraftVersion);
+ if(v == null) v = getMinecraftProvider().getOneVersion();
+ if(v != null) setSelectedMinecraftVersion(v.id);
+ return v;
+ }
+
+ public String getGameDir() {
+ if (StrUtils.isBlank(gameDir)) {
+ gameDir = MCUtils.getInitGameDir().getPath();
+ }
+ return IOUtils.addSeparator(gameDir);
+ }
+
+ public String getCanonicalGameDir() {
+ return IOUtils.tryGetCanonicalFolderPath(getGameDirFile());
+ }
+
+ public File getCanonicalGameDirFile() {
+ return IOUtils.tryGetCanonicalFile(getGameDirFile());
+ }
+
+ public File getGameDirFile() {
+ return new File(getGameDir());
+ }
+
+ public Profile setGameDir(String gameDir) {
+ this.gameDir = gameDir;
+ Settings.save();
+ return this;
+ }
+
+ public String getJavaDir() {
+ if (StrUtils.isBlank(javaDir)) {
+ javaDir = IOUtils.getJavaDir();
+ }
+ return javaDir;
+ }
+
+ public File getJavaDirFile() {
+ return new File(getJavaDir());
+ }
+
+ public void setJavaDir(String javaDir) {
+ this.javaDir = javaDir;
+ Settings.save();
+ }
+
+ public File getFolder(String folder) {
+ return new File(getGameDir(), folder);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setSelectedMinecraftVersion(String selectedMinecraftVersion) {
+ this.selectedMinecraftVersion = selectedMinecraftVersion;
+ }
+
+ public String getJavaArgs() {
+ if(StrUtils.isBlank(javaArgs)) return "";
+ return javaArgs;
+ }
+
+ public void setJavaArgs(String javaArgs) {
+ this.javaArgs = javaArgs;
+ Settings.save();
+ }
+
+ public boolean hasJavaArgs() {
+ return StrUtils.isNotBlank(getJavaArgs().trim());
+ }
+
+ public String getMaxMemory() {
+ if(StrUtils.isBlank(maxMemory)) return String.valueOf(Utils.getSuggestedMemorySize());
+ return maxMemory;
+ }
+
+ public void setMaxMemory(String maxMemory) {
+ this.maxMemory = maxMemory;
+ Settings.save();
+ }
+
+ public String getWidth() {
+ if(StrUtils.isBlank(width)) return "854";
+ return width;
+ }
+
+ public void setWidth(String width) {
+ this.width = width;
+ }
+
+ public String getHeight() {
+ if(StrUtils.isBlank(height)) return "480";
+ return height;
+ }
+
+ public void setHeight(String height) {
+ this.height = height;
+ Settings.save();
+ }
+
+ public String getUserProperties() {
+ if(userProperties == null) return "";
+ return userProperties;
+ }
+
+ public void setUserProperties(String userProperties) {
+ this.userProperties = userProperties;
+ }
+
+ public boolean isFullscreen() {
+ return fullscreen;
+ }
+
+ public void setFullscreen(boolean fullscreen) {
+ this.fullscreen = fullscreen;
+ Settings.save();
+ }
+
+ public boolean isDebug() {
+ return debug;
+ }
+
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ Settings.save();
+ }
+
+ public int getLauncherVisibility() {
+ return launcherVisibility;
+ }
+
+ public void setLauncherVisibility(int launcherVisibility) {
+ this.launcherVisibility = launcherVisibility;
+ Settings.save();
+ }
+
+ public GameDirType getGameDirType() {
+ return GameDirType.values()[gameDirType];
+ }
+
+ public void setGameDirType(GameDirType gameDirType) {
+ this.gameDirType = gameDirType.ordinal();
+ Settings.save();
+ }
+
+ public String getPermSize() {
+ return permSize;
+ }
+
+ public void setPermSize(String permSize) {
+ this.permSize = permSize;
+ Settings.save();
+ }
+
+ public boolean isNoJVMArgs() {
+ return noJVMArgs;
+ }
+
+ public void setNoJVMArgs(boolean noJVMArgs) {
+ this.noJVMArgs = noJVMArgs;
+ }
+
+ public String getMinecraftArgs() {
+ return minecraftArgs;
+ }
+
+ public void setMinecraftArgs(String minecraftArgs) {
+ this.minecraftArgs = minecraftArgs;
+ Settings.save();
+ }
+
+ public boolean isCanceledWrapper() {
+ return canceledWrapper;
+ }
+
+ public void setCanceledWrapper(boolean canceledWrapper) {
+ this.canceledWrapper = canceledWrapper;
+ Settings.save();
+ }
+
+ public String getWrapperLauncher() {
+ return wrapperLauncher;
+ }
+
+ public void setWrapperLauncher(String wrapperLauncher) {
+ this.wrapperLauncher = wrapperLauncher;
+ Settings.save();
+ }
+
+ public String getServerIp() {
+ return serverIp;
+ }
+
+ public void setServerIp(String serverIp) {
+ this.serverIp = serverIp;
+ Settings.save();
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Settings.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Settings.java
new file mode 100644
index 000000000..be5b40f56
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/settings/Settings.java
@@ -0,0 +1,148 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.settings;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.utils.functions.DoneListener0;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.Main;
+import org.jackhuang.hellominecraft.utils.tinystream.CollectionUtils;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.UpdateChecker;
+import org.jackhuang.hellominecraft.utils.VersionNumber;
+
+/**
+ *
+ * @author hyh
+ */
+public final class Settings {
+ public static final File settingsFile = new File(IOUtils.currentDir(), "hmcl.json");
+
+ private static boolean isFirstLoad;
+ private static Config settings;
+ public static final UpdateChecker UPDATE_CHECKER;
+
+ public static Config s() {
+ return settings;
+ }
+
+ public static boolean isFirstLoad() {
+ return isFirstLoad;
+ }
+
+ static {
+ if (settingsFile.exists()) {
+ try {
+ String str = FileUtils.readFileToString(settingsFile);
+ if (str == null || str.trim().equals("")) {
+ init();
+
+ HMCLog.log("Settings file is empty, use the default settings.");
+ } else {
+ settings = C.gsonPrettyPrinting.fromJson(str, Config.class);
+ }
+ HMCLog.log("Initialized settings.");
+ } catch (Exception e) {
+ HMCLog.warn("Something happened wrongly when load settings.", e);
+ if (MessageBox.Show(C.i18n("settings.failed_load"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) {
+ init();
+ } else {
+ HMCLog.err("Cancelled loading settings.");
+ System.exit(1);
+ }
+ }
+ } else {
+ HMCLog.log("No settings file here, may be first loading.");
+
+ isFirstLoad = true;
+ init();
+ }
+ if(settings == null) init();
+ isFirstLoad = StrUtils.isBlank(settings.getUsername());
+ if(!settings.getConfigurations().containsKey("Default")) {
+ settings.getConfigurations().put("Default", new Profile());
+ }
+
+ UPDATE_CHECKER = new UpdateChecker(new VersionNumber(Main.firstVer, Main.secondVer, Main.thirdVer), "hmcl", settings.isCheckUpdate(), new DoneListener0() {
+
+ @Override
+ public void onDone() {
+ Main.invokeUpdate();
+ }
+ });
+ }
+
+ public static void init() {
+ settings = new Config();
+ save();
+ }
+
+ public static void save() {
+ try {
+ FileUtils.write(settingsFile, C.gsonPrettyPrinting.toJson(settings));
+ } catch (IOException ex) {
+ HMCLog.err("Failed to save config", ex);
+ }
+ }
+
+ public static Profile getVersion(String name) {
+ if (settings == null) {
+ return null;
+ }
+ if (settings.getConfigurations() == null) {
+ return null;
+ }
+ return settings.getConfigurations().get(name);
+ }
+
+ public static Map getVersions() {
+ return settings.getConfigurations();
+ }
+
+ public static void setVersion(Profile ver) {
+ if (ver == null) {
+ return;
+ }
+ settings.getConfigurations().put(ver.getName(), ver);
+ }
+
+ public static Collection getProfiles() {
+ return CollectionUtils.sortOut(settings.getConfigurations().values(), (t) -> t != null && t.getName() != null);
+ }
+
+ public static Profile getOneProfile() {
+ if(settings.getConfigurations().size() == 0) {
+ settings.getConfigurations().put("Default", new Profile());
+ }
+ return settings.getConfigurations().firstEntry().getValue();
+ }
+
+ public static boolean trySetVersion(Profile ver) {
+ if (ver == null || ver.getName() == null) {
+ return false;
+ }
+ if (settings.getConfigurations().containsKey(ver.getName())) {
+ return false;
+ }
+ settings.getConfigurations().put(ver.getName(), ver);
+ return true;
+ }
+
+ public static void delVersion(Profile ver) {
+ delVersion(ver.getName());
+ }
+
+ public static void delVersion(String ver) {
+ if (settings == null) return;
+ settings.getConfigurations().remove(ver);
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Extract.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Extract.java
new file mode 100644
index 000000000..af987a497
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Extract.java
@@ -0,0 +1,25 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+import java.util.Arrays;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public class Extract extends Object implements Cloneable {
+ String[] exclude;
+
+ @Override
+ protected Object clone() {
+ Extract e = new Extract();
+ e.exclude = exclude == null ? null : Arrays.copyOf(exclude, exclude.length);
+ return e;
+ }
+
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/GameDirType.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/GameDirType.java
new file mode 100644
index 000000000..511021f52
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/GameDirType.java
@@ -0,0 +1,15 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+/**
+ *
+ * @author huangyuhui
+ */
+public enum GameDirType {
+ ROOT_FOLDER,
+ VERSION_FOLDER;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/IMinecraftLibrary.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/IMinecraftLibrary.java
new file mode 100644
index 000000000..f6a86e7a4
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/IMinecraftLibrary.java
@@ -0,0 +1,46 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+import java.io.File;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+
+/**
+ *
+ * @author hyh
+ */
+public abstract class IMinecraftLibrary {
+
+ public String name;
+ public IMinecraftLibrary(String name) {
+ this.name = name;
+ }
+
+ public abstract boolean isRequiredToUnzip();
+ public abstract String[] getDecompressExtractRules();
+ public abstract void init();
+ public abstract boolean allow();
+ public abstract File getFilePath(File gameDir);
+ public abstract String getDownloadURL(String urlBase, DownloadType downloadType);
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof MinecraftLibrary)
+ return ((MinecraftLibrary) obj).name.equals(name);
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ protected abstract Object clone();
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftClassicVersion.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftClassicVersion.java
new file mode 100644
index 000000000..8b99dd27f
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftClassicVersion.java
@@ -0,0 +1,63 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Set;
+import org.jackhuang.hellominecraft.launcher.launch.IMinecraftProvider;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+
+/**
+ *
+ * @author hyh
+ */
+public class MinecraftClassicVersion extends MinecraftVersion {
+
+ public MinecraftClassicVersion() {
+ super();
+
+ mainClass = "net.minecraft.client.Minecraft";
+ id = "Classic";
+ type = "release";
+ processArguments = assets = releaseTime = time = null;
+ minecraftArguments = "${auth_player_name} ${auth_session} --workDir ${game_directory}";
+ libraries = new ArrayList<>();
+ libraries.add(new MinecraftOldLibrary("lwjgl"));
+ libraries.add(new MinecraftOldLibrary("jinput"));
+ libraries.add(new MinecraftOldLibrary("lwjgl_util"));
+ }
+
+ @Override
+ public Object clone() {
+ return super.clone();
+ }
+
+ @Override
+ public MinecraftVersion resolve(IMinecraftProvider manager, Set resolvedSoFar, DownloadType sourceType) {
+ return this;
+ }
+
+ @Override
+ public File getJar(File gameDir) {
+ return new File(gameDir, "bin/minecraft.jar");
+ }
+
+ @Override
+ public File getJar(File gameDir, String suffix) {
+ return new File(gameDir, "bin/minecraft" + suffix + ".jar");
+ }
+
+ @Override
+ public File getNatives(File gameDir) {
+ return new File(gameDir, "bin/natives");
+ }
+
+ @Override
+ public boolean isAllowedToUnpackNatives() {
+ return false;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftLibrary.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftLibrary.java
new file mode 100644
index 000000000..ff3961fff
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftLibrary.java
@@ -0,0 +1,137 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.utils.OS;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class MinecraftLibrary extends IMinecraftLibrary implements Cloneable {
+
+ public ArrayList rules;
+ public String url, formatted=null;
+ //public boolean serverreq=true, clientreq=true;
+ public String[] checksums;
+ public Natives natives;
+ public Extract extract;
+
+ public MinecraftLibrary(String name) {
+ super(name);
+ }
+
+ public MinecraftLibrary(ArrayList rules, String url, String[] checksums, Natives natives, String name, Extract extract) {
+ super(name);
+ this.rules = rules == null ? null : (ArrayList) rules.clone();
+ this.url = url;
+ this.checksums = checksums == null ? null : Arrays.copyOf(checksums, checksums.length);
+ this.natives = natives == null ? null : (Natives)natives.clone();
+ this.extract = extract == null ? null :(Extract)extract.clone();
+ }
+
+ @Override
+ public Object clone() {
+ return new MinecraftLibrary(rules, url, checksums, natives, name, extract);
+ }
+
+ /**
+ * is the library allowed to load.
+ *
+ * @return
+ */
+ @Override
+ public boolean allow() {
+ boolean flag = false;
+ if (rules == null || rules.isEmpty()) {
+ flag = true;
+ } else {
+ for (Rules r : rules) {
+ if (r.action.equals("disallow")) {
+ if (r.os != null && (StrUtils.isBlank(r.os.name) || r.os.name.equalsIgnoreCase(OS.os().toString()))) {
+ flag = false;
+ break;
+ }
+ } else {
+ if (r.os != null && (StrUtils.isBlank(r.os.name) || r.os.name.equalsIgnoreCase(OS.os().toString()))) {
+ flag = true;
+ }
+ if (r.os == null) {
+ flag = true;
+ }
+ }
+ }
+ }
+ return flag;
+ }
+
+ private String formatArch(String nati) {
+ String arch = System.getProperty("os.arch");
+ if (arch.contains("64")) {
+ arch = "64";
+ } else {
+ arch = "32";
+ }
+ if (nati == null) {
+ return "";
+ }
+ return nati.replace("${arch}", arch);
+ }
+
+ private String getNative() {
+ OS os = OS.os();
+ if (os == OS.WINDOWS) {
+ return formatArch(natives.windows);
+ } else if (os == OS.OSX) {
+ return formatArch(natives.osx);
+ } else {
+ return formatArch(natives.linux);
+ }
+ }
+
+ @Override
+ public boolean isRequiredToUnzip() {
+ return natives != null && allow();
+ }
+
+ @Override
+ public void init() {
+ String str = name;
+ String[] s = str.split(":");
+ str = s[0];
+ str = str.replace('.', File.separatorChar);
+ if (natives == null) {
+ str += File.separator + s[1] + File.separator + s[2]
+ + File.separator + s[1] + '-' + s[2] + ".jar";
+ } else {
+ str += File.separator + s[1] + File.separator + s[2]
+ + File.separator + s[1] + '-' + s[2] + '-';
+ str += getNative();
+ str += ".jar";
+ }
+ formatted = str;
+ }
+
+ @Override
+ public File getFilePath(File gameDir) {
+ return new File(gameDir, "libraries" + File.separatorChar + formatted);
+ }
+
+ @Override
+ public String getDownloadURL(String urlBase, DownloadType downloadType) {
+ if(StrUtils.isNotBlank(url)&&downloadType.getProvider().isAllowedToUseSelfURL()) urlBase = this.url;
+ return urlBase + formatted.replace('\\', '/');
+ }
+
+ @Override
+ public String[] getDecompressExtractRules() {
+ return extract == null || extract.exclude == null ? new String[0] : extract.exclude;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftOldLibrary.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftOldLibrary.java
new file mode 100644
index 000000000..011095699
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftOldLibrary.java
@@ -0,0 +1,51 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+import java.io.File;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+
+/**
+ *
+ * @author hyh
+ */
+public class MinecraftOldLibrary extends MinecraftLibrary {
+
+ public MinecraftOldLibrary(String name) {
+ super(name);
+ }
+
+ @Override
+ public boolean isRequiredToUnzip() {
+ return false;
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean allow() {
+ return true;
+ }
+
+ @Override
+ public File getFilePath(File gameDir) {
+ return new File(gameDir, "bin/" + name + ".jar");
+ }
+
+ @Override
+ public Object clone() {
+ return new MinecraftOldLibrary(name);
+ }
+
+ @Override
+ public String getDownloadURL(String urlBase, DownloadType downloadType) {
+ return null;
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftVersion.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftVersion.java
new file mode 100644
index 000000000..e90e50c8a
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftVersion.java
@@ -0,0 +1,113 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.jackhuang.hellominecraft.launcher.launch.IMinecraftProvider;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.utils.ArrayUtils;
+
+/**
+ *
+ * @author hyh
+ */
+public class MinecraftVersion implements Cloneable, Comparable {
+
+ public String minecraftArguments, mainClass, time, id, type, processArguments,
+ releaseTime, assets, jar, inheritsFrom;
+ public int minimumLauncherVersion;
+ public boolean hidden;
+
+ public List libraries;
+
+ public MinecraftVersion() {
+ }
+
+ public MinecraftVersion(String minecraftArguments, String mainClass, String time, String id, String type, String processArguments, String releaseTime, String assets, String jar, String inheritsFrom, int minimumLauncherVersion, List libraries, boolean hidden) {
+ this();
+ this.minecraftArguments = minecraftArguments;
+ this.mainClass = mainClass;
+ this.time = time;
+ this.id = id;
+ this.type = type;
+ this.processArguments = processArguments;
+ this.releaseTime = releaseTime;
+ this.assets = assets;
+ this.jar = jar;
+ this.inheritsFrom = inheritsFrom;
+ this.minimumLauncherVersion = minimumLauncherVersion;
+ this.hidden = hidden;
+ if(libraries == null) this.libraries = new ArrayList<>();
+ else {
+ this.libraries = new ArrayList<>(libraries.size());
+ for (IMinecraftLibrary library : libraries) {
+ this.libraries.add((MinecraftLibrary) library.clone());
+ }
+ }
+ }
+
+ @Override
+ public Object clone() {
+ return new MinecraftVersion(minecraftArguments, mainClass, time, id, type, processArguments, releaseTime, assets, jar, inheritsFrom, minimumLauncherVersion, libraries, hidden);
+ }
+
+ public MinecraftVersion resolve(IMinecraftProvider manager, DownloadType sourceType) {
+ return resolve(manager, new HashSet<>(), sourceType);
+ }
+
+ protected MinecraftVersion resolve(IMinecraftProvider manager, Set resolvedSoFar, DownloadType sourceType) {
+ if (inheritsFrom == null) {
+ return this;
+ }
+ if (!resolvedSoFar.add(id)) {
+ throw new IllegalStateException("Circular dependency detected.");
+ }
+
+ MinecraftVersion parent = manager.getVersionById(inheritsFrom);
+ if(parent == null) {
+ if(!manager.install(inheritsFrom, sourceType)) return this;
+ parent = manager.getVersionById(inheritsFrom);
+ }
+ parent = parent.resolve(manager, resolvedSoFar, sourceType);
+ MinecraftVersion result = new MinecraftVersion(
+ this.minecraftArguments != null ? this.minecraftArguments : parent.minecraftArguments,
+ this.mainClass != null ? this.mainClass : parent.mainClass,
+ this.time, this.id, this.type, parent.processArguments, this.releaseTime,
+ this.assets != null ? this.assets : parent.assets,
+ this.jar != null ? this.jar : parent.jar,
+ null, parent.minimumLauncherVersion,
+ this.libraries != null ? ArrayUtils.merge(this.libraries, parent.libraries) : parent.libraries, this.hidden);
+
+ return result;
+ }
+
+ public File getJar(File gameDir) {
+ String jarId = this.jar == null ? this.id : this.jar;
+ return new File(gameDir, "versions/" + jarId + "/" + jarId + ".jar");
+ }
+
+ public File getJar(File gameDir, String suffix) {
+ String jarId = this.jar == null ? this.id : this.jar;
+ return new File(gameDir, "versions/" + jarId + "/" + jarId + suffix + ".jar");
+ }
+
+ public File getNatives(File gameDir) {
+ return new File(gameDir, "versions/" + id + "/" + id
+ + "-natives");
+ }
+
+ public boolean isAllowedToUnpackNatives() {
+ return true;
+ }
+
+ @Override
+ public int compareTo(MinecraftVersion o) {
+ return id.compareTo(((MinecraftVersion) o).id);
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftVersionManager.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftVersionManager.java
new file mode 100644
index 000000000..f8aacc522
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/MinecraftVersionManager.java
@@ -0,0 +1,306 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.launch.GameLauncher;
+import org.jackhuang.hellominecraft.launcher.launch.GameLauncher.DownloadLibraryJob;
+import org.jackhuang.hellominecraft.launcher.launch.IMinecraftLoader;
+import org.jackhuang.hellominecraft.launcher.launch.IMinecraftProvider;
+import org.jackhuang.hellominecraft.launcher.launch.MinecraftLoader;
+import org.jackhuang.hellominecraft.utils.BaseLauncherProfile;
+import org.jackhuang.hellominecraft.utils.FileUtils;
+import org.jackhuang.hellominecraft.launcher.utils.MCUtils;
+import org.jackhuang.hellominecraft.launcher.utils.auth.UserProfileProvider;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.Utils;
+
+/**
+ *
+ * @author hyh
+ */
+public final class MinecraftVersionManager extends IMinecraftProvider {
+
+ private File baseFolder;
+ private final Profile profile;
+ private final Map versions = new TreeMap();
+ private final Gson gson = Utils.getDefaultGsonBuilder().create();
+
+ /**
+ *
+ * @param p
+ */
+ public MinecraftVersionManager(Profile p) {
+ super(p);
+ this.profile = p;
+ refreshVersions();
+ }
+
+ public File getFolder() {
+ return baseFolder;
+ }
+
+ @Override
+ public Collection getVersions() {
+ return versions.values();
+ }
+
+ @Override
+ public int getVersionCount() {
+ return versions.size();
+ }
+
+ @Override
+ public void refreshVersions() {
+ baseFolder = profile.getCanonicalGameDirFile();
+ try {
+ BaseLauncherProfile.tryWriteProfile(baseFolder);
+ } catch (IOException ex) {
+ HMCLog.warn("Failed to create launcher_profiles.json, Forge/LiteLoader installer will not work.", ex);
+ }
+
+ versions.clear();
+ File oldDir = new File(baseFolder, "bin");
+ if (oldDir.exists()) {
+ MinecraftClassicVersion v = new MinecraftClassicVersion();
+ versions.put(v.id, v);
+ }
+
+ File version = new File(baseFolder, "versions");
+ File[] files = version.listFiles();
+ if (files == null || files.length == 0) return;
+
+ for (File dir : files) {
+ String id = dir.getName();
+ File jsonFile = new File(dir, id + ".json");
+
+ if (!dir.isDirectory()) continue;
+ boolean ask = false;
+ File[] jsons = null;
+ if (!jsonFile.exists()) {
+ jsons = FileUtils.searchSuffix(dir, "json");
+ if (jsons.length == 1) ask = true;
+ }
+ if (ask) {
+ HMCLog.warn("Found not matched filenames version: " + id + ", json: " + jsons[0].getName());
+ if (MessageBox.Show(String.format(C.i18n("launcher.versions_json_not_matched"), id, jsons[0].getName()), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
+ jsons[0].renameTo(new File(jsons[0].getParent(), id + ".json"));
+ }
+ if (!jsonFile.exists()) {
+ if (StrUtils.formatVersion(id) == null) {
+ if (MessageBox.Show(C.i18n("launcher.versions_json_not_matched_cannot_auto_completion", id), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
+ FileUtils.deleteDirectoryQuietly(dir);
+ } else if (MessageBox.Show(C.i18n("launcher.versions_json_not_matched_needs_auto_completion", id), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) {
+ if (!refreshJson(id)) {
+ if (MessageBox.Show(C.i18n("launcher.versions_json_not_matched_cannot_auto_completion", id), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
+ FileUtils.deleteDirectoryQuietly(dir);
+ }
+ }
+ continue;
+ }
+ MinecraftVersion mcVersion = null;
+ try {
+ mcVersion = gson.fromJson(FileUtils.readFileToString(jsonFile), MinecraftVersion.class);
+ if (mcVersion == null) throw new RuntimeException("Wrong json format, got null.");
+ } catch (IOException | RuntimeException e) {
+ HMCLog.warn("Found wrong format json, try to fix it.", e);
+ if (MessageBox.Show(C.i18n("launcher.versions_json_not_formattedn", id), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION) {
+ refreshJson(id);
+ try {
+ mcVersion = gson.fromJson(FileUtils.readFileToString(jsonFile), MinecraftVersion.class);
+ if (mcVersion == null) throw new RuntimeException("Wrong json format, got null.");
+ } catch (IOException | RuntimeException ex) {
+ HMCLog.err("Retried but still failed.");
+ HMCLog.warn("Ignoring: " + dir + ", the json of this Minecraft is malformed.", ex);
+ continue;
+ }
+ }
+ }
+ try {
+ if (!id.equals(mcVersion.id)) {
+ HMCLog.warn("Found: " + dir + ", it contains id: " + mcVersion.id + ", expected: " + id + ", the launcher will fix this problem.");
+ mcVersion.id = id;
+ FileUtils.writeQuietly(jsonFile, gson.toJson(mcVersion));
+ }
+
+ if (mcVersion.libraries != null)
+ for (MinecraftLibrary ml : mcVersion.libraries)
+ ml.init();
+ versions.put(id, mcVersion);
+ } catch (Exception e) {
+ HMCLog.warn("Ignoring: " + dir + ", the json of this Minecraft is malformed.");
+ }
+ }
+ }
+
+ @Override
+ public boolean removeVersionFromDisk(String name) {
+ File version = new File(baseFolder, "versions/" + name);
+ if (!version.exists()) return true;
+
+ versions.remove(name);
+ return FileUtils.deleteDirectoryQuietly(version);
+ }
+
+ @Override
+ public boolean renameVersion(String from, String to) {
+ try {
+ File fromJson = new File(baseFolder, "versions/" + from + "/" + from + ".json");
+ MinecraftVersion mcVersion = gson.fromJson(FileUtils.readFileToString(fromJson), MinecraftVersion.class);
+ mcVersion.id = to;
+ FileUtils.writeQuietly(fromJson, gson.toJson(mcVersion));
+ File toDir = new File(baseFolder, "versions/" + to);
+ new File(baseFolder, "versions/" + from).renameTo(toDir);
+ File toJson = new File(toDir, to + ".json");
+ File toJar = new File(toDir, to + ".jar");
+ new File(toDir, from + ".json").renameTo(toJson);
+ new File(toDir, from + ".jar").renameTo(toJar);
+ return true;
+ } catch (IOException | JsonSyntaxException e) {
+ HMCLog.warn("Failed to rename " + from + " to " + to + ", the json of this Minecraft is malformed.", e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean refreshJson(String id) {
+ return MCUtils.downloadMinecraftVersionJson(baseFolder, id, Settings.s().getDownloadSource());
+ }
+
+ @Override
+ public boolean refreshAssetsIndex(String id) {
+ MinecraftVersion mv = getVersionById(id);
+ if (mv == null) return false;
+ return MCUtils.downloadMinecraftAssetsIndex(new File(baseFolder, "assets"), mv.assets, Settings.s().getDownloadSource());
+ }
+
+ @Override
+ public boolean install(String id, DownloadType sourceType) {
+ MinecraftVersion v = MCUtils.downloadMinecraft(baseFolder, id, sourceType);
+ if (v != null) {
+ versions.put(v.id, v);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public File getRunDirectory(String id) {
+ switch (profile.getGameDirType()) {
+ case VERSION_FOLDER:
+ return new File(baseFolder, "versions/" + id + "/");
+ default:
+ return baseFolder;
+ }
+ }
+
+ @Override
+ public List getDownloadLibraries(DownloadType downloadType) {
+ ArrayList downloadLibraries = new ArrayList<>();
+ MinecraftVersion v = profile.getSelectedMinecraftVersion().resolve(this, Settings.s().getDownloadSource());
+ for (IMinecraftLibrary l : v.libraries) {
+ l.init();
+ if (l.allow()) {
+ File ff = l.getFilePath(baseFolder);
+ if (!ff.exists()) {
+ String libURL = downloadType.getProvider().getLibraryDownloadURL() + "/";
+ libURL = l.getDownloadURL(libURL, downloadType);
+ if (libURL != null)
+ downloadLibraries.add(new DownloadLibraryJob(l.name, libURL, ff));
+ }
+ }
+ }
+ return downloadLibraries;
+ }
+
+ @Override
+ public void openSelf(String mv) {
+ Utils.openFolder(getRunDirectory(mv));
+ }
+
+ @Override
+ public void open(String mv, String name) {
+ Utils.openFolder(new File(getRunDirectory(mv), name));
+ }
+
+ @Override
+ public File getAssets() {
+ return new File(profile.getCanonicalGameDirFile(), "assets");
+ }
+
+ @Override
+ public GameLauncher.DecompressLibraryJob getDecompressLibraries() {
+ MinecraftVersion v = profile.getSelectedMinecraftVersion().resolve(this, Settings.s().getDownloadSource());
+ ArrayList unzippings = new ArrayList<>();
+ ArrayList extractRules = new ArrayList<>();
+ for (IMinecraftLibrary l : v.libraries) {
+ l.init();
+ if (l.isRequiredToUnzip() && v.isAllowedToUnpackNatives()) {
+ unzippings.add(IOUtils.tryGetCanonicalFile(l.getFilePath(baseFolder)));
+ extractRules.add(l.getDecompressExtractRules());
+ }
+ }
+ return new GameLauncher.DecompressLibraryJob(unzippings.toArray(new File[0]), extractRules.toArray(new String[0][]), getDecompressNativesToLocation());
+ }
+
+ @Override
+ public File getDecompressNativesToLocation() {
+ MinecraftVersion v = profile.getSelectedMinecraftVersion();
+ return v.getNatives(profile.getCanonicalGameDirFile());
+ }
+
+ @Override
+ public File getMinecraftJar() {
+ return profile.getSelectedMinecraftVersion().getJar(baseFolder);
+ }
+
+ @Override
+ public IMinecraftLoader provideMinecraftLoader(UserProfileProvider p, DownloadType type)
+ throws IllegalStateException {
+ return new MinecraftLoader(profile, this, p, getMinecraftJar(), type);
+ }
+
+ @Override
+ public MinecraftVersion getOneVersion() {
+ return versions.isEmpty() ? null : versions.values().iterator().next();
+ }
+
+ @Override
+ public MinecraftVersion getVersionById(String id) {
+ return id == null ? null : versions.get(id);
+ }
+
+ @Override
+ public File getResourcePacks() {
+ return new File(profile.getCanonicalGameDirFile(), "resourcepacks");
+ }
+
+ @Override
+ public File getBaseFolder() {
+ return baseFolder;
+ }
+
+ @Override
+ public void onLaunch() {
+ File resourcePacks = getResourcePacks();
+ if (!resourcePacks.exists()) resourcePacks.mkdirs();
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Natives.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Natives.java
new file mode 100644
index 000000000..c2a2bf353
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Natives.java
@@ -0,0 +1,22 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+/**
+ *
+ * @author hyh
+ */
+public class Natives implements Cloneable {
+ public String windows, osx, linux;
+
+ @Override
+ protected Object clone() {
+ Natives n = new Natives();
+ n.windows = windows;
+ n.osx = osx;
+ n.linux = linux;
+ return n;
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/OS.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/OS.java
new file mode 100644
index 000000000..0bc04d8fd
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/OS.java
@@ -0,0 +1,13 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+/**
+ *
+ * @author hyh
+ */
+public class OS {
+ public String version, name;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Rules.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Rules.java
new file mode 100644
index 000000000..4d346f317
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/utils/version/Rules.java
@@ -0,0 +1,14 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.utils.version;
+
+/**
+ *
+ * @author hyh
+ */
+public class Rules {
+ public String action;
+ public OS os;
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/DraggableFrame.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/DraggableFrame.java
new file mode 100644
index 000000000..8b09d92b4
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/DraggableFrame.java
@@ -0,0 +1,63 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.views;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import javax.swing.JFrame;
+
+/**
+ *
+ * @author hyh
+ */
+public class DraggableFrame extends JFrame
+ implements MouseListener, MouseMotionListener {
+
+ private int dragGripX;
+ private int dragGripY;
+
+ public DraggableFrame() {
+ setUndecorated(true);
+ addMouseListener(this);
+ addMouseMotionListener(this);
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (e.getButton() == 1) {
+ this.dragGripX = e.getX();
+ this.dragGripY = e.getY();
+ }
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ if ((e.getModifiersEx() & 0x400) != 0) {
+ setLocation(e.getXOnScreen() - this.dragGripX, e.getYOnScreen() - this.dragGripY);
+ }
+ }
+
+ @Override
+ public void mouseMoved(MouseEvent e) {
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/GameSettingsPanel.form b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/GameSettingsPanel.form
new file mode 100644
index 000000000..89290ee9e
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/GameSettingsPanel.form
@@ -0,0 +1,1073 @@
+
+
+
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/GameSettingsPanel.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/GameSettingsPanel.java
new file mode 100644
index 000000000..8c8de5d27
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/GameSettingsPanel.java
@@ -0,0 +1,1468 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.views;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.swing.JFileChooser;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+import javax.swing.table.DefaultTableModel;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.utils.MCUtils;
+import org.jackhuang.hellominecraft.launcher.utils.assets.IAssetsHandler;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList;
+import org.jackhuang.hellominecraft.launcher.utils.installers.InstallerVersionList.InstallerVersion;
+import org.jackhuang.hellominecraft.launcher.utils.installers.forge.ForgeInstaller;
+import org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderInstaller;
+import org.jackhuang.hellominecraft.launcher.utils.installers.liteloader.LiteLoaderVersionList.LiteLoaderInstallerVersion;
+import org.jackhuang.hellominecraft.launcher.utils.installers.optifine.OptiFineInstaller;
+import org.jackhuang.hellominecraft.launcher.utils.installers.optifine.vanilla.OptiFineDownloadFormatter;
+import org.jackhuang.hellominecraft.launcher.utils.download.DownloadType;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.launcher.utils.version.GameDirType;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+import org.jackhuang.hellominecraft.tasks.Task;
+import org.jackhuang.hellominecraft.tasks.TaskRunnableArg1;
+import org.jackhuang.hellominecraft.tasks.TaskWindow;
+import org.jackhuang.hellominecraft.tasks.communication.DefaultPreviousResult;
+import org.jackhuang.hellominecraft.tasks.download.FileDownloadTask;
+import org.jackhuang.hellominecraft.tasks.download.HTTPGetTask;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.MinecraftVersionRequest;
+import org.jackhuang.hellominecraft.utils.OS;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.SwingUtils;
+import org.jackhuang.hellominecraft.version.MinecraftRemoteVersion;
+import org.jackhuang.hellominecraft.version.MinecraftRemoteVersions;
+import org.jackhuang.hellominecraft.views.Selector;
+
+/**
+ *
+ * @author hyh
+ */
+public class GameSettingsPanel extends javax.swing.JPanel {
+
+ /**
+ * Creates new form GameSettingsPanel
+ */
+ public GameSettingsPanel() {
+ initComponents();
+ setBackground(Color.white);
+ setOpaque(true);
+
+ forge = new InstallerHelper(lstForge, "forge");
+ liteloader = new InstallerHelper(lstLiteLoader, "liteloader");
+ optifine = new InstallerHelper(lstOptifine, "optifine");
+ //
+ ppmExplore = new JPopupMenu();
+ class ImplementedActionListener implements ActionListener {
+
+ ImplementedActionListener(String s) {
+ a = s;
+ }
+ String a;
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Profile v = getProfile();
+ if (v != null)
+ v.getMinecraftProvider().open(mcVersion, a);
+ }
+ }
+ JMenuItem itm;
+ itm = new JMenuItem(C.i18n("folder.game"));
+ itm.addActionListener((e) -> {
+ Profile v = getProfile();
+ if (v != null) v.getMinecraftProvider().openSelf(mcVersion);
+ });
+ ppmExplore.add(itm);
+ itm = new JMenuItem(C.i18n("folder.mod"));
+ itm.addActionListener(new ImplementedActionListener("mods"));
+ ppmExplore.add(itm);
+ itm = new JMenuItem(C.i18n("folder.coremod"));
+ itm.addActionListener(new ImplementedActionListener("coremods"));
+ ppmExplore.add(itm);
+ itm = new JMenuItem(C.i18n("folder.config"));
+ itm.addActionListener(new ImplementedActionListener("config"));
+ ppmExplore.add(itm);
+ itm = new JMenuItem(C.i18n("folder.resourcepacks"));
+ itm.addActionListener(new ImplementedActionListener("resourcepacks"));
+ ppmExplore.add(itm);
+ itm = new JMenuItem(C.i18n("folder.screenshots"));
+ itm.addActionListener(new ImplementedActionListener("screenshots"));
+ ppmExplore.add(itm);
+ //
+ //
+ ppmManage = new JPopupMenu();
+ itm = new JMenuItem(C.i18n("versions.manage.rename"));
+ itm.addActionListener((e) -> {
+ Profile v = getProfile();
+ if (v != null && mcVersion != null) {
+ String newName = JOptionPane.showInputDialog(C.i18n("versions.manage.rename.message"), mcVersion);
+ if (newName != null)
+ if (v.getMinecraftProvider().renameVersion(mcVersion, newName))
+ refreshVersions();
+ }
+ });
+ ppmManage.add(itm);
+ itm = new JMenuItem(C.i18n("versions.manage.remove"));
+ itm.addActionListener((e) -> {
+ Profile v = getProfile();
+ if (v != null && mcVersion != null && MessageBox.Show(C.i18n("versions.manage.remove.confirm") + mcVersion, MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
+ if (v.getMinecraftProvider().removeVersionFromDisk(mcVersion))
+ refreshVersions();
+ });
+ ppmManage.add(itm);
+ itm = new JMenuItem(C.i18n("versions.manage.redownload_json"));
+ itm.addActionListener((e) -> {
+ Profile v = getProfile();
+ if (v != null && mcVersion != null)
+ v.getMinecraftProvider().refreshJson(mcVersion);
+ });
+ ppmManage.add(itm);
+ itm = new JMenuItem(C.i18n("versions.manage.redownload_assets_index"));
+ itm.addActionListener((e) -> {
+ Profile v = getProfile();
+ if (v != null && mcVersion != null)
+ v.getMinecraftProvider().refreshAssetsIndex(mcVersion);
+ });
+ ppmManage.add(itm);
+ //
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jPanel1 = new javax.swing.JPanel();
+ jPanel4 = new javax.swing.JPanel();
+ jLabel1 = new javax.swing.JLabel();
+ cboProfiles = new javax.swing.JComboBox();
+ cboVersions = new javax.swing.JComboBox();
+ jLabel2 = new javax.swing.JLabel();
+ jPanel5 = new javax.swing.JPanel();
+ btnModify = new javax.swing.JButton();
+ btnRefreshVersions = new javax.swing.JButton();
+ txtMinecraftVersion = new javax.swing.JTextField();
+ btnNewProfile = new javax.swing.JButton();
+ btnRemoveProfile = new javax.swing.JButton();
+ btnExplore = new javax.swing.JButton();
+ tabVersionEdit = new javax.swing.JTabbedPane();
+ jPanel22 = new javax.swing.JPanel();
+ jLabel24 = new javax.swing.JLabel();
+ txtGameDir = new javax.swing.JTextField();
+ jLabel25 = new javax.swing.JLabel();
+ txtWidth = new javax.swing.JTextField();
+ txtHeight = new javax.swing.JTextField();
+ jLabel9 = new javax.swing.JLabel();
+ chkFullscreen = new javax.swing.JCheckBox();
+ txtJavaDir = new javax.swing.JTextField();
+ jLabel11 = new javax.swing.JLabel();
+ jLabel27 = new javax.swing.JLabel();
+ txtMaxMemory = new javax.swing.JTextField();
+ lblMaxMemory = new javax.swing.JLabel();
+ btnDownloadAllAssets = new javax.swing.JButton();
+ cboLauncherVisibility = new javax.swing.JComboBox();
+ jLabel10 = new javax.swing.JLabel();
+ jLabel12 = new javax.swing.JLabel();
+ cboGameDirType = new javax.swing.JComboBox();
+ jPanel2 = new javax.swing.JPanel();
+ chkDebug = new javax.swing.JCheckBox();
+ jLabel26 = new javax.swing.JLabel();
+ txtJavaArgs = new javax.swing.JTextField();
+ txtMinecraftArgs = new javax.swing.JTextField();
+ jLabel28 = new javax.swing.JLabel();
+ jLabel29 = new javax.swing.JLabel();
+ txtPermSize = new javax.swing.JTextField();
+ chkNoJVMArgs = new javax.swing.JCheckBox();
+ chkCancelWrapper = new javax.swing.JCheckBox();
+ jLabel30 = new javax.swing.JLabel();
+ txtWrapperLauncher = new javax.swing.JTextField();
+ jLabel31 = new javax.swing.JLabel();
+ txtServerIP = new javax.swing.JTextField();
+ pnlAutoInstall = new javax.swing.JPanel();
+ tabInstallers = new javax.swing.JTabbedPane();
+ jPanel16 = new javax.swing.JPanel();
+ jScrollPane11 = new javax.swing.JScrollPane();
+ lstForge = new javax.swing.JTable();
+ btnRefreshForge = new javax.swing.JButton();
+ btnDownloadForge = new javax.swing.JButton();
+ btnRetryForge = new javax.swing.JButton();
+ pnlOptifine = new javax.swing.JPanel();
+ jScrollPane13 = new javax.swing.JScrollPane();
+ lstOptifine = new javax.swing.JTable();
+ btnRefreshOptifine = new javax.swing.JButton();
+ btnDownloadOptifine = new javax.swing.JButton();
+ jPanel3 = new javax.swing.JPanel();
+ btnInstallLiteLoader = new javax.swing.JButton();
+ jScrollPane12 = new javax.swing.JScrollPane();
+ lstLiteLoader = new javax.swing.JTable();
+ btnRefreshLiteLoader = new javax.swing.JButton();
+ btnRetryLiteLoader = new javax.swing.JButton();
+ pnlGameDownloads = new javax.swing.JPanel();
+ btnDownload = new javax.swing.JButton();
+ jScrollPane2 = new javax.swing.JScrollPane();
+ lstDownloads = new javax.swing.JTable();
+ btnRefreshGameDownloads = new javax.swing.JButton();
+ btnIncludeMinecraft = new javax.swing.JButton();
+
+ setBackground(new java.awt.Color(255, 255, 255));
+
+ java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/jackhuang/hellominecraft/launcher/I18N"); // NOI18N
+ jLabel1.setText(bundle.getString("ui.label.profile")); // NOI18N
+
+ cboProfiles.setMinimumSize(new java.awt.Dimension(32, 23));
+ cboProfiles.setPreferredSize(new java.awt.Dimension(32, 23));
+ cboProfiles.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent evt) {
+ cboProfilesItemStateChanged(evt);
+ }
+ });
+
+ cboVersions.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent evt) {
+ cboVersionsItemStateChanged(evt);
+ }
+ });
+
+ jLabel2.setText(bundle.getString("ui.label.version")); // NOI18N
+
+ javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
+ jPanel4.setLayout(jPanel4Layout);
+ jPanel4Layout.setHorizontalGroup(
+ jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addComponent(jLabel1)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(cboProfiles, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addComponent(jLabel2)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(cboVersions, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+ );
+ jPanel4Layout.setVerticalGroup(
+ jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel4Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cboProfiles, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel1))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cboVersions, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel2))
+ .addContainerGap(11, Short.MAX_VALUE))
+ );
+
+ btnModify.setText(bundle.getString("settings.manage")); // NOI18N
+ btnModify.addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mouseClicked(java.awt.event.MouseEvent evt) {
+ btnModifyMouseClicked(evt);
+ }
+ });
+
+ btnRefreshVersions.setText(bundle.getString("ui.button.refresh")); // NOI18N
+ btnRefreshVersions.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRefreshVersionsActionPerformed(evt);
+ }
+ });
+
+ txtMinecraftVersion.setEditable(false);
+
+ btnNewProfile.setText(bundle.getString("setupwindow.new")); // NOI18N
+ btnNewProfile.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnNewProfileActionPerformed(evt);
+ }
+ });
+
+ btnRemoveProfile.setText(bundle.getString("ui.button.delete")); // NOI18N
+ btnRemoveProfile.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRemoveProfileActionPerformed(evt);
+ }
+ });
+
+ btnExplore.setText(bundle.getString("settings.explore")); // NOI18N
+ btnExplore.setToolTipText("");
+ btnExplore.addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mouseClicked(java.awt.event.MouseEvent evt) {
+ btnExploreMouseClicked(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
+ jPanel5.setLayout(jPanel5Layout);
+ jPanel5Layout.setHorizontalGroup(
+ jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addGap(0, 0, 0)
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btnNewProfile, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(txtMinecraftVersion))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btnRemoveProfile, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnRefreshVersions, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btnModify, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnExplore, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ jPanel5Layout.setVerticalGroup(
+ jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel5Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(btnNewProfile, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(btnRemoveProfile, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(btnExplore, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(txtMinecraftVersion, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(btnRefreshVersions, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(btnModify, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGap(0, 0, Short.MAX_VALUE))
+ );
+
+ tabVersionEdit.setName("tabVersionEdit"); // NOI18N
+
+ jLabel24.setText(bundle.getString("settings.game_directory")); // NOI18N
+
+ txtGameDir.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtGameDirFocusLost(evt);
+ }
+ });
+
+ jLabel25.setText(bundle.getString("settings.dimension")); // NOI18N
+
+ txtWidth.setToolTipText("");
+ txtWidth.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtWidthFocusLost(evt);
+ }
+ });
+
+ txtHeight.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtHeightFocusLost(evt);
+ }
+ });
+
+ jLabel9.setText("x");
+
+ chkFullscreen.setText(bundle.getString("settings.fullscreen")); // NOI18N
+ chkFullscreen.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ chkFullscreenFocusLost(evt);
+ }
+ });
+
+ txtJavaDir.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtJavaDirFocusLost(evt);
+ }
+ });
+ txtJavaDir.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtJavaDirActionPerformed(evt);
+ }
+ });
+
+ jLabel11.setText(bundle.getString("settings.java_dir")); // NOI18N
+
+ jLabel27.setText(bundle.getString("settings.max_memory")); // NOI18N
+
+ txtMaxMemory.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtMaxMemoryFocusLost(evt);
+ }
+ });
+
+ lblMaxMemory.setText(C.i18n("settings.physical_memory") + ": " + OS.getTotalPhysicalMemory() / 1024 / 1024 + "MB");
+
+ btnDownloadAllAssets.setText(bundle.getString("assets.download_all")); // NOI18N
+ btnDownloadAllAssets.setToolTipText("");
+ btnDownloadAllAssets.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnDownloadAllAssetsActionPerformed(evt);
+ }
+ });
+
+ cboLauncherVisibility.setModel(new javax.swing.DefaultComboBoxModel(new String[] { C.I18N.getString("advancedsettings.launcher_visibility.close"), C.I18N.getString("advancedsettings.launcher_visibility.hide"), C.I18N.getString("advancedsettings.launcher_visibility.keep") }));
+ cboLauncherVisibility.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ cboLauncherVisibilityFocusLost(evt);
+ }
+ });
+
+ jLabel10.setText(bundle.getString("advancedsettings.launcher_visible")); // NOI18N
+
+ jLabel12.setText(bundle.getString("advancedsettings.run_directory")); // NOI18N
+
+ cboGameDirType.setModel(new javax.swing.DefaultComboBoxModel(new String[] { C.I18N.getString("advancedsettings.game_dir.default"), C.I18N.getString("advancedsettings.game_dir.independent") }));
+ cboGameDirType.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ cboGameDirTypeFocusLost(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel22Layout = new javax.swing.GroupLayout(jPanel22);
+ jPanel22.setLayout(jPanel22Layout);
+ jPanel22Layout.setHorizontalGroup(
+ jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel22Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel22Layout.createSequentialGroup()
+ .addComponent(btnDownloadAllAssets)
+ .addGap(0, 0, Short.MAX_VALUE))
+ .addGroup(jPanel22Layout.createSequentialGroup()
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel11)
+ .addComponent(jLabel27)
+ .addComponent(jLabel24)
+ .addComponent(jLabel12)
+ .addComponent(jLabel10)
+ .addComponent(jLabel25))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(cboGameDirType, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(cboLauncherVisibility, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(txtJavaDir, javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(jPanel22Layout.createSequentialGroup()
+ .addComponent(txtMaxMemory)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(lblMaxMemory))
+ .addComponent(txtGameDir)
+ .addGroup(jPanel22Layout.createSequentialGroup()
+ .addComponent(txtWidth, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel9)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(txtHeight, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 306, Short.MAX_VALUE)
+ .addComponent(chkFullscreen)))))
+ .addContainerGap())
+ );
+ jPanel22Layout.setVerticalGroup(
+ jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel22Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(txtGameDir, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel24))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(txtJavaDir, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel11))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(lblMaxMemory)
+ .addComponent(txtMaxMemory, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel27))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cboLauncherVisibility, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel10))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cboGameDirType, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel12))
+ .addGap(4, 4, 4)
+ .addGroup(jPanel22Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(txtHeight, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(chkFullscreen, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel9, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel25)
+ .addComponent(txtWidth, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 92, Short.MAX_VALUE)
+ .addComponent(btnDownloadAllAssets)
+ .addContainerGap())
+ );
+
+ tabVersionEdit.addTab(bundle.getString("settings"), jPanel22); // NOI18N
+
+ chkDebug.setText(bundle.getString("advencedsettings.debug_mode")); // NOI18N
+ chkDebug.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ chkDebugFocusLost(evt);
+ }
+ });
+
+ jLabel26.setText(bundle.getString("advancedsettings.jvm_args")); // NOI18N
+
+ txtJavaArgs.setToolTipText(bundle.getString("advancedsettings.java_args_default")); // NOI18N
+ txtJavaArgs.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtJavaArgsFocusLost(evt);
+ }
+ });
+
+ txtMinecraftArgs.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtMinecraftArgsFocusLost(evt);
+ }
+ });
+
+ jLabel28.setText(bundle.getString("advancedsettings.Minecraft_arguments")); // NOI18N
+
+ jLabel29.setText(bundle.getString("advancedsettings.java_permanent_generation_space")); // NOI18N
+
+ txtPermSize.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtPermSizeFocusLost(evt);
+ }
+ });
+
+ chkNoJVMArgs.setText(bundle.getString("advancedsettings.no_jvm_args")); // NOI18N
+ chkNoJVMArgs.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ chkNoJVMArgsFocusLost(evt);
+ }
+ });
+
+ chkCancelWrapper.setText("鍙栨秷鍖呰9鍚姩鍣紙鍑虹幇濂囨棶棰樻椂鍙皾璇曚娇鐢,涓庤皟璇曟ā寮忓啿绐侊級");
+ chkCancelWrapper.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ chkCancelWrapperFocusLost(evt);
+ }
+ });
+
+ jLabel30.setText(bundle.getString("advancedsettings.wrapper_launcher")); // NOI18N
+
+ txtWrapperLauncher.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtWrapperLauncherFocusLost(evt);
+ }
+ });
+
+ jLabel31.setText(bundle.getString("advancedsettings.server_ip")); // NOI18N
+
+ txtServerIP.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtServerIPFocusLost(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
+ jPanel2.setLayout(jPanel2Layout);
+ jPanel2Layout.setHorizontalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(txtWrapperLauncher)
+ .addComponent(txtServerIP)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel30)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addComponent(chkDebug)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(chkCancelWrapper)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(chkNoJVMArgs))
+ .addComponent(jLabel31))
+ .addGap(0, 0, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel28)
+ .addComponent(jLabel29)
+ .addComponent(jLabel26))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(txtJavaArgs)
+ .addComponent(txtMinecraftArgs)
+ .addComponent(txtPermSize, javax.swing.GroupLayout.Alignment.TRAILING))))
+ .addContainerGap())
+ );
+ jPanel2Layout.setVerticalGroup(
+ jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel2Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(txtJavaArgs, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel26))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(txtMinecraftArgs, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel28))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(txtPermSize, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel29))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel30)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(txtWrapperLauncher, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jLabel31)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(txtServerIP, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 85, Short.MAX_VALUE)
+ .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(chkDebug)
+ .addComponent(chkNoJVMArgs)
+ .addComponent(chkCancelWrapper))
+ .addContainerGap())
+ );
+
+ tabVersionEdit.addTab(bundle.getString("advancedsettings"), jPanel2); // NOI18N
+
+ lstForge.setModel(SwingUtils.makeDefaultTableModel(new String[]{C.I18N.getString("install.version"), C.I18N.getString("install.mcversion")},
+ new Class[]{String.class, String.class}, new boolean[]{false, false}));
+ lstForge.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+ jScrollPane11.setViewportView(lstForge);
+
+ btnRefreshForge.setText(bundle.getString("ui.button.refresh")); // NOI18N
+ btnRefreshForge.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRefreshForgeActionPerformed(evt);
+ }
+ });
+
+ btnDownloadForge.setText(bundle.getString("ui.button.install")); // NOI18N
+ btnDownloadForge.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnDownloadForgeActionPerformed(evt);
+ }
+ });
+
+ btnRetryForge.setText(bundle.getString("ui.button.retry")); // NOI18N
+ btnRetryForge.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRetryForgeActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel16Layout = new javax.swing.GroupLayout(jPanel16);
+ jPanel16.setLayout(jPanel16Layout);
+ jPanel16Layout.setHorizontalGroup(
+ jPanel16Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel16Layout.createSequentialGroup()
+ .addComponent(jScrollPane11, javax.swing.GroupLayout.DEFAULT_SIZE, 587, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel16Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btnRetryForge, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnDownloadForge, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnRefreshForge, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+ jPanel16Layout.setVerticalGroup(
+ jPanel16Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jScrollPane11, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
+ .addGroup(jPanel16Layout.createSequentialGroup()
+ .addComponent(btnDownloadForge)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnRetryForge)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnRefreshForge)
+ .addGap(0, 210, Short.MAX_VALUE))
+ );
+
+ tabInstallers.addTab("Forge", jPanel16);
+
+ lstOptifine.setModel(SwingUtils.makeDefaultTableModel(new String[]{C.I18N.getString("install.version"), C.I18N.getString("install.mcversion")},
+ new Class[]{String.class, String.class}, new boolean[]{false, false}));
+lstOptifine.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+jScrollPane13.setViewportView(lstOptifine);
+
+btnRefreshOptifine.setText(bundle.getString("ui.button.refresh")); // NOI18N
+btnRefreshOptifine.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRefreshOptifineActionPerformed(evt);
+ }
+ });
+
+ btnDownloadOptifine.setText(bundle.getString("ui.button.install")); // NOI18N
+ btnDownloadOptifine.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnDownloadOptifineActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout pnlOptifineLayout = new javax.swing.GroupLayout(pnlOptifine);
+ pnlOptifine.setLayout(pnlOptifineLayout);
+ pnlOptifineLayout.setHorizontalGroup(
+ pnlOptifineLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(pnlOptifineLayout.createSequentialGroup()
+ .addComponent(jScrollPane13, javax.swing.GroupLayout.DEFAULT_SIZE, 587, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(pnlOptifineLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btnDownloadOptifine, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnRefreshOptifine))
+ .addContainerGap())
+ );
+ pnlOptifineLayout.setVerticalGroup(
+ pnlOptifineLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jScrollPane13, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
+ .addGroup(pnlOptifineLayout.createSequentialGroup()
+ .addComponent(btnDownloadOptifine)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnRefreshOptifine)
+ .addGap(0, 239, Short.MAX_VALUE))
+ );
+
+ tabInstallers.addTab("OptiFine", pnlOptifine);
+
+ btnInstallLiteLoader.setText(bundle.getString("ui.button.install")); // NOI18N
+ btnInstallLiteLoader.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnInstallLiteLoaderActionPerformed(evt);
+ }
+ });
+
+ lstLiteLoader.setModel(SwingUtils.makeDefaultTableModel(new String[]{C.I18N.getString("install.version"), C.I18N.getString("install.mcversion")},
+ new Class[]{String.class, String.class}, new boolean[]{false, false}));
+lstLiteLoader.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+jScrollPane12.setViewportView(lstLiteLoader);
+
+btnRefreshLiteLoader.setText(bundle.getString("ui.button.refresh")); // NOI18N
+btnRefreshLiteLoader.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRefreshLiteLoaderActionPerformed(evt);
+ }
+ });
+
+ btnRetryLiteLoader.setText(bundle.getString("ui.button.retry")); // NOI18N
+ btnRetryLiteLoader.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRetryLiteLoaderActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
+ jPanel3.setLayout(jPanel3Layout);
+ jPanel3Layout.setHorizontalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addComponent(jScrollPane12, javax.swing.GroupLayout.DEFAULT_SIZE, 587, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btnInstallLiteLoader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnRetryLiteLoader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnRefreshLiteLoader))
+ .addContainerGap())
+ );
+ jPanel3Layout.setVerticalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jScrollPane12, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
+ .addGroup(jPanel3Layout.createSequentialGroup()
+ .addComponent(btnInstallLiteLoader)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnRetryLiteLoader)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnRefreshLiteLoader)
+ .addGap(0, 210, Short.MAX_VALUE))
+ );
+
+ tabInstallers.addTab("LiteLoader", jPanel3);
+
+ javax.swing.GroupLayout pnlAutoInstallLayout = new javax.swing.GroupLayout(pnlAutoInstall);
+ pnlAutoInstall.setLayout(pnlAutoInstallLayout);
+ pnlAutoInstallLayout.setHorizontalGroup(
+ pnlAutoInstallLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(tabInstallers)
+ );
+ pnlAutoInstallLayout.setVerticalGroup(
+ pnlAutoInstallLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(tabInstallers)
+ );
+
+ tabVersionEdit.addTab(bundle.getString("settings.tabs.installers"), pnlAutoInstall); // NOI18N
+
+ btnDownload.setText(bundle.getString("ui.button.download")); // NOI18N
+ btnDownload.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnDownloadActionPerformed(evt);
+ }
+ });
+
+ lstDownloads.setModel(SwingUtils.makeDefaultTableModel(new String[]{C.I18N.getString("install.version"), C.I18N.getString("install.time"), C.I18N.getString("install.type")},new Class[]{String.class, String.class, String.class}, new boolean[]{false, false, false}));
+ lstDownloads.setToolTipText("");
+ lstDownloads.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+ jScrollPane2.setViewportView(lstDownloads);
+
+ btnRefreshGameDownloads.setText(bundle.getString("ui.button.refresh")); // NOI18N
+ btnRefreshGameDownloads.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnRefreshGameDownloadsActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout pnlGameDownloadsLayout = new javax.swing.GroupLayout(pnlGameDownloads);
+ pnlGameDownloads.setLayout(pnlGameDownloadsLayout);
+ pnlGameDownloadsLayout.setHorizontalGroup(
+ pnlGameDownloadsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(pnlGameDownloadsLayout.createSequentialGroup()
+ .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 592, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(pnlGameDownloadsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(btnRefreshGameDownloads, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnDownload))
+ .addContainerGap())
+ );
+ pnlGameDownloadsLayout.setVerticalGroup(
+ pnlGameDownloadsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(pnlGameDownloadsLayout.createSequentialGroup()
+ .addComponent(btnRefreshGameDownloads)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnDownload))
+ .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 320, Short.MAX_VALUE)
+ );
+
+ tabVersionEdit.addTab(bundle.getString("settings.tabs.game_download"), pnlGameDownloads); // NOI18N
+
+ btnIncludeMinecraft.setText(bundle.getString("setupwindow.include_minecraft")); // NOI18N
+ btnIncludeMinecraft.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnIncludeMinecraftActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(btnIncludeMinecraft)
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addComponent(tabVersionEdit)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(tabVersionEdit)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnIncludeMinecraft)
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+ //
+ private void cboProfilesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboProfilesItemStateChanged
+ if (isLoading) return;
+ profile = getProfile();
+ if (profile.getMinecraftProvider().getVersionCount() <= 0)
+ versionChanged(profile, null);
+ prepare(profile);
+ }//GEN-LAST:event_cboProfilesItemStateChanged
+
+ private void btnNewProfileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewProfileActionPerformed
+ NewProfileWindow window = new NewProfileWindow(null);
+ window.setVisible(true);
+ loadProfiles();
+ }//GEN-LAST:event_btnNewProfileActionPerformed
+
+ private void btnRemoveProfileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoveProfileActionPerformed
+ if (profile == null) return;
+ if ("Default".equals(profile.getName())) {
+ MessageBox.Show(C.i18n("settings.cannot_remove_default_config"));
+ return;
+ }
+ if (MessageBox.Show(C.i18n("ui.message.sure_remove", profile.getName()), MessageBox.YES_NO_OPTION) == MessageBox.NO_OPTION) return;
+ cboProfiles.removeItem(profile.getName());
+ Settings.delVersion(profile);
+ profile = Settings.getOneProfile();
+ if (profile != null) {
+ prepare(profile);
+ loadVersions();
+ }
+ }//GEN-LAST:event_btnRemoveProfileActionPerformed
+
+ private void cboVersionsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboVersionsItemStateChanged
+ if (isLoading || cboVersions.getSelectedIndex() < 0 || StrUtils.isBlank((String) cboVersions.getSelectedItem()) || getProfile() == null) return;
+ loadMinecraftVersion((String) cboVersions.getSelectedItem());
+ versionChanged(getProfile(), (String) cboVersions.getSelectedItem());
+ }//GEN-LAST:event_cboVersionsItemStateChanged
+
+ private void btnRefreshVersionsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRefreshVersionsActionPerformed
+ refreshVersions();
+ }//GEN-LAST:event_btnRefreshVersionsActionPerformed
+
+ private void btnRefreshForgeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRefreshForgeActionPerformed
+ forge.refreshVersions();//refreshForgeVersions();
+ }//GEN-LAST:event_btnRefreshForgeActionPerformed
+
+ private void btnDownloadForgeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDownloadForgeActionPerformed
+ int idx = lstForge.getSelectedRow();
+ if (idx == -1) {
+ MessageBox.Show(C.i18n("install.not_refreshed"));
+ return;
+ }
+ InstallerVersion v = forge.getVersion(idx);//forgeVersions.get(idx);
+ String url;
+ File filepath = IOUtils.tryGetCanonicalFile(IOUtils.currentDirWithSeparator() + "forge-installer.jar");
+ if (v.installer != null) {
+ url = v.installer;
+ TaskWindow.getInstance()
+ .addTask(new FileDownloadTask(url, filepath).setTag("forge"))
+ .addTask(new ForgeInstaller(profile.getMinecraftProvider(), filepath))
+ .start();
+ }
+ }//GEN-LAST:event_btnDownloadForgeActionPerformed
+
+ private void btnRetryForgeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRetryForgeActionPerformed
+ if (profile == null) return;
+ MinecraftVersion v = profile.getMinecraftProvider().getVersionById(mcVersion);
+ if (v == null) return;
+ TaskWindow.getInstance().addTask(new ForgeInstaller(profile.getMinecraftProvider(), IOUtils.tryGetCanonicalFile(IOUtils.currentDirWithSeparator() + "forge-installer.jar"))).start();
+ }//GEN-LAST:event_btnRetryForgeActionPerformed
+
+ private void btnRefreshOptifineActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRefreshOptifineActionPerformed
+ optifine.refreshVersions();
+ }//GEN-LAST:event_btnRefreshOptifineActionPerformed
+
+ private void btnDownloadOptifineActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDownloadOptifineActionPerformed
+ int idx = lstOptifine.getSelectedRow();
+ if (idx == -1) {
+ MessageBox.Show(C.i18n("install.not_refreshed"));
+ return;
+ }
+ InstallerVersion v = optifine.getVersion(idx);
+ File filepath = IOUtils.tryGetCanonicalFile(IOUtils.currentDirWithSeparator() + "optifine-installer.jar");
+ if (v.installer != null) {
+ OptiFineDownloadFormatter task = new OptiFineDownloadFormatter(v.installer);
+ TaskWindow.getInstance().addTask(task)
+ .addTask(new FileDownloadTask(filepath).registerPreviousResult(task).setTag("optifine"))
+ .addTask(new OptiFineInstaller(profile, v.selfVersion, filepath))
+ .start();
+ }
+ }//GEN-LAST:event_btnDownloadOptifineActionPerformed
+
+ private void btnInstallLiteLoaderActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnInstallLiteLoaderActionPerformed
+ int idx = lstLiteLoader.getSelectedRow();
+ if (idx == -1) {
+ MessageBox.Show(C.i18n("install.not_refreshed"));
+ return;
+ }
+ InstallerVersion v = liteloader.getVersion(idx);
+ String url;
+ File filepath = IOUtils.tryGetCanonicalFile(IOUtils.currentDirWithSeparator() + "liteloader-universal.jar");
+ url = v.universal;
+ FileDownloadTask task = (FileDownloadTask) new FileDownloadTask(url, filepath).setTag("LiteLoader");
+ TaskWindow.getInstance()
+ .addTask(task).addTask(new LiteLoaderInstaller(profile, (LiteLoaderInstallerVersion) v).registerPreviousResult(task))
+ .start();
+ }//GEN-LAST:event_btnInstallLiteLoaderActionPerformed
+
+ private void btnRefreshLiteLoaderActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRefreshLiteLoaderActionPerformed
+ liteloader.refreshVersions();
+ }//GEN-LAST:event_btnRefreshLiteLoaderActionPerformed
+
+ private void btnRetryLiteLoaderActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRetryLiteLoaderActionPerformed
+ if (profile == null) return;
+ int idx = lstLiteLoader.getSelectedRow();
+ if (idx == -1) return;
+ InstallerVersion v = liteloader.getVersion(idx);
+ File filepath = new File(IOUtils.currentDir(), "liteloader-universal.jar");
+ TaskWindow.getInstance().addTask(new LiteLoaderInstaller(profile, (LiteLoaderInstallerVersion) v, filepath)).start();
+ }//GEN-LAST:event_btnRetryLiteLoaderActionPerformed
+
+ private void btnDownloadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDownloadActionPerformed
+ downloadMinecraft(Settings.s().getDownloadSource());
+ refreshVersions();
+ }//GEN-LAST:event_btnDownloadActionPerformed
+
+ private void btnRefreshGameDownloadsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRefreshGameDownloadsActionPerformed
+ refreshDownloads(Settings.s().getDownloadSource());
+ }//GEN-LAST:event_btnRefreshGameDownloadsActionPerformed
+
+ private void btnExploreMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_btnExploreMouseClicked
+ ppmExplore.show(evt.getComponent(), evt.getPoint().x, evt.getPoint().y);
+ }//GEN-LAST:event_btnExploreMouseClicked
+
+ private void btnIncludeMinecraftActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnIncludeMinecraftActionPerformed
+ JFileChooser fc = new JFileChooser(IOUtils.currentDir());
+ fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ int ret = fc.showOpenDialog(this);
+ if (ret == JFileChooser.APPROVE_OPTION) {
+ File newGameDir = fc.getSelectedFile();
+ String name = JOptionPane.showInputDialog(C.i18n("setupwindow.give_a_name"));
+ if (StrUtils.isBlank(name)) {
+ MessageBox.Show(C.i18n("setupwindow.no_empty_name"));
+ return;
+ }
+ Settings.trySetVersion(new Profile(name).setGameDir(newGameDir.getAbsolutePath()));
+ MessageBox.Show(C.i18n("setupwindow.find_in_configurations"));
+ loadProfiles();
+ }
+ }//GEN-LAST:event_btnIncludeMinecraftActionPerformed
+
+ private void btnModifyMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_btnModifyMouseClicked
+ ppmManage.show(evt.getComponent(), evt.getPoint().x, evt.getPoint().y);
+ }//GEN-LAST:event_btnModifyMouseClicked
+
+ private void txtJavaArgsFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtJavaArgsFocusLost
+ profile.setJavaArgs(txtJavaArgs.getText());
+ }//GEN-LAST:event_txtJavaArgsFocusLost
+
+ private void txtMinecraftArgsFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtMinecraftArgsFocusLost
+ profile.setMinecraftArgs(txtMinecraftArgs.getText());
+ }//GEN-LAST:event_txtMinecraftArgsFocusLost
+
+ private void txtPermSizeFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtPermSizeFocusLost
+ profile.setPermSize(txtPermSize.getText());
+ }//GEN-LAST:event_txtPermSizeFocusLost
+
+ private void chkDebugFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_chkDebugFocusLost
+ profile.setDebug(chkDebug.isSelected());
+ }//GEN-LAST:event_chkDebugFocusLost
+
+ private void chkNoJVMArgsFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_chkNoJVMArgsFocusLost
+ profile.setNoJVMArgs(chkNoJVMArgs.isSelected());
+ }//GEN-LAST:event_chkNoJVMArgsFocusLost
+
+ private void chkCancelWrapperFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_chkCancelWrapperFocusLost
+ profile.setCanceledWrapper(chkCancelWrapper.isSelected());
+ }//GEN-LAST:event_chkCancelWrapperFocusLost
+
+ private void txtWrapperLauncherFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtWrapperLauncherFocusLost
+ profile.setWrapperLauncher(txtWrapperLauncher.getText());
+ }//GEN-LAST:event_txtWrapperLauncherFocusLost
+
+ private void txtServerIPFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtServerIPFocusLost
+ profile.setServerIp(txtServerIP.getText());
+ }//GEN-LAST:event_txtServerIPFocusLost
+
+ private void cboGameDirTypeFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_cboGameDirTypeFocusLost
+ if (cboGameDirType.getSelectedIndex() >= 0)
+ profile.setGameDirType(GameDirType.values()[cboGameDirType.getSelectedIndex()]);
+ }//GEN-LAST:event_cboGameDirTypeFocusLost
+
+ private void cboLauncherVisibilityFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_cboLauncherVisibilityFocusLost
+ if (cboLauncherVisibility.getSelectedIndex() >= 0)
+ profile.setLauncherVisibility(cboLauncherVisibility.getSelectedIndex());
+ }//GEN-LAST:event_cboLauncherVisibilityFocusLost
+
+ private void btnDownloadAllAssetsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDownloadAllAssetsActionPerformed
+ boolean flag = false;
+ ArrayList al = new ArrayList<>();
+ if (minecraftVersion == null) {
+ MessageBox.Show(C.i18n("mainwindow.no_version"));
+ return;
+ }
+ String s = StrUtils.formatVersion(minecraftVersion.version);
+ for (IAssetsHandler a : IAssetsHandler.getAssetsHandlers()) {
+ if (a.isVersionAllowed(s)) {
+ downloadAssets(a);
+ return;
+ }
+ al.add(a.getName());
+ }
+ if (!flag) {
+ Selector selector = new Selector(MainFrame.instance, al.toArray(new String[0]), C.i18n("assets.unkown_type_select_one", mcVersion));
+ selector.setVisible(true);
+ if (selector.sel != -1)
+ downloadAssets(IAssetsHandler.getAssetsHandler(selector.sel));
+ }
+ }//GEN-LAST:event_btnDownloadAllAssetsActionPerformed
+
+ private void txtMaxMemoryFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtMaxMemoryFocusLost
+ profile.setMaxMemory(txtMaxMemory.getText());
+ }//GEN-LAST:event_txtMaxMemoryFocusLost
+
+ private void txtJavaDirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtJavaDirActionPerformed
+ // TODO add your handling code here:
+ }//GEN-LAST:event_txtJavaDirActionPerformed
+
+ private void txtJavaDirFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtJavaDirFocusLost
+ profile.setJavaDir(txtJavaDir.getText());
+ }//GEN-LAST:event_txtJavaDirFocusLost
+
+ private void chkFullscreenFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_chkFullscreenFocusLost
+ profile.setFullscreen(chkFullscreen.isSelected());
+ }//GEN-LAST:event_chkFullscreenFocusLost
+
+ private void txtHeightFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtHeightFocusLost
+ profile.setHeight(txtHeight.getText());
+ }//GEN-LAST:event_txtHeightFocusLost
+
+ private void txtWidthFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtWidthFocusLost
+ profile.setWidth(txtWidth.getText());
+ }//GEN-LAST:event_txtWidthFocusLost
+
+ private void txtGameDirFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtGameDirFocusLost
+ if (profile == null) return;
+ profile.setGameDir(txtGameDir.getText());
+ loadVersions();
+ }//GEN-LAST:event_txtGameDirFocusLost
+
+ //
+ //
+ private void loadProfiles() {
+ isLoading = true;
+ cboProfiles.removeAllItems();
+ Profile firstProfile = null, selectedProfile = null;
+ int index = 0, i = 0;
+ for (Profile s : Settings.getProfiles()) {
+ if (firstProfile == null) firstProfile = s;
+ cboProfiles.addItem(s.getName());
+ if (Settings.s().getLast() != null && Settings.s().getLast().equals(s.getName())) {
+ index = i;
+ selectedProfile = s;
+ }
+ i++;
+ }
+ if (selectedProfile == null) selectedProfile = Settings.getOneProfile();
+
+ isLoading = false;
+ if (index < cboProfiles.getItemCount()) {
+ cboProfiles.setSelectedIndex(index);
+ profile = selectedProfile;
+ if (profile == null) profile = firstProfile;
+ prepare(profile);
+ loadVersions();
+ }
+ }
+
+ Profile getProfile() {
+ if (cboProfiles.getSelectedIndex() >= 0)
+ return Settings.getVersion(cboProfiles.getSelectedItem().toString());
+ else return null;
+ }
+
+ void prepare(Profile profile) {
+ if (profile == null) return;
+ txtWidth.setText(profile.getWidth());
+ txtHeight.setText(profile.getHeight());
+ txtMaxMemory.setText(profile.getMaxMemory());
+ txtPermSize.setText(profile.getPermSize());
+ txtGameDir.setText(profile.getGameDir());
+ txtJavaArgs.setText(profile.getJavaArgs());
+ txtMinecraftArgs.setText(profile.getMinecraftArgs());
+ txtJavaDir.setText(profile.getJavaDir());
+ txtWrapperLauncher.setText(profile.getWrapperLauncher());
+ txtServerIP.setText(profile.getServerIp());
+ chkDebug.setSelected(profile.isDebug());
+ chkNoJVMArgs.setSelected(profile.isNoJVMArgs());
+ chkFullscreen.setSelected(profile.isFullscreen());
+ chkCancelWrapper.setSelected(profile.isCanceledWrapper());
+ cboLauncherVisibility.setSelectedIndex(profile.getLauncherVisibility());
+ cboGameDirType.setSelectedIndex(profile.getGameDirType().ordinal());
+
+ loadVersions();
+ loadMinecraftVersion();
+ }
+
+ void loadVersions() {
+ if (profile == null) return;
+ isLoading = true;
+ cboVersions.removeAllItems();
+ int index = 0, i = 0;
+ MinecraftVersion selVersion = profile.getSelectedMinecraftVersion();
+ String selectedMC = selVersion == null ? null : selVersion.id;
+ for (MinecraftVersion each : profile.getMinecraftProvider().getVersions()) {
+ cboVersions.addItem(each.id);
+ if (each.id.equals(selectedMC)) index = i;
+ i++;
+ }
+ isLoading = false;
+ if (index < cboVersions.getItemCount()) cboVersions.setSelectedIndex(index);
+ }
+
+ void loadMinecraftVersion() {
+ loadMinecraftVersion(profile.getSelectedMinecraftVersion());
+ }
+
+ void loadMinecraftVersion(String v) {
+ loadMinecraftVersion(profile.getMinecraftProvider().getVersionById(v));
+ }
+
+ /**
+ * Anaylze the jar of selected minecraft version of current profile to get
+ * the version.
+ *
+ * @param v
+ */
+ void loadMinecraftVersion(MinecraftVersion v) {
+ txtMinecraftVersion.setText("");
+ if (v == null) return;
+ File minecraftJar = v.getJar(profile.getGameDirFile());
+ minecraftVersion = MCUtils.minecraftVersion(minecraftJar);
+ txtMinecraftVersion.setText(MinecraftVersionRequest.getResponse(minecraftVersion));
+ }
+ //
+ //
+ public int assetsType;
+
+ private void downloadAssets(final IAssetsHandler type) {
+ if (mcVersion == null || profile == null) return;
+ type.getList((value) -> {
+ if (value != null)
+ TaskWindow.getInstance().addTask(type.getDownloadTask(Settings.s().getDownloadSource().getProvider())).start();
+ });
+ }
+
+ //
+ //
+ public void refreshDownloads(final DownloadType provider) {
+ TaskWindow.getInstance().addTask(new Task() {
+ HTTPGetTask tsk = new HTTPGetTask(provider.getProvider().getVersionsListDownloadURL());
+
+ @Override
+ public boolean executeTask() {
+ final MinecraftRemoteVersions v = MinecraftRemoteVersions.fromJson(tsk.getResult());
+ if (v == null || v.versions == null)
+ return true;
+ SwingUtilities.invokeLater(() -> {
+ DefaultTableModel model = (DefaultTableModel) lstDownloads.getModel();
+ while (model.getRowCount() > 0) model.removeRow(0);
+ for (MinecraftRemoteVersion ver : v.versions) {
+ Object[] line = new Object[3];
+ line[0] = ver.id;
+ line[1] = ver.time;
+ if (StrUtils.equalsOne(ver.type, "old_beta", "old_alpha", "release", "snapshot"))
+ line[2] = C.i18n("versions." + ver.type);
+ else line[2] = ver.type;
+ model.addRow(line);
+ }
+ lstDownloads.updateUI();
+ });
+ return true;
+ }
+
+ @Override
+ public String getInfo() {
+ return "Format list.";
+ }
+
+ @Override
+ public Collection getDependTasks() {
+ return Arrays.asList((Task) tsk);
+ }
+ }).start();
+ }
+
+ void downloadMinecraft(DownloadType index) {
+ if (profile == null) return;
+ if (lstDownloads.getSelectedRow() < 0)
+ refreshDownloads(Settings.s().getDownloadSource());
+ if (lstDownloads.getSelectedRow() < 0) {
+ MessageBox.Show(C.i18n("gamedownload.not_refreshed"));
+ return;
+ }
+ String id = (String) lstDownloads.getModel().getValueAt(lstDownloads.getSelectedRow(), 0);
+ MCUtils.downloadMinecraft(profile.getGameDirFile(), id, index);
+ }
+
+ //
+ //
+ private String getMinecraftVersionFormatted() {
+ return minecraftVersion == null ? "" : (StrUtils.formatVersion(minecraftVersion.version) == null) ? mcVersion : minecraftVersion.version;
+ }
+
+ class InstallerHelper {
+ List versions;
+ InstallerVersionList list;
+ JTable jt;
+ String id;
+
+ public InstallerHelper(JTable jt, String id) {
+ this.jt = jt;
+ this.id = id;
+ }
+
+ public void loadVersions() {
+ versions = loadVersions(list, jt);
+ }
+
+ void refreshVersions() {
+ list = Settings.s().getDownloadSource().getProvider().getInstallerByType(id);
+ if (TaskWindow.getInstance().addTask(new TaskRunnableArg1<>(C.i18n("install." + id + ".get_list"), list)
+ .registerPreviousResult(new DefaultPreviousResult<>(new String[]{getMinecraftVersionFormatted()})))
+ .start())
+ loadVersions();
+ }
+
+ public InstallerVersion getVersion(int idx) {
+ return versions.get(idx);
+ }
+
+ private List loadVersions(InstallerVersionList list, JTable table) {
+ if (list == null)
+ return null;
+ DefaultTableModel model = (DefaultTableModel) table.getModel();
+ while (model.getRowCount() > 0)
+ model.removeRow(0);
+ String mcver = StrUtils.formatVersion(getMinecraftVersionFormatted());
+ List versions = list.getVersions(mcver);
+ if (versions != null) {
+ for (InstallerVersionList.InstallerVersion v : versions) {
+ Object a = v.selfVersion == null ? "null" : v.selfVersion;
+ Object b = v.mcVersion == null ? "null" : v.mcVersion;
+ Object[] row = new Object[]{a, b};
+ model.addRow(row);
+ }
+ table.updateUI();
+ }
+ return versions;
+ }
+ }
+
+ private void refreshVersions() {
+ getProfile().getMinecraftProvider().refreshVersions();
+ loadVersions();
+ }
+
+ //
+ //
+ boolean isLoading = false;
+ Profile profile;
+ public MinecraftVersionRequest minecraftVersion;
+ InstallerHelper forge, optifine, liteloader;
+ String mcVersion;
+
+ //
+
+ //
+ public void versionChanged(Profile profile, String version) {
+ this.mcVersion = version;
+ forge.loadVersions();
+ optifine.loadVersions();
+ liteloader.loadVersions();
+
+ MinecraftVersion v = profile.getMinecraftProvider().getVersionById(version);
+ if (v != null)
+ for (IAssetsHandler ph : IAssetsHandler.getAssetsHandlers())
+ try {
+ ph.setAssets(profile.getMinecraftProvider(), v);
+ } catch (Exception e) {
+ HMCLog.warn("Failed to load assets", e);
+ }
+ }
+
+ public void onSelected() {
+ loadProfiles();
+ if (profile == null) return;
+ if (profile.getMinecraftProvider().getVersionCount() <= 0)
+ versionChanged(profile, null);
+ else versionChanged(getProfile(), (String) cboVersions.getSelectedItem());
+ }
+
+ //
+ JPopupMenu ppmManage, ppmExplore;
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton btnDownload;
+ private javax.swing.JButton btnDownloadAllAssets;
+ private javax.swing.JButton btnDownloadForge;
+ private javax.swing.JButton btnDownloadOptifine;
+ private javax.swing.JButton btnExplore;
+ private javax.swing.JButton btnIncludeMinecraft;
+ private javax.swing.JButton btnInstallLiteLoader;
+ private javax.swing.JButton btnModify;
+ private javax.swing.JButton btnNewProfile;
+ private javax.swing.JButton btnRefreshForge;
+ private javax.swing.JButton btnRefreshGameDownloads;
+ private javax.swing.JButton btnRefreshLiteLoader;
+ private javax.swing.JButton btnRefreshOptifine;
+ private javax.swing.JButton btnRefreshVersions;
+ private javax.swing.JButton btnRemoveProfile;
+ private javax.swing.JButton btnRetryForge;
+ private javax.swing.JButton btnRetryLiteLoader;
+ private javax.swing.JComboBox cboGameDirType;
+ private javax.swing.JComboBox cboLauncherVisibility;
+ private javax.swing.JComboBox cboProfiles;
+ private javax.swing.JComboBox cboVersions;
+ private javax.swing.JCheckBox chkCancelWrapper;
+ private javax.swing.JCheckBox chkDebug;
+ private javax.swing.JCheckBox chkFullscreen;
+ private javax.swing.JCheckBox chkNoJVMArgs;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel10;
+ private javax.swing.JLabel jLabel11;
+ private javax.swing.JLabel jLabel12;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel24;
+ private javax.swing.JLabel jLabel25;
+ private javax.swing.JLabel jLabel26;
+ private javax.swing.JLabel jLabel27;
+ private javax.swing.JLabel jLabel28;
+ private javax.swing.JLabel jLabel29;
+ private javax.swing.JLabel jLabel30;
+ private javax.swing.JLabel jLabel31;
+ private javax.swing.JLabel jLabel9;
+ private javax.swing.JPanel jPanel1;
+ private javax.swing.JPanel jPanel16;
+ private javax.swing.JPanel jPanel2;
+ private javax.swing.JPanel jPanel22;
+ private javax.swing.JPanel jPanel3;
+ private javax.swing.JPanel jPanel4;
+ private javax.swing.JPanel jPanel5;
+ private javax.swing.JScrollPane jScrollPane11;
+ private javax.swing.JScrollPane jScrollPane12;
+ private javax.swing.JScrollPane jScrollPane13;
+ private javax.swing.JScrollPane jScrollPane2;
+ private javax.swing.JLabel lblMaxMemory;
+ private javax.swing.JTable lstDownloads;
+ private javax.swing.JTable lstForge;
+ private javax.swing.JTable lstLiteLoader;
+ private javax.swing.JTable lstOptifine;
+ private javax.swing.JPanel pnlAutoInstall;
+ private javax.swing.JPanel pnlGameDownloads;
+ private javax.swing.JPanel pnlOptifine;
+ private javax.swing.JTabbedPane tabInstallers;
+ private javax.swing.JTabbedPane tabVersionEdit;
+ private javax.swing.JTextField txtGameDir;
+ private javax.swing.JTextField txtHeight;
+ private javax.swing.JTextField txtJavaArgs;
+ private javax.swing.JTextField txtJavaDir;
+ private javax.swing.JTextField txtMaxMemory;
+ private javax.swing.JTextField txtMinecraftArgs;
+ private javax.swing.JTextField txtMinecraftVersion;
+ private javax.swing.JTextField txtPermSize;
+ private javax.swing.JTextField txtServerIP;
+ private javax.swing.JTextField txtWidth;
+ private javax.swing.JTextField txtWrapperLauncher;
+ // End of variables declaration//GEN-END:variables
+ //
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/HeaderTab.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/HeaderTab.java
new file mode 100644
index 000000000..1e4f2171a
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/HeaderTab.java
@@ -0,0 +1,92 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.views;
+
+import java.awt.EventQueue;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import javax.swing.BorderFactory;
+import javax.swing.DefaultButtonModel;
+import javax.swing.JLabel;
+
+/**
+ *
+ * @author hyh
+ */
+public class HeaderTab extends JLabel
+ implements MouseListener {
+
+ private boolean isActive;
+ private final DefaultButtonModel model;
+
+ public HeaderTab(String text) {
+ super(text);
+
+ this.model = new DefaultButtonModel();
+ setIsActive(false);
+
+ setBorder(BorderFactory.createEmptyBorder(6, 18, 7, 18));
+ addMouseListener(this);
+ }
+
+ public boolean isActive() {
+ return this.isActive;
+ }
+
+ public void setIsActive(boolean isActive) {
+ this.isActive = isActive;
+ setOpaque(isActive);
+
+ EventQueue.invokeLater(HeaderTab.this::repaint);
+ }
+
+ public void addActionListener(ActionListener listener) {
+ this.model.addActionListener(listener);
+ }
+
+ public String getActionCommand() {
+ return this.model.getActionCommand();
+ }
+
+ public ActionListener[] getActionListeners() {
+ return this.model.getActionListeners();
+ }
+
+ public void removeActionListener(ActionListener listener) {
+ this.model.removeActionListener(listener);
+ }
+
+ public void setActionCommand(String command) {
+ this.model.setActionCommand(command);
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ this.model.setPressed(true);
+ this.model.setArmed(true);
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ this.model.setPressed(false);
+ this.model.setArmed(false);
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ this.model.setRollover(true);
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ this.model.setRollover(false);
+ }
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/LauncherSettingsPanel.form b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/LauncherSettingsPanel.form
new file mode 100644
index 000000000..8f5af0159
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/LauncherSettingsPanel.form
@@ -0,0 +1,178 @@
+
+
+
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/LauncherSettingsPanel.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/LauncherSettingsPanel.java
new file mode 100644
index 000000000..4cc8fd676
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/LauncherSettingsPanel.java
@@ -0,0 +1,223 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.views;
+
+import java.awt.Color;
+import java.io.IOException;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JFileChooser;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.utils.IOUtils;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+
+/**
+ *
+ * @author hyh
+ */
+public class LauncherSettingsPanel extends javax.swing.JPanel {
+
+ /**
+ * Creates new form LancherSettingsPanel
+ */
+ public LauncherSettingsPanel() {
+ initComponents();
+
+ txtBackgroundPath.setText(Settings.s().getBgpath());
+ cboDownloadSource.setSelectedIndex(Settings.s().getDownloadType());
+ cboTheme.setSelectedIndex(Settings.s().getTheme());
+ chkEnableShadow.setSelected(Settings.s().isEnableShadow());
+
+ setBackground(Color.white);
+ setOpaque(true);
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ cboDownloadSource = new javax.swing.JComboBox();
+ jLabel4 = new javax.swing.JLabel();
+ btnSelBackgroundPath = new javax.swing.JButton();
+ jLabel14 = new javax.swing.JLabel();
+ txtBackgroundPath = new javax.swing.JTextField();
+ jLabel7 = new javax.swing.JLabel();
+ btnCheckUpdate = new javax.swing.JButton();
+ chkEnableShadow = new javax.swing.JCheckBox();
+ jLabel1 = new javax.swing.JLabel();
+ cboTheme = new javax.swing.JComboBox();
+
+ cboDownloadSource.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Mojang", "BMCLAPI(By bangbang93)" }));
+ cboDownloadSource.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent evt) {
+ cboDownloadSourceItemStateChanged(evt);
+ }
+ });
+
+ java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/jackhuang/hellominecraft/launcher/I18N"); // NOI18N
+ jLabel4.setText(bundle.getString("launcher.about")); // NOI18N
+
+ btnSelBackgroundPath.setText(bundle.getString("ui.button.explore")); // NOI18N
+ btnSelBackgroundPath.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnSelBackgroundPathActionPerformed(evt);
+ }
+ });
+
+ jLabel14.setText(bundle.getString("launcher.background_location")); // NOI18N
+ jLabel14.setToolTipText("");
+
+ txtBackgroundPath.setToolTipText(bundle.getString("launcher.background_tooltip")); // NOI18N
+ txtBackgroundPath.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtBackgroundPathFocusLost(evt);
+ }
+ });
+
+ jLabel7.setText(bundle.getString("launcher.download_source")); // NOI18N
+
+ btnCheckUpdate.setText(bundle.getString("launcher.update_launcher")); // NOI18N
+ btnCheckUpdate.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnCheckUpdateActionPerformed(evt);
+ }
+ });
+
+ chkEnableShadow.setText(bundle.getString("launcher.enable_shadow")); // NOI18N
+ chkEnableShadow.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ chkEnableShadowFocusLost(evt);
+ }
+ });
+
+ jLabel1.setText(bundle.getString("launcher.theme")); // NOI18N
+
+ cboTheme.setModel(new DefaultComboBoxModel(new String[]{C.i18n("color.blue"),C.i18n("color.green"),C.i18n("color.purple"),C.i18n("color.dark_blue"),C.i18n("color.orange"),C.i18n("color.red")}));
+ cboTheme.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent evt) {
+ cboThemeItemStateChanged(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(0, 0, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(chkEnableShadow)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnCheckUpdate))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jLabel7)
+ .addComponent(jLabel1)
+ .addComponent(jLabel14))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addComponent(txtBackgroundPath)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnSelBackgroundPath))
+ .addComponent(cboDownloadSource, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(cboTheme, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel14)
+ .addComponent(btnSelBackgroundPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(txtBackgroundPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel7)
+ .addComponent(cboDownloadSource, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(cboTheme, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(chkEnableShadow)
+ .addComponent(btnCheckUpdate, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 119, Short.MAX_VALUE)
+ .addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+ }// //GEN-END:initComponents
+
+ private void cboDownloadSourceItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboDownloadSourceItemStateChanged
+ Settings.s().setDownloadType(cboDownloadSource.getSelectedIndex());
+ }//GEN-LAST:event_cboDownloadSourceItemStateChanged
+
+ private void btnSelBackgroundPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSelBackgroundPathActionPerformed
+ JFileChooser fc = new JFileChooser();
+ fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ fc.setDialogTitle(C.i18n("launcher.choose_bgpath"));
+ fc.setMultiSelectionEnabled(false);
+ fc.showOpenDialog(this);
+ if (fc.getSelectedFile() == null) {
+ return;
+ }
+ try {
+ String path = fc.getSelectedFile().getCanonicalPath();
+ path = IOUtils.removeLastSeparator(path);
+ txtBackgroundPath.setText(path);
+ Settings.s().setBgpath(path);
+ MainFrame.instance.loadBackground();
+ } catch (IOException e) {
+ HMCLog.warn("Failed to set background path.", e);
+ MessageBox.Show(C.i18n("ui.label.failed_set") + e.getMessage());
+ }
+ }//GEN-LAST:event_btnSelBackgroundPathActionPerformed
+
+ private void txtBackgroundPathFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtBackgroundPathFocusLost
+ String path = txtBackgroundPath.getText();
+ Settings.s().setBgpath(path);
+ MainFrame.instance.loadBackground();
+ }//GEN-LAST:event_txtBackgroundPathFocusLost
+
+ private void btnCheckUpdateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheckUpdateActionPerformed
+ Settings.UPDATE_CHECKER.process(true);
+ }//GEN-LAST:event_btnCheckUpdateActionPerformed
+
+ private void chkEnableShadowFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_chkEnableShadowFocusLost
+ Settings.s().setEnableShadow(chkEnableShadow.isSelected());
+ }//GEN-LAST:event_chkEnableShadowFocusLost
+
+ private void cboThemeItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboThemeItemStateChanged
+ Settings.s().setTheme(cboTheme.getSelectedIndex());
+ if(MainFrame.instance != null)
+ MainFrame.instance.reloadColor();
+ }//GEN-LAST:event_cboThemeItemStateChanged
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton btnCheckUpdate;
+ private javax.swing.JButton btnSelBackgroundPath;
+ private javax.swing.JComboBox cboDownloadSource;
+ private javax.swing.JComboBox cboTheme;
+ private javax.swing.JCheckBox chkEnableShadow;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel14;
+ private javax.swing.JLabel jLabel4;
+ private javax.swing.JLabel jLabel7;
+ private javax.swing.JTextField txtBackgroundPath;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainFrame.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainFrame.java
new file mode 100644
index 000000000..b0a3fc8ba
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainFrame.java
@@ -0,0 +1,360 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.views;
+
+import com.sun.awt.AWTUtilities;
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.image.BufferedImage;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.Main;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.utils.UpdateChecker;
+import org.jackhuang.hellominecraft.utils.Utils;
+import org.jackhuang.hellominecraft.views.DropShadowBorder;
+import org.jackhuang.hellominecraft.views.TintablePanel;
+import org.jackhuang.hellominecraft.views.BasicColors;
+
+/**
+ *
+ * @author hyh
+ */
+public final class MainFrame extends DraggableFrame {
+
+ public static final MainFrame instance = new MainFrame();
+
+ HeaderTab mainTab, gameTab, launcherTab;
+ TintablePanel centralPanel;
+ JPanel header;
+ MainPagePanel mainPanel;
+ GameSettingsPanel gamePanel;
+ LauncherSettingsPanel launcherPanel;
+ CardLayout infoLayout;
+ JPanel infoSwap;
+ JLabel backgroundLabel, windowTitle;
+ JPanel realPanel;
+ DropShadowBorder border;
+ boolean enableShadow;
+ String defaultTitle;
+
+ MainFrame() {
+ defaultTitle = Main.makeTitle();
+ enableShadow = Settings.s().isEnableShadow();
+ if (enableShadow)
+ setSize(834, 542);
+ else
+ setSize(802, 511);
+ setDefaultCloseOperation(3);
+ setTitle(Main.makeTitle());
+ initComponents();
+ selectTab("main");
+ loadBackground();
+
+ setLocationRelativeTo(null);
+
+ if (enableShadow)
+ try {
+ AWTUtilities.setWindowOpaque(this, false);
+ getRootPane().setBorder(border = new DropShadowBorder(borderColor, 4));
+ } catch (Throwable ex) {
+ HMCLog.err("Failed to set window transparent.", ex);
+ Settings.s().setEnableShadow(false);
+ setSize(802, 511);
+ }
+
+ ((JPanel) getContentPane()).setOpaque(true);
+ }
+
+ private void initComponents() {
+ borderColor = BasicColors.bgcolors[Settings.s().getTheme()];
+ borderColorDarker = BasicColors.bgcolors_darker[Settings.s().getTheme()];
+
+ realPanel = new JPanel();
+ realPanel.setLayout(null);
+
+ header = new JPanel();
+ header.setBounds(0, 0, 800, 30);
+ realPanel.add(header);
+ header.setOpaque(true);
+ header.setLayout(new BoxLayout(header, BoxLayout.LINE_AXIS));
+ header.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 5));
+ header.setBackground(borderColor);
+ header.setForeground(BasicColors.COLOR_WHITE_TEXT);
+
+ ImageIcon headerIcon = Main.getIcon("icon.png");
+ this.setIconImage(headerIcon.getImage());
+ headerIcon = Utils.scaleImage(headerIcon, 16, 16);
+ JLabel headerLabel = new JLabel(headerIcon);
+ headerLabel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 0));
+ header.add(headerLabel);
+
+ header.add(Box.createRigidArea(new Dimension(8, 0)));
+
+ ActionListener tabListener = (e) -> MainFrame.this.selectTab(e.getActionCommand());
+
+ this.mainTab = new HeaderTab(C.i18n("launcher.title.main"));
+ this.mainTab.setForeground(BasicColors.COLOR_WHITE_TEXT);
+ this.mainTab.setBackground(borderColorDarker);
+ this.mainTab.setActionCommand("main");
+ this.mainTab.addActionListener(tabListener);
+ header.add(this.mainTab);
+
+ this.gameTab = new HeaderTab(C.i18n("launcher.title.game"));
+ this.gameTab.setForeground(BasicColors.COLOR_WHITE_TEXT);
+ this.gameTab.setBackground(borderColorDarker);
+ this.gameTab.setIsActive(true);
+ this.gameTab.setHorizontalTextPosition(10);
+ this.gameTab.addActionListener(tabListener);
+ this.gameTab.setActionCommand("game");
+ header.add(this.gameTab);
+
+ this.launcherTab = new HeaderTab(C.i18n("launcher.title.launcher"));
+ this.launcherTab.setForeground(BasicColors.COLOR_WHITE_TEXT);
+ this.launcherTab.setBackground(borderColorDarker);
+ this.launcherTab.setLayout(null);
+ this.launcherTab.addActionListener(tabListener);
+ this.launcherTab.setActionCommand("launcher");
+ header.add(this.launcherTab);
+
+ header.add(Box.createHorizontalGlue());
+
+ JPanel rightHeaderPanel = new JPanel();
+ rightHeaderPanel.setOpaque(false);
+ rightHeaderPanel.setLayout(new BoxLayout(rightHeaderPanel, BoxLayout.PAGE_AXIS));
+ rightHeaderPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
+
+ JPanel windowGadgetPanel = new JPanel();
+ windowGadgetPanel.setOpaque(false);
+ windowGadgetPanel.setLayout(new BoxLayout(windowGadgetPanel, BoxLayout.LINE_AXIS));
+ windowGadgetPanel.setAlignmentX(1.0F);
+
+ ImageIcon minimizeIcon = Main.getIcon("minimize.png");
+ JButton minimizeButton = new JButton(minimizeIcon);
+ minimizeButton.setBorder(BorderFactory.createEmptyBorder());
+ minimizeButton.setContentAreaFilled(false);
+ minimizeButton.setCursor(new Cursor(12));
+ minimizeButton.setFocusable(false);
+ minimizeButton.addActionListener((e) -> MainFrame.this.minimizeWindow());
+ windowGadgetPanel.add(minimizeButton);
+
+ ImageIcon closeIcon = Main.getIcon("close.png");
+ JButton closeButton = new JButton(closeIcon);
+ closeButton.setBorder(BorderFactory.createEmptyBorder());
+ closeButton.setContentAreaFilled(false);
+ closeButton.addActionListener((e) -> MainFrame.this.closeWindow());
+ closeButton.setCursor(new Cursor(12));
+ closeButton.setFocusable(false);
+ windowGadgetPanel.add(closeButton);
+
+ rightHeaderPanel.add(windowGadgetPanel);
+
+ windowTitle = new JLabel(defaultTitle);
+ windowTitle.setForeground(BasicColors.COLOR_WHITE_TEXT);
+ windowTitle.addMouseListener(new MouseListener() {
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (UpdateChecker.OUT_DATED) Main.update();
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ }
+ });
+ header.add(windowTitle);
+ header.add(Box.createHorizontalGlue());
+ header.add(rightHeaderPanel);
+
+ this.centralPanel = new TintablePanel();
+ this.centralPanel.setTintColor(BasicColors.COLOR_CENTRAL_BACK);
+ realPanel.add(this.centralPanel);
+ JPanel truePanel = new JPanel();
+ truePanel.setLayout(new BorderLayout());
+
+ this.infoSwap = new JPanel();
+ this.infoLayout = new CardLayout();
+ this.infoSwap.setLayout(infoLayout);
+ this.infoSwap.setOpaque(false);
+
+ this.mainPanel = new MainPagePanel();
+ this.infoSwap.add(mainPanel, "main");
+ this.gamePanel = new GameSettingsPanel();
+ this.infoSwap.add(gamePanel, "game");
+ this.launcherPanel = new LauncherSettingsPanel();
+ this.infoSwap.add(launcherPanel, "launcher");
+
+ truePanel.add(this.infoSwap, "Center");
+ centralPanel.setLayout(null);
+ centralPanel.add(truePanel);
+ truePanel.setBounds(0, 0, 800, 480);
+ centralPanel.setBounds(0, 30, 800, 480);
+
+ setLayout(null);
+ realPanel.setBounds(1, 0, 800, 511);
+ add(realPanel);
+ }
+
+ public void selectTab(String tabName) {
+ this.mainTab.setIsActive(false);
+ this.gameTab.setIsActive(false);
+ this.launcherTab.setIsActive(false);
+
+ if (tabName.equalsIgnoreCase("main")) {
+ this.mainTab.setIsActive(true);
+ this.mainPanel.onSelected();
+ } else if (tabName.equalsIgnoreCase("game")) {
+ this.gameTab.setIsActive(true);
+ this.gamePanel.onSelected();
+ } else if (tabName.equalsIgnoreCase("launcher"))
+ this.launcherTab.setIsActive(true);
+
+ this.infoLayout.show(this.infoSwap, tabName);
+ }
+
+ protected void closeWindow() {
+ System.exit(0);
+ }
+
+ protected void minimizeWindow() {
+ setState(1);
+ }
+
+ ImageIcon background;
+
+ public void loadBackground() {
+ background = Utils.searchBackgroundImage(Main.getIcon("background.jpg"), Settings.s().getBgpath(), 800, 480);
+ if (background != null) {
+ if (backgroundLabel == null) {
+ backgroundLabel = new JLabel(background);
+ backgroundLabel.setBounds(0, 0, 800, 480);
+ } else
+ backgroundLabel.setIcon(background);
+ centralPanel.add(backgroundLabel, -1);
+ } else
+ HMCLog.warn("No Background Image, the background will be white!");
+ }
+
+ public JPanel getTitleBar() {
+ return header;
+ }
+
+ boolean isShowedMessage = false;
+
+ public void closeMessage() {
+ if (isShowedMessage) {
+ isShowedMessage = false;
+ reloadColor();
+ windowTitle.setText(defaultTitle);
+ windowTitle.setForeground(UpdateChecker.OUT_DATED ? Color.red : Color.white);
+ }
+ }
+
+ public void showMessage(String message) {
+ isShowedMessage = true;
+ borderColor = BasicColors.COLOR_RED;
+ borderColorDarker = BasicColors.COLOR_RED_DARKER;
+ header.setBackground(borderColor);
+ mainTab.setBackground(borderColorDarker);
+ gameTab.setBackground(borderColorDarker);
+ launcherTab.setBackground(borderColorDarker);
+ if (border != null)
+ border.setColor(borderColor);
+ repaint();
+ windowTitle.setText(message);
+ windowTitle.setForeground(Color.white);
+ }
+
+ public static void showMainFrame(boolean firstLoad) {
+ instance.mainPanel.onShow(firstLoad);
+ instance.show();
+ }
+
+ Color borderColor = BasicColors.COLOR_BLUE;
+ Color borderColorDarker = BasicColors.COLOR_BLUE_DARKER;
+
+ public void reloadColor() {
+ borderColor = BasicColors.bgcolors[Settings.s().getTheme()];
+ borderColorDarker = BasicColors.bgcolors_darker[Settings.s().getTheme()];
+ if (border != null)
+ border.setColor(borderColor);
+ header.setBackground(borderColor);
+ mainTab.setBackground(borderColorDarker);
+ gameTab.setBackground(borderColorDarker);
+ launcherTab.setBackground(borderColorDarker);
+ repaint();
+ }
+
+ private void paintImpl(Graphics g) {
+ super.paint(g);
+ g.setColor(borderColor);
+ int off = enableShadow ? 16 : 0;
+ int width = 800;
+ int height = header.getHeight() + 480 - 1;
+ g.drawLine(off, off, off, height + off + 1);
+ g.drawLine(off + width + 1, off, off + width + 1, height + off + 1);
+ g.drawLine(off, height + off + 1, off + width + 1, height + off + 1);
+ }
+
+ @Override
+ public void paint(Graphics g) {
+ if (!enableShadow)
+ paintImpl(g);
+ else {
+ int off = enableShadow ? 16 : 0;
+ int width = this.getWidth();
+ int height = this.getHeight();
+ int contentWidth = width - off - off;
+ int contentHeight = height - off - off;
+ BufferedImage contentImage = new BufferedImage(contentWidth,
+ contentHeight, Transparency.OPAQUE);
+ Graphics2D contentG2d = contentImage.createGraphics();
+ contentG2d.translate(-off, -off);
+ paintImpl(g);
+ paintImpl(contentG2d);
+ contentG2d.dispose();
+ g.drawImage(contentImage, off, off, this);
+ }
+ }
+
+ public void invokeUpdate() {
+ if (!isVisible()) return;
+ defaultTitle = defaultTitle + C.i18n("update.found");
+ if (!isShowedMessage) {
+ windowTitle.setText(defaultTitle);
+ windowTitle.setForeground(Color.red);
+ }
+ }
+
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainPagePanel.form b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainPagePanel.form
new file mode 100644
index 000000000..dbf9f9ed4
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainPagePanel.form
@@ -0,0 +1,280 @@
+
+
+
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainPagePanel.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainPagePanel.java
new file mode 100644
index 000000000..98a1fea68
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/MainPagePanel.java
@@ -0,0 +1,540 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.jackhuang.hellominecraft.launcher.views;
+
+import java.awt.CardLayout;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ItemEvent;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.util.List;
+import javax.swing.SwingUtilities;
+import org.jackhuang.hellominecraft.C;
+import org.jackhuang.hellominecraft.HMCLog;
+import org.jackhuang.hellominecraft.launcher.launch.DefaultGameLauncher;
+import org.jackhuang.hellominecraft.launcher.utils.auth.IAuthenticator;
+import org.jackhuang.hellominecraft.launcher.utils.auth.LoginInfo;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.utils.MessageBox;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.launcher.utils.version.MinecraftVersion;
+import org.jackhuang.hellominecraft.launcher.launch.GameLauncher;
+import org.jackhuang.hellominecraft.launcher.launch.LaunchFinisher;
+import org.jackhuang.hellominecraft.launcher.launch.LaunchScriptFinisher;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+import org.jackhuang.hellominecraft.lookandfeel.GraphicsUtils;
+import org.jackhuang.hellominecraft.utils.Event;
+import org.jackhuang.hellominecraft.views.LogWindow;
+import org.jackhuang.hellominecraft.lookandfeel.components.ConstomButton;
+import org.jackhuang.hellominecraft.utils.functions.Consumer;
+
+/**
+ *
+ * @author hyh
+ */
+public class MainPagePanel extends javax.swing.JPanel {
+
+ /**
+ * Creates new form MainPagePanel
+ */
+ public MainPagePanel() {
+ initComponents();
+
+ pnlButtons = new javax.swing.JPanel();
+ pnlButtons.setLayout(null);
+
+ btnRun = new ConstomButton();
+ btnRun.setBounds(0, 0, 150, 50);
+ Font font = btnRun.getFont();
+ Font newFont = new Font(font.getName(), font.getStyle(), 15);
+ pnlButtons.add(btnRun);
+
+ btnRun.setText(C.i18n("ui.button.run"));
+ btnRun.setFont(newFont);
+ btnRun.addActionListener(e -> btnRunActionPerformed());
+
+ this.add(pnlButtons);
+ pnlButtons.setBounds(0, 0, 150, 50);
+
+ this.setSize(new Dimension(deWidth, deHeight));
+ this.pnlButtons.setLocation(deWidth - pnlButtons.getWidth() - 25, deHeight - pnlButtons.getHeight() - 25);
+ pnlMore.setBounds(0, 0, pnlMore.getWidth(), deHeight);
+ pnlMore.setBackground(GraphicsUtils.getWebColorWithAlpha("FFFFFF7F"));
+ pnlMore.setOpaque(true);
+
+ prepareAuths();
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ pnlMore = new javax.swing.JPanel();
+ txtPlayerName = new javax.swing.JTextField();
+ jLabel7 = new javax.swing.JLabel();
+ cboLoginMode = new javax.swing.JComboBox();
+ lblUserName = new javax.swing.JLabel();
+ jLabel10 = new javax.swing.JLabel();
+ cboProfiles = new javax.swing.JComboBox();
+ jLabel1 = new javax.swing.JLabel();
+ cboVersions = new javax.swing.JComboBox();
+ pnlPassword = new javax.swing.JPanel();
+ jPanel1 = new javax.swing.JPanel();
+ jLabel9 = new javax.swing.JLabel();
+ txtPassword = new javax.swing.JPasswordField();
+ jPanel3 = new javax.swing.JPanel();
+ btnLogout = new javax.swing.JButton();
+ btnShowLog = new javax.swing.JButton();
+ btnMakeLaunchScript = new javax.swing.JButton();
+
+ setLayout(null);
+
+ pnlMore.setBackground(new java.awt.Color(204, 204, 204));
+ pnlMore.setOpaque(false);
+
+ txtPlayerName.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusGained(java.awt.event.FocusEvent evt) {
+ txtPlayerNameFocusGained(evt);
+ }
+ public void focusLost(java.awt.event.FocusEvent evt) {
+ txtPlayerNameFocusLost(evt);
+ }
+ });
+ txtPlayerName.addKeyListener(new java.awt.event.KeyAdapter() {
+ public void keyPressed(java.awt.event.KeyEvent evt) {
+ txtPlayerNameKeyPressed(evt);
+ }
+ });
+
+ java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/jackhuang/hellominecraft/launcher/I18N"); // NOI18N
+ jLabel7.setText(bundle.getString("login.type")); // NOI18N
+
+ cboLoginMode.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent evt) {
+ cboLoginModeItemStateChanged(evt);
+ }
+ });
+
+ lblUserName.setText(bundle.getString("login.username")); // NOI18N
+
+ jLabel10.setText(bundle.getString("ui.label.profile")); // NOI18N
+
+ cboProfiles.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent evt) {
+ cboProfilesItemStateChanged(evt);
+ }
+ });
+
+ jLabel1.setText(C.I18N.getString("ui.label.version")); // NOI18N
+ jLabel1.setToolTipText(bundle.getString("ui.label.version")); // NOI18N
+
+ cboVersions.addItemListener(new java.awt.event.ItemListener() {
+ public void itemStateChanged(java.awt.event.ItemEvent evt) {
+ cboVersionsItemStateChanged(evt);
+ }
+ });
+
+ pnlPassword.setLayout(new java.awt.CardLayout());
+
+ jLabel9.setText(bundle.getString("ui.label.password")); // NOI18N
+
+ txtPassword.addFocusListener(new java.awt.event.FocusAdapter() {
+ public void focusGained(java.awt.event.FocusEvent evt) {
+ txtPasswordFocusGained(evt);
+ }
+ });
+ txtPassword.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ txtPasswordActionPerformed(evt);
+ }
+ });
+ txtPassword.addKeyListener(new java.awt.event.KeyAdapter() {
+ public void keyPressed(java.awt.event.KeyEvent evt) {
+ txtPasswordKeyPressed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(jLabel9)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(txtPassword))
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel9, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ );
+
+ pnlPassword.add(jPanel1, "card2");
+
+ btnLogout.setText(C.I18N.getString("ui.button.logout")); // NOI18N
+ btnLogout.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnLogoutActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
+ jPanel3.setLayout(jPanel3Layout);
+ jPanel3Layout.setHorizontalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(btnLogout, javax.swing.GroupLayout.DEFAULT_SIZE, 170, Short.MAX_VALUE)
+ );
+ jPanel3Layout.setVerticalGroup(
+ jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(btnLogout, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE)
+ );
+
+ pnlPassword.add(jPanel3, "card3");
+
+ btnShowLog.setText(bundle.getString("mainwindow.show_log")); // NOI18N
+ btnShowLog.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnShowLogActionPerformed(evt);
+ }
+ });
+
+ btnMakeLaunchScript.setText(bundle.getString("mainwindow.make_launch_script")); // NOI18N
+ btnMakeLaunchScript.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnMakeLaunchScriptActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout pnlMoreLayout = new javax.swing.GroupLayout(pnlMore);
+ pnlMore.setLayout(pnlMoreLayout);
+ pnlMoreLayout.setHorizontalGroup(
+ pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(pnlMoreLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(pnlPassword, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(pnlMoreLayout.createSequentialGroup()
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jLabel10)
+ .addComponent(jLabel1))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(cboProfiles, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(cboVersions, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ .addComponent(btnMakeLaunchScript, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(btnShowLog, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(pnlMoreLayout.createSequentialGroup()
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(lblUserName)
+ .addComponent(jLabel7))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(cboLoginMode, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(txtPlayerName))))
+ .addContainerGap())
+ );
+ pnlMoreLayout.setVerticalGroup(
+ pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlMoreLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel10)
+ .addComponent(cboProfiles, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(cboVersions, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel1))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel7)
+ .addComponent(cboLoginMode, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(pnlMoreLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(lblUserName)
+ .addComponent(txtPlayerName, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(pnlPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 254, Short.MAX_VALUE)
+ .addComponent(btnShowLog)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnMakeLaunchScript)
+ .addContainerGap())
+ );
+
+ add(pnlMore);
+ pnlMore.setBounds(0, 0, 190, 480);
+ }// //GEN-END:initComponents
+
+ private void txtPlayerNameFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtPlayerNameFocusGained
+ MainFrame.instance.closeMessage();
+ }//GEN-LAST:event_txtPlayerNameFocusGained
+
+ private void txtPlayerNameFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtPlayerNameFocusLost
+ Settings.s().setUsername(txtPlayerName.getText());
+ }//GEN-LAST:event_txtPlayerNameFocusLost
+
+ private void cboLoginModeItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboLoginModeItemStateChanged
+ if (preaparingAuth) return;
+ int index = cboLoginMode.getSelectedIndex();
+ if (index < 0) return;
+
+ IAuthenticator l = IAuthenticator.logins.get(index);
+ if (l.isHidePasswordBox()) {
+ pnlPassword.setVisible(false);
+ lblUserName.setText(C.i18n("login.username"));
+ } else {
+ pnlPassword.setVisible(true);
+ lblUserName.setText(C.i18n("login.account"));
+ }
+
+ CardLayout cl = (CardLayout) pnlPassword.getLayout();
+ if (l.isLoggedIn()) cl.last(pnlPassword);
+ else cl.first(pnlPassword);
+ String username = Settings.s().getUsername();
+ if (StrUtils.isNotBlank(username)) txtPlayerName.setText(username);
+
+ Settings.s().setLoginType(index);
+ }//GEN-LAST:event_cboLoginModeItemStateChanged
+
+ private void cboProfilesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboProfilesItemStateChanged
+ if (!isLoading && cboProfiles.getSelectedIndex() != -1 && !StrUtils.isBlank((String) cboProfiles.getSelectedItem())) {
+ Settings.s().setLast((String) cboProfiles.getSelectedItem());
+ loadMinecraftVersions();
+ }
+ }//GEN-LAST:event_cboProfilesItemStateChanged
+
+ private void cboVersionsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboVersionsItemStateChanged
+ if (isLoading || evt.getStateChange() != ItemEvent.SELECTED || cboVersions.getSelectedIndex() < 0 || StrUtils.isBlank((String) cboVersions.getSelectedItem()) || getCurrentProfile() == null)
+ return;
+ getCurrentProfile().setSelectedMinecraftVersion(cboVersions.getSelectedItem().toString());
+ cboVersions.setToolTipText(cboVersions.getSelectedItem().toString());
+ Settings.save();
+ }//GEN-LAST:event_cboVersionsItemStateChanged
+
+ private void txtPasswordFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txtPasswordFocusGained
+ MainFrame.instance.closeMessage();
+ }//GEN-LAST:event_txtPasswordFocusGained
+
+ private void txtPasswordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtPasswordActionPerformed
+ btnRunActionPerformed();
+ }//GEN-LAST:event_txtPasswordActionPerformed
+
+ private void btnLogoutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLogoutActionPerformed
+ if (preaparingAuth) return;
+ int index = cboLoginMode.getSelectedIndex();
+
+ IAuthenticator l = IAuthenticator.logins.get(index);
+ CardLayout cl = (CardLayout) pnlPassword.getLayout();
+ if (l.isLoggedIn()) l.logout();
+ cl.first(pnlPassword);
+ }//GEN-LAST:event_btnLogoutActionPerformed
+
+ private void btnShowLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnShowLogActionPerformed
+ LogWindow.instance.setVisible(true);
+ }//GEN-LAST:event_btnShowLogActionPerformed
+
+ private void btnMakeLaunchScriptActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMakeLaunchScriptActionPerformed
+ MainFrame.instance.showMessage(C.i18n("ui.message.launching"));
+ genLaunchCode(value -> {
+ value.successEvent.register(new LaunchScriptFinisher());
+ value.successEvent.register(new PrepareAuthDoneListener());
+ });
+ }//GEN-LAST:event_btnMakeLaunchScriptActionPerformed
+
+ private void txtPlayerNameKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtPlayerNameKeyPressed
+ if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
+ int index = cboLoginMode.getSelectedIndex();
+ if (index < 0) return;
+ IAuthenticator l = IAuthenticator.logins.get(index);
+ if (l.isHidePasswordBox()) btnRunActionPerformed();
+ else if (!l.isLoggedIn()) txtPassword.requestFocus();
+ }
+ }//GEN-LAST:event_txtPlayerNameKeyPressed
+
+ private void txtPasswordKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtPasswordKeyPressed
+ if (evt.getKeyCode() == KeyEvent.VK_ENTER) btnRunActionPerformed();
+ }//GEN-LAST:event_txtPasswordKeyPressed
+
+ //
+ void genLaunchCode(final Consumer listener) {
+ HMCLog.log("Start generating launching command...");
+ File file = getCurrentProfile().getCanonicalGameDirFile();
+ if (!file.exists()) {
+ HMCLog.warn("The minecraft path is wrong, please check it yourself.");
+ MessageBox.Show(C.i18n("minecraft.wrong_path"));
+ return;
+ }
+ final String name = (String) cboProfiles.getSelectedItem();
+ if (StrUtils.isBlank(name) || getCurrentProfile().getSelectedMinecraftVersion() == null) {
+ HMCLog.warn("There's no selected version, rechoose a version.");
+ MessageBox.Show(C.i18n("minecraft.no_selected_version"));
+ return;
+ }
+
+ if (cboLoginMode.getItemCount() == 0) {
+ HMCLog.warn("There's no login method.");
+ MessageBox.Show(C.i18n("login.methods.no_method"));
+ return;
+ }
+ final int index = cboLoginMode.getSelectedIndex();
+ final IAuthenticator l = IAuthenticator.logins.get(index);
+ final LoginInfo li = new LoginInfo(Settings.s().getUsername(), l.isLoggedIn() || l.isHidePasswordBox() ? null : new String(txtPassword.getPassword()));
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ Thread.currentThread().setName("Game Launcher");
+ DefaultGameLauncher gl = new DefaultGameLauncher(getCurrentProfile(), li, l, Settings.s().getDownloadSource());
+ gl.failEvent.register((sender, s) -> {
+ if (s != null) MessageBox.Show(s);
+ MainFrame.instance.closeMessage();
+ return true;
+ });
+ listener.accept(gl);
+ gl.makeLaunchCommand();
+ }
+ };
+ t.start();
+ }
+ //
+
+ //
+ private void prepareAuths() {
+ preaparingAuth = true;
+ List list = IAuthenticator.logins;
+ cboLoginMode.removeAllItems();
+ for (IAuthenticator str : list)
+ try {
+ cboLoginMode.addItem(str.getName());
+ } catch (Exception ex) {
+ HMCLog.warn("Failed to get login name", ex);
+ }
+ if (Settings.s().getLoginType() < list.size()) {
+ preaparingAuth = false;
+ cboLoginMode.setSelectedIndex(Settings.s().getLoginType());
+
+ cboLoginModeItemStateChanged(null);
+ }
+ }
+
+ void loadFromSettings() {
+ for (Profile s : Settings.getProfiles()) cboProfiles.addItem(s.getName());
+ }
+
+ boolean showedNoVersion = false;
+
+ void loadMinecraftVersions() {
+ isLoading = true;
+ cboVersions.removeAllItems();
+ int index = 0, i = 0;
+ getCurrentProfile().getMinecraftProvider().refreshVersions();
+ MinecraftVersion selVersion = getCurrentProfile().getSelectedMinecraftVersion();
+ String selectedMC = selVersion == null ? null : selVersion.id;
+ if (getCurrentProfile().getMinecraftProvider().getVersions().isEmpty()) {
+ if (!showedNoVersion)
+ SwingUtilities.invokeLater(() -> {
+ MessageBox.Show(C.i18n("mainwindow.no_version"));
+ showedNoVersion = true;
+ });
+ } else {
+ for (MinecraftVersion mcVersion : getCurrentProfile().getMinecraftProvider().getVersions()) {
+ if (mcVersion.hidden) continue;
+ cboVersions.addItem(mcVersion.id);
+ if (mcVersion.id.equals(selectedMC)) index = i;
+ i++;
+ }
+ if (index < cboVersions.getItemCount()) cboVersions.setSelectedIndex(index);
+ }
+ isLoading = false;
+ }
+
+ private void refreshMinecrafts(String last) {
+ isLoading = true;
+ cboProfiles.removeAllItems();
+ loadFromSettings();
+ for (int i = 0; i < cboProfiles.getItemCount(); i++) {
+ String s = (String) cboProfiles.getItemAt(i);
+ if (s != null && s.equals(last)) {
+ cboProfiles.setSelectedIndex(i);
+ break;
+ }
+ }
+ isLoading = false;
+ loadMinecraftVersions();
+ }
+ //
+
+ //
+ boolean preaparingAuth = true;
+ private boolean isLoading = false;
+ private final javax.swing.JPanel pnlButtons;
+ private final ConstomButton btnRun;
+ private static final int deWidth = 800, deHeight = 480;
+ //
+
+ class PrepareAuthDoneListener implements Event> {
+
+ @Override
+ public boolean call(Object sender, List value) {
+ prepareAuths();
+ return true;
+ }
+ }
+
+ private void btnRunActionPerformed() {
+ MainFrame.instance.showMessage(C.i18n("ui.message.launching"));
+ genLaunchCode(value -> {
+ value.successEvent.register(new LaunchFinisher());
+ value.successEvent.register(new PrepareAuthDoneListener());
+ });
+ }
+
+ public void onShow(boolean showLeft) {
+ if (showLeft)
+ SwingUtilities.invokeLater(() -> MainFrame.instance.showMessage(C.i18n("ui.message.first_load")));
+ if (cboLoginMode.getSelectedIndex() >= 0 && cboLoginMode.getSelectedIndex() < cboLoginMode.getItemCount()) {
+ IAuthenticator l = IAuthenticator.logins.get(cboLoginMode.getSelectedIndex());
+ if (!l.isHidePasswordBox() && !l.isLoggedIn())
+ SwingUtilities.invokeLater(() -> MainFrame.instance.showMessage(C.i18n("ui.message.enter_password")));
+ }
+ }
+
+ public Profile getCurrentProfile() {
+ return Settings.getVersion((String) cboProfiles.getSelectedItem());
+ }
+
+ public void onSelected() {
+ refreshMinecrafts(Settings.s().getLast());
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton btnLogout;
+ private javax.swing.JButton btnMakeLaunchScript;
+ private javax.swing.JButton btnShowLog;
+ private javax.swing.JComboBox cboLoginMode;
+ private javax.swing.JComboBox cboProfiles;
+ private javax.swing.JComboBox cboVersions;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel10;
+ private javax.swing.JLabel jLabel7;
+ private javax.swing.JLabel jLabel9;
+ private javax.swing.JPanel jPanel1;
+ private javax.swing.JPanel jPanel3;
+ private javax.swing.JLabel lblUserName;
+ private javax.swing.JPanel pnlMore;
+ private javax.swing.JPanel pnlPassword;
+ private javax.swing.JPasswordField txtPassword;
+ private javax.swing.JTextField txtPlayerName;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/NewProfileWindow.form b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/NewProfileWindow.form
new file mode 100644
index 000000000..65e0f21bf
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/NewProfileWindow.form
@@ -0,0 +1,124 @@
+
+
+
diff --git a/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/NewProfileWindow.java b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/NewProfileWindow.java
new file mode 100644
index 000000000..69f2839ff
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/hellominecraft/launcher/views/NewProfileWindow.java
@@ -0,0 +1,150 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.jackhuang.hellominecraft.launcher.views;
+
+import org.jackhuang.hellominecraft.launcher.utils.settings.Profile;
+import org.jackhuang.hellominecraft.launcher.utils.settings.Settings;
+
+/**
+ *
+ * @author hyh
+ */
+public final class NewProfileWindow extends javax.swing.JDialog {
+
+ /**
+ * Creates new form NewProfileWindow
+ */
+ public NewProfileWindow(java.awt.Frame parent) {
+ super(parent, true);
+ initComponents();
+
+ setLocationRelativeTo(null);
+
+ for (Profile s : Settings.getProfiles()) {
+ cboProfiles.addItem(s.getName());
+ }
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jLabel1 = new javax.swing.JLabel();
+ txtNewProfileName = new javax.swing.JTextField();
+ jLabel2 = new javax.swing.JLabel();
+ cboProfiles = new javax.swing.JComboBox();
+ btnCancel = new javax.swing.JButton();
+ btnOK = new javax.swing.JButton();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/jackhuang/hellominecraft/launcher/I18N"); // NOI18N
+ setTitle(bundle.getString("ui.newProfileWindow.title")); // NOI18N
+
+ jLabel1.setText(bundle.getString("ui.label.newProfileWindow.new_profile_name")); // NOI18N
+
+ txtNewProfileName.addKeyListener(new java.awt.event.KeyAdapter() {
+ public void keyTyped(java.awt.event.KeyEvent evt) {
+ txtNewProfileNameKeyTyped(evt);
+ }
+ });
+
+ jLabel2.setText(bundle.getString("ui.label.newProfileWindow.copy_from")); // NOI18N
+
+ btnCancel.setText(bundle.getString("button.cancel")); // NOI18N
+ btnCancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnCancelActionPerformed(evt);
+ }
+ });
+
+ btnOK.setText(bundle.getString("ui.button.ok")); // NOI18N
+ btnOK.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ btnOKActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel1)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(txtNewProfileName))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel2)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(cboProfiles, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGap(0, 262, Short.MAX_VALUE)
+ .addComponent(btnOK)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(btnCancel)))
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(txtNewProfileName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel2)
+ .addComponent(cboProfiles, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(btnCancel)
+ .addComponent(btnOK))
+ .addContainerGap())
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void txtNewProfileNameKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtNewProfileNameKeyTyped
+ switch(evt.getKeyCode()) {
+ case 13:
+ Profile newProfile = new Profile(Settings.getVersion(cboProfiles.getSelectedItem().toString()));
+ newProfile.setName(txtNewProfileName.getText());
+ Settings.trySetVersion(newProfile);
+ case 27:
+ this.dispose();
+ }
+ }//GEN-LAST:event_txtNewProfileNameKeyTyped
+
+ private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed
+ Profile newProfile = new Profile(Settings.getVersion(cboProfiles.getSelectedItem().toString()));
+ newProfile.setName(txtNewProfileName.getText());
+ Settings.trySetVersion(newProfile);
+ this.dispose();
+ }//GEN-LAST:event_btnOKActionPerformed
+
+ private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed
+ this.dispose();
+ }//GEN-LAST:event_btnCancelActionPerformed
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton btnCancel;
+ private javax.swing.JButton btnOK;
+ private javax.swing.JComboBox cboProfiles;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JTextField txtNewProfileName;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/HMCL/src/main/java/org/jackhuang/mojang/authlib/Agent.java b/HMCL/src/main/java/org/jackhuang/mojang/authlib/Agent.java
new file mode 100644
index 000000000..a83b0f678
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/mojang/authlib/Agent.java
@@ -0,0 +1,28 @@
+package org.jackhuang.mojang.authlib;
+
+public class Agent {
+
+ public static final Agent MINECRAFT = new Agent("Minecraft", 1);
+ public static final Agent SCROLLS = new Agent("Scrolls", 1);
+ private final String name;
+ private final int version;
+
+ public Agent(String name, int version) {
+ this.name = name;
+ this.version = version;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public int getVersion() {
+ return this.version;
+ }
+
+ @Override
+ public String toString() {
+ return "Agent{name='" + this.name + '\'' + ", version=" + this.version + '}';
+ }
+
+}
\ No newline at end of file
diff --git a/HMCL/src/main/java/org/jackhuang/mojang/authlib/AuthenticationService.java b/HMCL/src/main/java/org/jackhuang/mojang/authlib/AuthenticationService.java
new file mode 100644
index 000000000..8776477a9
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/mojang/authlib/AuthenticationService.java
@@ -0,0 +1,12 @@
+package org.jackhuang.mojang.authlib;
+
+import org.jackhuang.mojang.authlib.minecraft.MinecraftSessionService;
+
+public abstract interface AuthenticationService {
+
+ public abstract UserAuthentication createUserAuthentication(Agent paramAgent);
+
+ public abstract MinecraftSessionService createMinecraftSessionService();
+
+ public abstract GameProfileRepository createProfileRepository();
+}
diff --git a/HMCL/src/main/java/org/jackhuang/mojang/authlib/BaseAuthenticationService.java b/HMCL/src/main/java/org/jackhuang/mojang/authlib/BaseAuthenticationService.java
new file mode 100644
index 000000000..3b484d3c4
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/mojang/authlib/BaseAuthenticationService.java
@@ -0,0 +1,5 @@
+package org.jackhuang.mojang.authlib;
+
+public abstract class BaseAuthenticationService
+ implements AuthenticationService {
+}
diff --git a/HMCL/src/main/java/org/jackhuang/mojang/authlib/BaseUserAuthentication.java b/HMCL/src/main/java/org/jackhuang/mojang/authlib/BaseUserAuthentication.java
new file mode 100644
index 000000000..19fbe0a1e
--- /dev/null
+++ b/HMCL/src/main/java/org/jackhuang/mojang/authlib/BaseUserAuthentication.java
@@ -0,0 +1,263 @@
+package org.jackhuang.mojang.authlib;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jackhuang.hellominecraft.logging.logger.Logger;
+import org.jackhuang.hellominecraft.utils.StrUtils;
+import org.jackhuang.hellominecraft.utils.Validate;
+import org.jackhuang.mojang.authlib.properties.Property;
+import org.jackhuang.mojang.authlib.properties.PropertyMap;
+import org.jackhuang.mojang.util.UUIDTypeAdapter;
+
+public abstract class BaseUserAuthentication
+ implements UserAuthentication {
+
+ private static final Logger LOGGER = new Logger("BaseUserAuthentication");
+ protected static final String STORAGE_KEY_PROFILE_NAME = "displayName";
+ protected static final String STORAGE_KEY_PROFILE_ID = "uuid";
+ protected static final String STORAGE_KEY_PROFILE_PROPERTIES = "profileProperties";
+ protected static final String STORAGE_KEY_USER_NAME = "username";
+ protected static final String STORAGE_KEY_USER_ID = "userid";
+ protected static final String STORAGE_KEY_USER_PROPERTIES = "userProperties";
+ private final AuthenticationService authenticationService;
+ private final PropertyMap userProperties = new PropertyMap();
+ private String userid;
+ private String username;
+ private String password;
+ private GameProfile selectedProfile;
+ private UserType userType;
+
+ protected BaseUserAuthentication(AuthenticationService authenticationService) {
+ Validate.notNull(authenticationService);
+ this.authenticationService = authenticationService;
+ }
+
+ @Override
+ public boolean canLogIn() {
+ return (!canPlayOnline()) && (StrUtils.isNotBlank(getUsername())) && (StrUtils.isNotBlank(getPassword()));
+ }
+
+ @Override
+ public void logOut() {
+ this.password = null;
+ this.userid = null;
+ setSelectedProfile(null);
+ getModifiableUserProperties().clear();
+ setUserType(null);
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ return getSelectedProfile() != null;
+ }
+
+ @Override
+ public void setUsername(String username) {
+ if ((isLoggedIn()) && (canPlayOnline())) {
+ throw new IllegalStateException("Cannot change username whilst logged in & online");
+ }
+
+ this.username = username;
+ }
+
+ @Override
+ public void setPassword(String password) {
+ if ((isLoggedIn()) && (canPlayOnline()) && (StrUtils.isNotBlank(password))) {
+ throw new IllegalStateException("Cannot set password whilst logged in & online");
+ }
+
+ this.password = password;
+ }
+
+ protected String getUsername() {
+ return this.username;
+ }
+
+ protected String getPassword() {
+ return this.password;
+ }
+
+ @Override
+ public void loadFromStorage(Map credentials) {
+ logOut();
+
+ setUsername((String)credentials.get("username"));
+
+ if (credentials.containsKey("userid")) {
+ this.userid = (String)credentials.get("userid");
+ } else {
+ this.userid = this.username;
+ }
+
+ if (credentials.containsKey("userProperties")) {
+ try {
+ List