Compare commits

...
52 Commits
Author SHA1 Message Date
huangyuhui 69c0382bd5 Removed -XstartOnFirstThread to fix freezed launched game. GH-257 2018-02-01 16:03:10 +08:00
huangyuhui 1d80cd9fa2 Merge 'arguments' in json 2017-11-16 00:02:32 +08:00
huangyuhui 3721842c8b Removed funtionalities of Log4jHandler 2017-11-04 22:30:38 +08:00
huangyuhui 47d8b59b93 Update Gradle 2017-11-04 10:22:37 +08:00
huangyuhui b5fbc69ee6 Default game arguments 2017-11-03 13:38:20 +08:00
huangyuhui 9d95d3ea66 JVM arguments in minecraft.json supported 2017-11-02 20:21:32 +08:00
huangyuhui 06b3ec8337 Launching 1.13 2017-11-02 19:34:21 +08:00
huangyuhui fabb6b50b4 Prevent launching in path that contains ! 2017-10-31 19:31:22 +08:00
huangyuhui ca627b8a2d Update Gradle 2017-10-18 19:27:35 +08:00
huanghongxun a0c619a747 Ignore RejectedExecutionException 2017-10-11 20:03:17 +08:00
huanghongxun 5386ae3736 Removed log4j support 2017-10-11 19:54:08 +08:00
huangyuhui 32fd125b97 Added proxy type selection. Closes #192 2017-08-27 13:34:12 +08:00
huangyuhui 7a86881926 Fixed downloading libraries that are forbidden in specific OS. Closes #163 2017-08-26 17:45:40 +08:00
huangyuhui b255fd4601 CDATA 2017-08-26 09:04:10 +08:00
huangyuhui 562dd8d916 Fixed #190 2017-08-26 08:35:27 +08:00
huangyuhui 97d820446a Fixed Read end dead. Closes #186 2017-08-24 10:03:19 +08:00
huangyuhui 826d382ad5 Changed log colors. Closes #187 2017-08-24 09:37:33 +08:00
huangyuhui 59d7ac3c82 ProcessThread.interrupt 2017-08-20 12:07:13 +08:00
huanghongxun 566aed4ed6 Merge pull request #185 from ron-gh/master
Fix "java.io.IOException: Read end dead"
2017-08-18 21:32:53 +08:00
ron 1b90e94615 Fix "java.io.IOException: Read end dead" when different Threads are writing to a single PipedOutputStream 2017-08-18 14:54:55 +02:00
huanghongxun 22ede84c3d Merge pull request #183 from jp8900308/master
Set a custom User-Agent, fixes optifine.net downloads
2017-08-15 23:43:05 +08:00
Jean-Paul Bergeron 0c4da069b7 Set a custom User-Agent, fixes optifine.net downloads 2017-08-15 07:23:03 -07:00
huangyuhui 90733c6a7e Merge branch 'master' of https://github.com/huanghongxun/HMCL 2017-08-13 19:57:08 +08:00
huangyuhui 44daf656b5 Fixed downloading text2speech 2017-08-13 19:56:02 +08:00
huangyuhui 90f416095e Fixed multi-character account: no player when log in the next time 2017-08-13 19:39:41 +08:00
huanghongxun 076d4d5dce Merge pull request #180 from jp8900308/master
partial rv 7fe3e28a, fixes Forge on 1.12
2017-08-12 09:39:23 +08:00
Jean-Paul Bergeron 3c77f249e5 partial rv 7fe3e28a, fixes Forge on 1.12 2017-08-09 12:24:39 -07:00
huangyuhui 11e5dc3782 Update Russian lang 2017-08-07 21:55:03 +08:00
huangyuhui 34d560b75e Now OptionPane Yes No are translated 2017-07-28 09:44:29 +08:00
huangyuhui caa77387df Fixed reseting the color at warning mode 2017-07-28 08:45:36 +08:00
huangyuhui a0b19cfd1b Russian Lang 2017-07-19 23:28:56 +08:00
huangyuhui 6afcf8d0d7 Support liteloader snapshots 2017-07-03 19:43:36 +08:00
huangyuhui b8591007cf Fixed wrong username when already logged in. Closes #158 2017-07-03 08:52:41 +08:00
huangyuhui 7fe3e28a2d Add default log4j config file for better performance 2017-07-03 08:46:35 +08:00
huangyuhui 72a1d4f186 Initial version here 2017-07-02 23:08:26 +08:00
huangyuhui 472658293f 2.7.8 2017-07-02 16:31:24 +08:00
huangyuhui 4ecdefc14e Added searching log texts. Closes #69 2017-07-02 16:24:50 +08:00
huangyuhui 38eff7e59c Supported 'logging' item in minecraft.json 2017-07-02 13:12:41 +08:00
huangyuhui 7d8a66b71a Fixed not downloading when a library and an native library have the same name 2017-07-01 23:38:07 +08:00
huangyuhui 29f53d574c add log lines limitation 2017-07-01 21:31:24 +08:00
huangyuhui 979a1a9810 Prevent too much memory used by logging system 2017-06-29 08:30:01 +08:00
huangyuhui 2f3c8285c8 Fixed #153 2017-06-24 15:52:28 +08:00
huangyuhui cfa7b4832a try to avoid not supporting translucent 2017-06-11 21:46:54 +08:00
huangyuhui 091898551e Force to disable shadows in Mac and Linux in order to close issue #122 #124 #131 #136 #145 2017-06-11 13:15:38 +08:00
huangyuhui fbeabfcad2 Fix spinner UI 2017-06-11 13:11:44 +08:00
huangyuhui 5a6c64377a Fix console font coloring 2017-06-11 12:24:50 +08:00
huangyuhui faa806fc9f New console font options 2017-06-11 12:17:41 +08:00
huangyuhui 4fdafecf77 add STDOUT & STDERR highlight 2017-06-08 22:59:21 +08:00
huangyuhui a8bc4eb352 Change version to 2.7.7 2017-05-20 19:10:38 +08:00
huangyuhui 532ff88ea4 Fixed not aborting launching when not having successfully downloaded all libraries 2017-05-20 19:01:32 +08:00
huangyuhui 30ee33cd66 Now component animations and MainPagePanel animation can be disabled 2017-05-20 18:44:26 +08:00
huangyuhui 3462123877 Fixed not turn to state 'done' when game crashes at the beginning 2017-05-20 18:24:42 +08:00
126 changed files with 4009 additions and 1132 deletions

No files matched your search

+9 -9
View File
@@ -19,17 +19,17 @@ import java.util.jar.JarFile
import java.util.jar.Pack200
import java.util.zip.GZIPOutputStream
import java.security.MessageDigest
plugins {
id "edu.sc.seis.macAppBundle" version "2.1.6"
id "me.tatarka.retrolambda" version "3.5.0"
id 'edu.sc.seis.launch4j' version '2.3.0'
}
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
plugins {
id "edu.sc.seis.macAppBundle" version "2.1.8"
id "me.tatarka.retrolambda" version "3.7.0"
id 'edu.sc.seis.launch4j' version '2.4.2'
//id "org.jetbrains.kotlin.jvm" version "1.1.2-2"
}
if (!hasProperty('mainClass')) {
ext.mainClass = 'org.jackhuang.hmcl.Main'
}
@@ -38,11 +38,11 @@ def buildnumber = System.getenv("TRAVIS_BUILD_NUMBER")
if (buildnumber == null)
buildnumber = System.getenv("BUILD_NUMBER")
if (buildnumber == null)
buildnumber = "25"
buildnumber = "52"
def versionroot = System.getenv("VERSION_ROOT")
if (versionroot == null)
versionroot = "2.7.6"
versionroot = "2.7.9"
String mavenGroupId = 'HMCL'
String mavenVersion = versionroot + '.' + buildnumber
@@ -20,8 +20,10 @@ package org.jackhuang.hmcl;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.Locale;
@@ -32,19 +34,20 @@ import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.RepaintManager;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.api.ILogger;
import org.jackhuang.hmcl.api.PluginManager;
import org.jackhuang.hmcl.api.VersionNumber;
import org.jackhuang.hmcl.core.MCUtils;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.ui.LogWindow;
import org.jackhuang.hmcl.ui.MainFrame;
import org.jackhuang.hmcl.util.CrashReporter;
import org.jackhuang.hmcl.util.DefaultPlugin;
import org.jackhuang.hmcl.util.MathUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.lang.SupportedLocales;
import org.jackhuang.hmcl.util.log.Configuration;
@@ -97,6 +100,11 @@ public final class Main {
public static void main(String[] args) throws IOException {
{
if (new File("").getAbsolutePath().contains("!")) {
MessageBox.show("HMCL cannot run at such path that contains '!'");
return;
}
try {
File file = new File("hmcl.log").getAbsoluteFile();
if (!file.exists() && !file.createNewFile())
@@ -176,8 +184,9 @@ public final class Main {
String s = Settings.getInstance().getLocalization();
for (SupportedLocales sl : SupportedLocales.values())
if (sl.name().equals(s)) {
SupportedLocales.NOW_LOCALE = sl;
SupportedLocales.setNowLocale(sl);
Locale.setDefault(sl.self);
JOptionPane.setDefaultLocale(sl.self);
}
if (System.getProperty("java.vm.name").contains("Open")) // OpenJDK
@@ -195,10 +204,24 @@ public final class Main {
Settings.UPDATE_CHECKER.upgrade.register(IUpgrader.NOW_UPGRADER);
Settings.UPDATE_CHECKER.process(false).reg(t -> Main.invokeUpdate()).execute();
if (StrUtils.isNotBlank(Settings.getInstance().getProxyHost()) && StrUtils.isNotBlank(Settings.getInstance().getProxyPort()) && MathUtils.canParseInt(Settings.getInstance().getProxyPort())) {
HMCLog.log("Initializing customized proxy");
System.setProperty("http.proxyHost", Settings.getInstance().getProxyHost());
System.setProperty("http.proxyPort", Settings.getInstance().getProxyPort());
if (Settings.getInstance().getProxyType() != Proxy.Type.DIRECT && StrUtils.isNotBlank(Settings.getInstance().getProxyHost())) {
switch (Settings.getInstance().getProxyType()) {
case DIRECT:
break;
case HTTP:
System.setProperty("http.proxyHost", Settings.getInstance().getProxyHost());
if (StrUtils.isNotBlank(Settings.getInstance().getProxyPort()))
System.setProperty("http.proxyPort", Settings.getInstance().getProxyPort());
HMCLog.log("Initialized HTTP proxy.");
break;
case SOCKS:
System.setProperty("socksProxyHost", Settings.getInstance().getProxyHost());
if (StrUtils.isNotBlank(Settings.getInstance().getProxyPort()))
System.setProperty("socksProxyPort", Settings.getInstance().getProxyPort());
HMCLog.log("Initialized Socks v4/v5 proxy.");
break;
}
if (StrUtils.isNotBlank(Settings.getInstance().getProxyUserName()) && StrUtils.isNotBlank(Settings.getInstance().getProxyPassword()))
Authenticator.setDefault(new Authenticator() {
@Override
@@ -207,6 +230,12 @@ public final class Main {
}
});
}
try {
unpackDefaultLog4jConfiguration();
} catch(IOException e) {
HMCLog.err("Failed to unpack log4j.xml, log window will not work well.", e);
}
MainFrame.showMainFrame();
}
@@ -215,6 +244,20 @@ public final class Main {
public static void invokeUpdate() {
MainFrame.INSTANCE.invokeUpdate();
}
public static final File LOG4J_FILE = new File(MCUtils.getWorkingDirectory("hmcl"), "log4j.xml");
public static void unpackDefaultLog4jConfiguration() throws IOException {
LOG4J_FILE.getParentFile().mkdirs();
if (LOG4J_FILE.exists()) return;
LOG4J_FILE.createNewFile();
try (InputStream is = Main.class.getResourceAsStream("/org/jackhuang/hmcl/log4j.xml");
FileOutputStream fos = new FileOutputStream(LOG4J_FILE)) {
int b;
while ((b = is.read()) != -1)
fos.write(b);
}
}
public static ImageIcon getIcon(String path) {
try {
@@ -19,6 +19,8 @@ package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.core.download.DownloadType;
import com.google.gson.annotations.SerializedName;
import java.awt.Font;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -59,6 +61,8 @@ public final class Config implements Cloneable {
private String proxyUserName;
@SerializedName("proxyPassword")
private String proxyPassword;
@SerializedName("proxyType")
private int proxyType;
@SerializedName("enableShadow")
private boolean enableShadow;
@SerializedName("enableBlur")
@@ -81,6 +85,31 @@ public final class Config implements Cloneable {
private TreeMap<String, Profile> configurations;
@SerializedName("auth")
private Map<String, Map> auth;
@SerializedName("fontFamily")
private String fontFamily;
@SerializedName("fontSize")
private int fontSize;
@SerializedName("logLines")
private int logLines;
public Config() {
clientToken = UUID.randomUUID().toString();
proxyType = logintype = downloadtype = 0;
logLines = 100;
enableAnimation = enableBlur = true;
if (OS.os() == OS.WINDOWS)
enableShadow = true;
theme = LAFTheme.BLUE.id;
decorated = OS.os() == OS.LINUX;
auth = new HashMap<>();
Font font = Font.decode("Consolas");
if (font == null)
font = Font.decode("Monospace");
if (font != null)
fontFamily = font.getFamily();
fontSize = 12;
commonpath = MCUtils.getLocation().getPath();
}
public List<JdkVersion> getJava() {
return java == null ? java = new ArrayList<>() : java;
@@ -107,7 +136,7 @@ public final class Config implements Cloneable {
}
public boolean isEnableShadow() {
return enableShadow;
return enableShadow && OS.os() == OS.WINDOWS;
}
public void setEnableShadow(boolean enableShadow) {
@@ -217,16 +246,6 @@ public final class Config implements Cloneable {
Settings.save();
}
public Config() {
clientToken = UUID.randomUUID().toString();
logintype = downloadtype = 0;
enableAnimation = enableBlur = enableShadow = true;
theme = LAFTheme.BLUE.id;
decorated = OS.os() == OS.LINUX;
auth = new HashMap<>();
commonpath = MCUtils.getLocation().getPath();
}
public DownloadType getDownloadSource() {
if (downloadtype >= DownloadType.values().length || downloadtype < 0) {
downloadtype = 0;
@@ -253,6 +272,19 @@ public final class Config implements Cloneable {
Settings.save();
}
public Proxy.Type getProxyType() {
if (proxyType < 0 || proxyType >= 3) {
proxyType = 0;
Settings.save();
}
return Proxy.Type.values()[proxyType];
}
public void setProxyType(Proxy.Type proxyType) {
this.proxyType = proxyType.ordinal();
Settings.save();
}
public String getProxyUserName() {
return proxyUserName == null ? "" : proxyUserName;
}
@@ -271,6 +303,28 @@ public final class Config implements Cloneable {
Settings.save();
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
Settings.save();
}
public Font getConsoleFont() {
return Font.decode(fontFamily + "-" + fontSize);
}
public String getFontFamily() {
return Font.decode(fontFamily).getFamily();
}
public void setFontFamily(String fontFamily) {
this.fontFamily = Font.decode(fontFamily).getFamily(); // avoid invalid font family
Settings.save();
}
public String getLocalization() {
return localization;
}
@@ -280,6 +334,18 @@ public final class Config implements Cloneable {
Settings.save();
}
public int getLogLines() {
if (logLines == 100 || logLines == 1000 || logLines == 5000)
return logLines;
else
return logLines = 100;
}
public void setLogLines(int logLines) {
this.logLines = logLines;
Settings.save();
}
@Override
public Object clone() {
try {
@@ -112,8 +112,7 @@ public final class Profile implements IProfile {
}
public void makeVersionSettingGlobal(String id) {
HMCLMinecraftService s = (HMCLMinecraftService) service();
VersionSetting vs = s.getVersionSetting(id);
VersionSetting vs = service().getVersionSetting(id);
if (vs == null)
return;
vs.setUsesGlobal(true);
@@ -30,9 +30,10 @@ import org.jackhuang.hmcl.util.CollectionUtils;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.config.ProfileChangedEvent;
import org.jackhuang.hmcl.api.event.config.ProfileLoadingEvent;
import org.jackhuang.hmcl.laf.utils.AnimationController;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.UpdateChecker;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.sys.IOUtils;
/**
@@ -56,6 +57,7 @@ public final class Settings {
static {
SETTINGS = initSettings();
DownloadType.setSuggestedDownloadType(SETTINGS.getDownloadSource().name());
AnimationController.ENABLE_ANIMATION = SETTINGS.isEnableAnimation();
if (!getProfiles().containsKey(DEFAULT_PROFILE))
getProfiles().put(DEFAULT_PROFILE, new Profile(DEFAULT_PROFILE));
@@ -310,7 +310,6 @@ public class VersionSetting {
x.setFullscreen(isFullscreen());
x.setWrapper(getWrapper());
x.setGameDir(gameDir);
x.setGameDirType(getGameDirType());
x.setHeight(getHeight());
x.setJavaArgs(getJavaArgs());
x.setLaunchVersion(id);
@@ -23,7 +23,7 @@ import org.jackhuang.hmcl.api.ui.TopTabPage;
import org.jackhuang.hmcl.core.download.MinecraftRemoteVersions;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.task.ProgressProviderListener;
import org.jackhuang.hmcl.util.task.Task;
@@ -86,7 +86,7 @@
<SubComponents>
<Container class="javax.swing.JPanel" name="pnlSettings">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page()"/>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
@@ -370,7 +370,9 @@
</Component>
<Component class="javax.swing.JLabel" name="lblUsesGlobal">
<Properties>
<Property name="text" type="java.lang.String" value="jLabel1"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="settings.type.none" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="&#x624b;&#x578b;&#x5149;&#x6807;"/>
</Property>
@@ -383,7 +385,7 @@
</Container>
<Container class="javax.swing.JPanel" name="pnlAdvancedSettings">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page()"/>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
@@ -593,7 +595,7 @@
</Container>
<Container class="javax.swing.JPanel" name="pnlModManagement">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page()"/>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
@@ -620,7 +622,7 @@
<SubComponents>
<Container class="javax.swing.JPanel" name="pnlModManagementContent">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page()"/>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Layout>
@@ -724,7 +726,7 @@
</Container>
<Container class="javax.swing.JPanel" name="pnlAutoInstall">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page()"/>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
@@ -36,7 +36,6 @@ import java.io.IOException;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
@@ -59,7 +58,7 @@ import org.jackhuang.hmcl.core.install.InstallerType;
import org.jackhuang.hmcl.api.game.GameDirType;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.AbstractSwingWorker;
import org.jackhuang.hmcl.util.MinecraftVersionRequest;
import org.jackhuang.hmcl.util.sys.OS;
@@ -110,7 +109,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
for (int i = 0; i < InstallerType.values().length; i++)
installerPanels[i] = new InstallerPanel(this, InstallerType.values()[i]);
pnlGameDownloads = new GameDownloadPanel(this);
pnlGameDownloads = new GameDownloadPanel(this).setAnimationEnabled(Settings.getInstance().isEnableAnimation());
initExplorationMenu();
initManagementMenu();
@@ -168,7 +167,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
JMenuItem itm = new JMenuItem(C.i18n("versions.manage.rename"));
itm.addActionListener((e) -> {
if (mcVersion != null) {
String newName = JOptionPane.showInputDialog(C.i18n("versions.manage.rename.message"), mcVersion);
String newName = MessageBox.showInputDialog(C.i18n("versions.manage.rename.message"), mcVersion);
if (newName != null)
if (Settings.getLastProfile().service().version().renameVersion(mcVersion, newName))
refreshVersions();
@@ -187,7 +186,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
itm = new JMenuItem(C.i18n("versions.manage.redownload_json"));
itm.addActionListener((e) -> {
if (mcVersion != null)
Settings.getLastProfile().service().download().downloadMinecraftVersionJson(mcVersion);
TaskWindow.factory().execute(Settings.getLastProfile().service().download().downloadMinecraftVersionJson(mcVersion));
});
ppmManage.add(itm);
itm = new JMenuItem(C.i18n("versions.manage.redownload_assets_index"));
@@ -278,7 +277,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
tabVersionEdit = new NewTabPane();
((NewTabPane)tabVersionEdit).initializing = true;
pnlSettings = new Page();
pnlSettings = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
lblGameDir = new javax.swing.JLabel();
txtGameDir = new javax.swing.JTextField();
lblDimension = new javax.swing.JLabel();
@@ -301,7 +300,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
btnChoosingGameDir = new javax.swing.JButton();
btnCleanGame = new javax.swing.JButton();
lblUsesGlobal = new javax.swing.JLabel();
pnlAdvancedSettings = new Page();
pnlAdvancedSettings = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
lblJavaArgs = new javax.swing.JLabel();
txtJavaArgs = new javax.swing.JTextField();
txtMinecraftArgs = new javax.swing.JTextField();
@@ -317,14 +316,14 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
txtWrapperLauncher = new javax.swing.JTextField();
chkDontCheckGame = new javax.swing.JCheckBox();
chkNoCommon = new javax.swing.JCheckBox();
pnlModManagement = new Page();
pnlModManagementContent = new Page();
pnlModManagement = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
pnlModManagementContent = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
jScrollPane1 = new javax.swing.JScrollPane();
lstExternalMods = new javax.swing.JTable();
btnAddMod = new javax.swing.JButton();
btnRemoveMod = new javax.swing.JButton();
lblModInfo = new javax.swing.JLabel();
pnlAutoInstall = new Page();
pnlAutoInstall = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
tabInstallers = new NewTabPane();
pnlTop = new javax.swing.JPanel();
pnlSelection = new javax.swing.JPanel();
@@ -452,7 +451,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
}
});
lblUsesGlobal.setText("jLabel1");
lblUsesGlobal.setText(C.i18n("settings.type.none")); // NOI18N
lblUsesGlobal.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
lblUsesGlobal.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
@@ -994,7 +993,7 @@ public final class GameSettingsPanel extends RepaintPage implements DropTargetLi
fc.setFileSelectionMode(JSystemFileChooser.DIRECTORIES_ONLY);
if (fc.showOpenDialog(this) == JSystemFileChooser.APPROVE_OPTION) {
File newGameDir = fc.getSelectedFile();
String name = JOptionPane.showInputDialog(C.i18n("setupwindow.give_a_name"));
String name = MessageBox.showInputDialog(C.i18n("setupwindow.give_a_name"));
if (StrUtils.isBlank(name)) {
MessageBox.show(C.i18n("setupwindow.no_empty_name"));
return;
@@ -28,7 +28,7 @@ import org.jackhuang.hmcl.core.install.InstallerType;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.util.task.TaskRunnable;
import org.jackhuang.hmcl.util.task.TaskWindow;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.task.ProgressProviderListener;
import org.jackhuang.hmcl.util.task.Task;
@@ -50,6 +50,8 @@ public class InstallerPanel extends Page implements ProgressProviderListener {
*/
public InstallerPanel(GameSettingsPanel gsp, InstallerType installerType) {
initComponents();
animationEnabled = Settings.getInstance().isEnableAnimation();
setOpaque(false);
this.gsp = gsp;
@@ -0,0 +1,101 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui;
import javax.swing.JTextPane;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BoxView;
import javax.swing.text.ComponentView;
import javax.swing.text.Element;
import javax.swing.text.IconView;
import javax.swing.text.LabelView;
import javax.swing.text.ParagraphView;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
/**
*
* @author huang
*/
public class JLineWrapTextPane extends JTextPane {
// 内部类
// 以下内部类全都用于实现自动强制折行
private class WarpEditorKit extends StyledEditorKit {
private ViewFactory defaultFactory = new WarpColumnFactory();
@Override
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
private class WarpColumnFactory implements ViewFactory {
public View create(Element elem) {
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new WarpLabelView(elem);
} else if (kind.equals(AbstractDocument.ParagraphElementName)) {
return new ParagraphView(elem);
} else if (kind.equals(AbstractDocument.SectionElementName)) {
return new BoxView(elem, View.Y_AXIS);
} else if (kind.equals(StyleConstants.ComponentElementName)) {
return new ComponentView(elem);
} else if (kind.equals(StyleConstants.IconElementName)) {
return new IconView(elem);
}
}
// default to text display
return new LabelView(elem);
}
}
private class WarpLabelView extends LabelView {
public WarpLabelView(Element elem) {
super(elem);
}
@Override
public float getMinimumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
// 本类
// 构造函数
public JLineWrapTextPane() {
super();
this.setEditorKit(new WarpEditorKit());
}
}
@@ -17,299 +17,30 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="lblProxy" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="lblProxyHost" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyHost" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblProxyPort" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyPort" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblProxyUserName" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyUsername" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblProxyPassword" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyPassword" min="-2" pref="80" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="chkDecorated" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="chkEnableShadow" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkEnableBlur" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkEnableAnimation" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lblAbout" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="btnCheckUpdate" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnMCBBS" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lblModpack" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblRestart" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblDownloadSource" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblTheme" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblBackground" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblLang" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblCommonPath" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="txtCommonPath" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnSetCommonPath" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cboLang" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="txtBackgroundPath" pref="585" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnSelBackgroundPath" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cboDownloadSource" alignment="1" max="32767" attributes="0"/>
<Component id="cboTheme" alignment="1" max="32767" attributes="0"/>
</Group>
</Group>
<Component id="lblModpack" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblRestart" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Component id="tabLauncherSettings" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblCommonPath" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtCommonPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="btnSetCommonPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblBackground" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnSelBackgroundPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="txtBackgroundPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblDownloadSource" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cboDownloadSource" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblTheme" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cboTheme" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblLang" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cboLang" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="txtProxyHost" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxy" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblProxyHost" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtProxyPort" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxyPort" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtProxyUsername" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxyUserName" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtProxyPassword" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxyPassword" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="chkEnableShadow" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="chkEnableBlur" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="chkEnableAnimation" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkDecorated" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="btnCheckUpdate" min="-2" pref="26" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="btnMCBBS" pref="26" max="32767" attributes="0"/>
<EmptySpace min="1" pref="1" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="lblRestart" min="-2" max="-2" attributes="0"/>
<Component id="tabLauncherSettings" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblModpack" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblAbout" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="lblRestart" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JComboBox" name="cboDownloadSource">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboDownloadSourceItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblAbout">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.about" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="&#x9ed8;&#x8ba4;&#x5149;&#x6807;"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnSelBackgroundPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="ui.button.explore" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnSelBackgroundPathActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblBackground">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.background_location" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtBackgroundPath">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.background_tooltip" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtBackgroundPathFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblDownloadSource">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.download_source" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnCheckUpdate">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.update_launcher" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCheckUpdateActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkEnableShadow">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.enable_shadow" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableShadowItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblTheme">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.theme" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="cboTheme">
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboThemeItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblProxy">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.proxy" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyHost">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyHostFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyHost">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.host" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyPort">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyPortFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyPort">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.port" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyUserName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.username" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyUsername">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyUsernameFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyPassword">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyPasswordFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyPassword">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.password" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="chkDecorated">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.decorated" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkDecoratedItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblModpack">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@@ -323,23 +54,6 @@
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="lblModpackMouseClicked"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="cboLang">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultComboBoxModel(new String[]{C.i18n(&quot;color.blue&quot;),C.i18n(&quot;color.green&quot;),C.i18n(&quot;color.purple&quot;),C.i18n(&quot;color.dark_blue&quot;),C.i18n(&quot;color.orange&quot;),C.i18n(&quot;color.red&quot;)})" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboLangItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblLang">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.lang" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblRestart">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@@ -347,61 +61,541 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnMCBBS">
<Properties>
<Property name="text" type="java.lang.String" value="MCBBS"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnMCBBSActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkEnableBlur">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.enable_blur" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableBlurItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkEnableAnimation">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.enable_animation" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableAnimationItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblCommonPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.common_location" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtCommonPath">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.commpath_tooltip" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtCommonPathFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnSetCommonPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="ui.button.explore" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnSetCommonPathActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JTabbedPane" name="tabLauncherSettings">
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" value=""/>
</AccessibilityProperties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new NewTabPane()"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="pnlGeneral">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="General">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.tab.general" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</JTabbedPaneConstraints>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="btnCheckUpdate" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnMCBBS" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblDownloadSource" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblCommonPath" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblLang" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblProxy" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="cboProxyType" pref="156" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblProxyHost" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyHost" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblProxyPort" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyPort" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblProxyUserName" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyUsername" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblProxyPassword" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtProxyPassword" min="-2" pref="80" max="-2" attributes="0"/>
</Group>
<Component id="cboLang" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="txtCommonPath" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnSetCommonPath" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cboDownloadSource" alignment="1" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblCommonPath" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtCommonPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="btnSetCommonPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblDownloadSource" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cboDownloadSource" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cboLang" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblLang" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="txtProxyHost" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxy" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblProxyHost" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtProxyPort" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxyPort" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtProxyUsername" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxyUserName" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtProxyPassword" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblProxyPassword" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cboProxyType" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="69" max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnCheckUpdate" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="btnMCBBS" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="lblProxy">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.proxy" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyHost">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.host" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyHost">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyHostFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyPort">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.port" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyPort">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyPortFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyUserName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.username" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyUsername">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyUsernameFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblProxyPassword">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="proxy.password" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtProxyPassword">
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtProxyPasswordFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="cboDownloadSource">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboDownloadSourceItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblDownloadSource">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.download_source" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblCommonPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.common_location" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtCommonPath">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.commpath_tooltip" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtCommonPathFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnSetCommonPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="ui.button.explore" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnSetCommonPathActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnCheckUpdate">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.update_launcher" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCheckUpdateActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnMCBBS">
<Properties>
<Property name="text" type="java.lang.String" value="MCBBS"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnMCBBSActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="cboLang">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultComboBoxModel(new String[]{C.i18n(&quot;color.blue&quot;),C.i18n(&quot;color.green&quot;),C.i18n(&quot;color.purple&quot;),C.i18n(&quot;color.dark_blue&quot;),C.i18n(&quot;color.orange&quot;),C.i18n(&quot;color.red&quot;)})" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboLangItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblLang">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.lang" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="cboProxyType">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="3">
<StringItem index="0" value="Direct"/>
<StringItem index="1" value="HTTP"/>
<StringItem index="2" value="Socks"/>
</StringArray>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboProxyTypeItemStateChanged"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnlUI">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="UI">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.tab.ui" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</JTabbedPaneConstraints>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="chkDecorated" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="chkEnableShadow" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkEnableBlur" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkEnableAnimation" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="163" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblBackground" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblFont" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lblTheme" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cboTheme" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="txtBackgroundPath" max="32767" attributes="0"/>
<Component id="cboFontFamily" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="btnSelBackgroundPath" max="32767" attributes="0"/>
<Component id="spinnerFontSize" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="lblExample" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblBackground" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtBackgroundPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="btnSelBackgroundPath" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cboFontFamily" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblFont" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spinnerFontSize" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblExample" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cboTheme" alignment="3" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lblTheme" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="63" max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="chkEnableShadow" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="chkEnableBlur" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="chkEnableAnimation" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkDecorated" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="lblTheme">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.theme" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="cboTheme">
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboThemeItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblBackground">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.background_location" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtBackgroundPath">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.background_tooltip" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="txtBackgroundPathFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkEnableShadow">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.enable_shadow" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableShadowItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkEnableBlur">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.enable_blur" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableBlurItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkEnableAnimation">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.enable_animation" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkEnableAnimationItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkDecorated">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.decorated" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="chkDecoratedItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnSelBackgroundPath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="ui.button.explore" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnSelBackgroundPathActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="cboFontFamily">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<SerializedValue value="-84,-19,0,5,115,114,0,32,106,97,118,97,120,46,115,119,105,110,103,46,68,101,102,97,117,108,116,67,111,109,98,111,66,111,120,77,111,100,101,108,92,-10,105,-80,-61,-104,-56,48,2,0,2,76,0,7,111,98,106,101,99,116,115,116,0,18,76,106,97,118,97,47,117,116,105,108,47,86,101,99,116,111,114,59,76,0,14,115,101,108,101,99,116,101,100,79,98,106,101,99,116,116,0,18,76,106,97,118,97,47,108,97,110,103,47,79,98,106,101,99,116,59,120,114,0,29,106,97,118,97,120,46,115,119,105,110,103,46,65,98,115,116,114,97,99,116,76,105,115,116,77,111,100,101,108,87,-79,61,-30,108,45,-87,-39,2,0,1,76,0,12,108,105,115,116,101,110,101,114,76,105,115,116,116,0,37,76,106,97,118,97,120,47,115,119,105,110,103,47,101,118,101,110,116,47,69,118,101,110,116,76,105,115,116,101,110,101,114,76,105,115,116,59,120,112,115,114,0,35,106,97,118,97,120,46,115,119,105,110,103,46,101,118,101,110,116,46,69,118,101,110,116,76,105,115,116,101,110,101,114,76,105,115,116,-79,54,-58,125,-124,-22,-42,68,3,0,0,120,112,116,0,34,106,97,118,97,120,46,115,119,105,110,103,46,101,118,101,110,116,46,76,105,115,116,68,97,116,97,76,105,115,116,101,110,101,114,115,114,0,21,106,97,118,97,120,46,115,119,105,110,103,46,74,67,111,109,98,111,66,111,120,115,-25,-16,-68,-83,109,105,-54,3,0,14,90,0,17,102,105,114,105,110,103,65,99,116,105,111,110,69,118,101,110,116,90,0,10,105,115,69,100,105,116,97,98,108,101,90,0,23,108,105,103,104,116,87,101,105,103,104,116,80,111,112,117,112,69,110,97,98,108,101,100,73,0,15,109,97,120,105,109,117,109,82,111,119,67,111,117,110,116,90,0,13,115,101,108,101,99,116,105,110,103,73,116,101,109,76,0,6,97,99,116,105,111,110,116,0,20,76,106,97,118,97,120,47,115,119,105,110,103,47,65,99,116,105,111,110,59,76,0,13,97,99,116,105,111,110,67,111,109,109,97,110,100,116,0,18,76,106,97,118,97,47,108,97,110,103,47,83,116,114,105,110,103,59,76,0,28,97,99,116,105,111,110,80,114,111,112,101,114,116,121,67,104,97,110,103,101,76,105,115,116,101,110,101,114,116,0,35,76,106,97,118,97,47,98,101,97,110,115,47,80,114,111,112,101,114,116,121,67,104,97,110,103,101,76,105,115,116,101,110,101,114,59,76,0,9,100,97,116,97,77,111,100,101,108,116,0,27,76,106,97,118,97,120,47,115,119,105,110,103,47,67,111,109,98,111,66,111,120,77,111,100,101,108,59,76,0,6,101,100,105,116,111,114,116,0,28,76,106,97,118,97,120,47,115,119,105,110,103,47,67,111,109,98,111,66,111,120,69,100,105,116,111,114,59,76,0,19,107,101,121,83,101,108,101,99,116,105,111,110,77,97,110,97,103,101,114,116,0,43,76,106,97,118,97,120,47,115,119,105,110,103,47,74,67,111,109,98,111,66,111,120,36,75,101,121,83,101,108,101,99,116,105,111,110,77,97,110,97,103,101,114,59,76,0,21,112,114,111,116,111,116,121,112,101,68,105,115,112,108,97,121,86,97,108,117,101,113,0,126,0,2,76,0,8,114,101,110,100,101,114,101,114,116,0,30,76,106,97,118,97,120,47,115,119,105,110,103,47,76,105,115,116,67,101,108,108,82,101,110,100,101,114,101,114,59,76,0,20,115,101,108,101,99,116,101,100,73,116,101,109,82,101,109,105,110,100,101,114,113,0,126,0,2,120,114,0,22,106,97,118,97,120,46,115,119,105,110,103,46,74,67,111,109,112,111,110,101,110,116,51,-17,99,38,30,108,-90,-113,3,0,16,70,0,10,97,108,105,103,110,109,101,110,116,88,70,0,10,97,108,105,103,110,109,101,110,116,89,90,0,11,97,117,116,111,115,99,114,111,108,108,115,73,0,5,102,108,97,103,115,90,0,15,105,115,65,108,105,103,110,109,101,110,116,88,83,101,116,90,0,15,105,115,65,108,105,103,110,109,101,110,116,89,83,101,116,90,0,26,118,101,114,105,102,121,73,110,112,117,116,87,104,101,110,70,111,99,117,115,84,97,114,103,101,116,76,0,9,97,99,116,105,111,110,77,97,112,116,0,23,76,106,97,118,97,120,47,115,119,105,110,103,47,65,99,116,105,111,110,77,97,112,59,76,0,16,97,110,99,101,115,116,111,114,73,110,112,117,116,77,97,112,116,0,22,76,106,97,118,97,120,47,115,119,105,110,103,47,73,110,112,117,116,77,97,112,59,76,0,6,98,111,114,100,101,114,116,0,27,76,106,97,118,97,120,47,115,119,105,110,103,47,98,111,114,100,101,114,47,66,111,114,100,101,114,59,76,0,13,102,111,99,117,115,73,110,112,117,116,77,97,112,113,0,126,0,19,76,0,13,105,110,112,117,116,86,101,114,105,102,105,101,114,116,0,27,76,106,97,118,97,120,47,115,119,105,110,103,47,73,110,112,117,116,86,101,114,105,102,105,101,114,59,76,0,12,108,105,115,116,101,110,101,114,76,105,115,116,113,0,126,0,4,76,0,9,112,111,112,117,112,77,101,110,117,116,0,24,76,106,97,118,97,120,47,115,119,105,110,103,47,74,80,111,112,117,112,77,101,110,117,59,76,0,21,118,101,116,111,97,98,108,101,67,104,97,110,103,101,83,117,112,112,111,114,116,116,0,34,76,106,97,118,97,47,98,101,97,110,115,47,86,101,116,111,97,98,108,101,67,104,97,110,103,101,83,117,112,112,111,114,116,59,76,0,14,119,105,110,100,111,119,73,110,112,117,116,77,97,112,116,0,31,76,106,97,118,97,120,47,115,119,105,110,103,47,67,111,109,112,111,110,101,110,116,73,110,112,117,116,77,97,112,59,120,114,0,18,106,97,118,97,46,97,119,116,46,67,111,110,116,97,105,110,101,114,64,7,-128,115,-3,20,12,39,3,0,8,73,0,30,99,111,110,116,97,105,110,101,114,83,101,114,105,97,108,105,122,101,100,68,97,116Line truncated
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboFontFamilyItemStateChanged"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JFontComboBox()"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="lblFont">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.log_font" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="spinnerFontSize">
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="spinnerFontSizeStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblExample">
<Properties>
<Property name="text" type="java.lang.String" value="19:10:10 [Client Thread/INFO]: Example"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnlAbout">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation())"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="About">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.tab.about" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</JTabbedPaneConstraints>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="lblAbout" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="252" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="lblAbout" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="lblAbout">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="launcher.about" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="&#x9ed8;&#x8ba4;&#x5149;&#x6807;"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.ui;
import java.io.IOException;
import java.net.Proxy;
import javax.swing.DefaultComboBoxModel;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.jackhuang.hmcl.util.C;
@@ -25,10 +26,13 @@ import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.api.ui.Theme;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.core.download.DownloadType;
import org.jackhuang.hmcl.util.MathUtils;
import org.jackhuang.hmcl.util.sys.IOUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.lang.SupportedLocales;
import org.jackhuang.hmcl.util.ui.JFontComboBox;
import org.jackhuang.hmcl.util.ui.JSystemFileChooser;
import org.jackhuang.hmcl.util.ui.Page;
import org.jackhuang.hmcl.util.ui.SwingUtils;
/**
@@ -72,7 +76,10 @@ public class LauncherSettingsPanel extends RepaintPage {
txtProxyPort.setText(Settings.getInstance().getProxyPort());
txtProxyUsername.setText(Settings.getInstance().getProxyUserName());
txtProxyPassword.setText(Settings.getInstance().getProxyPassword());
spinnerFontSize.setValue(Settings.getInstance().getFontSize());
cboFontFamily.setSelectedItem(Settings.getInstance().getFontFamily());
cboDownloadSource.setSelectedIndex(Settings.getInstance().getDownloadType());
cboProxyType.setSelectedIndex(Settings.getInstance().getProxyType().ordinal());
cboTheme.setSelectedItem(Settings.getInstance().getTheme());
chkEnableShadow.setSelected(Settings.getInstance().isEnableShadow());
chkEnableBlur.setSelected(Settings.getInstance().isEnableBlur());
@@ -95,127 +102,45 @@ public class LauncherSettingsPanel extends RepaintPage {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
cboDownloadSource = new javax.swing.JComboBox();
lblAbout = new javax.swing.JLabel();
btnSelBackgroundPath = new javax.swing.JButton();
lblBackground = new javax.swing.JLabel();
txtBackgroundPath = new javax.swing.JTextField();
lblDownloadSource = new javax.swing.JLabel();
btnCheckUpdate = new javax.swing.JButton();
chkEnableShadow = new javax.swing.JCheckBox();
lblTheme = new javax.swing.JLabel();
cboTheme = new javax.swing.JComboBox();
lblModpack = new javax.swing.JLabel();
lblRestart = new javax.swing.JLabel();
tabLauncherSettings = new NewTabPane();
pnlGeneral = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
lblProxy = new javax.swing.JLabel();
txtProxyHost = new javax.swing.JTextField();
lblProxyHost = new javax.swing.JLabel();
txtProxyPort = new javax.swing.JTextField();
txtProxyHost = new javax.swing.JTextField();
lblProxyPort = new javax.swing.JLabel();
txtProxyPort = new javax.swing.JTextField();
lblProxyUserName = new javax.swing.JLabel();
txtProxyUsername = new javax.swing.JTextField();
txtProxyPassword = new javax.swing.JTextField();
lblProxyPassword = new javax.swing.JLabel();
chkDecorated = new javax.swing.JCheckBox();
lblModpack = new javax.swing.JLabel();
cboLang = new javax.swing.JComboBox();
lblLang = new javax.swing.JLabel();
lblRestart = new javax.swing.JLabel();
btnMCBBS = new javax.swing.JButton();
chkEnableBlur = new javax.swing.JCheckBox();
chkEnableAnimation = new javax.swing.JCheckBox();
txtProxyPassword = new javax.swing.JTextField();
cboDownloadSource = new javax.swing.JComboBox();
lblDownloadSource = new javax.swing.JLabel();
lblCommonPath = new javax.swing.JLabel();
txtCommonPath = new javax.swing.JTextField();
btnSetCommonPath = new javax.swing.JButton();
cboDownloadSource.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboDownloadSourceItemStateChanged(evt);
}
});
lblAbout.setText(C.i18n("launcher.about")); // NOI18N
lblAbout.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
btnSelBackgroundPath.setText(C.i18n("ui.button.explore")); // NOI18N
btnSelBackgroundPath.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnSelBackgroundPathActionPerformed(evt);
}
});
lblBackground.setText(C.i18n("launcher.background_location")); // NOI18N
lblBackground.setToolTipText("");
txtBackgroundPath.setToolTipText(C.i18n("launcher.background_tooltip")); // NOI18N
txtBackgroundPath.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtBackgroundPathFocusLost(evt);
}
});
lblDownloadSource.setText(C.i18n("launcher.download_source")); // NOI18N
btnCheckUpdate.setText(C.i18n("launcher.update_launcher")); // NOI18N
btnCheckUpdate.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnCheckUpdateActionPerformed(evt);
}
});
chkEnableShadow.setText(C.i18n("launcher.enable_shadow")); // NOI18N
chkEnableShadow.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkEnableShadowItemStateChanged(evt);
}
});
lblTheme.setText(C.i18n("launcher.theme")); // NOI18N
cboTheme.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboThemeItemStateChanged(evt);
}
});
lblProxy.setText(C.i18n("launcher.proxy")); // NOI18N
txtProxyHost.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyHostFocusLost(evt);
}
});
lblProxyHost.setText(C.i18n("proxy.host")); // NOI18N
txtProxyPort.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyPortFocusLost(evt);
}
});
lblProxyPort.setText(C.i18n("proxy.port")); // NOI18N
lblProxyUserName.setText(C.i18n("proxy.username")); // NOI18N
txtProxyUsername.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyUsernameFocusLost(evt);
}
});
txtProxyPassword.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyPasswordFocusLost(evt);
}
});
lblProxyPassword.setText(C.i18n("proxy.password")); // NOI18N
chkDecorated.setText(C.i18n("launcher.decorated")); // NOI18N
chkDecorated.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkDecoratedItemStateChanged(evt);
}
});
btnCheckUpdate = new javax.swing.JButton();
btnMCBBS = new javax.swing.JButton();
cboLang = new javax.swing.JComboBox();
lblLang = new javax.swing.JLabel();
cboProxyType = new javax.swing.JComboBox<>();
pnlUI = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
lblTheme = new javax.swing.JLabel();
cboTheme = new javax.swing.JComboBox();
lblBackground = new javax.swing.JLabel();
txtBackgroundPath = new javax.swing.JTextField();
chkEnableShadow = new javax.swing.JCheckBox();
chkEnableBlur = new javax.swing.JCheckBox();
chkEnableAnimation = new javax.swing.JCheckBox();
chkDecorated = new javax.swing.JCheckBox();
btnSelBackgroundPath = new javax.swing.JButton();
cboFontFamily = new JFontComboBox();
lblFont = new javax.swing.JLabel();
spinnerFontSize = new javax.swing.JSpinner();
lblExample = new javax.swing.JLabel();
pnlAbout = new Page().setAnimationEnabled(Settings.getInstance().isEnableAnimation());
lblAbout = new javax.swing.JLabel();
lblModpack.setText(C.i18n("launcher.modpack")); // NOI18N
lblModpack.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
@@ -225,39 +150,50 @@ public class LauncherSettingsPanel extends RepaintPage {
}
});
cboLang.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")}));
cboLang.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboLangItemStateChanged(evt);
}
});
lblLang.setText(C.i18n("launcher.lang")); // NOI18N
lblRestart.setText(C.i18n("launcher.restart")); // NOI18N
btnMCBBS.setText("MCBBS");
btnMCBBS.setToolTipText("");
btnMCBBS.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnMCBBSActionPerformed(evt);
lblProxy.setText(C.i18n("launcher.proxy")); // NOI18N
lblProxyHost.setText(C.i18n("proxy.host")); // NOI18N
txtProxyHost.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyHostFocusLost(evt);
}
});
chkEnableBlur.setText(C.i18n("launcher.enable_blur")); // NOI18N
chkEnableBlur.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkEnableBlurItemStateChanged(evt);
lblProxyPort.setText(C.i18n("proxy.port")); // NOI18N
txtProxyPort.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyPortFocusLost(evt);
}
});
chkEnableAnimation.setText(C.i18n("launcher.enable_animation")); // NOI18N
chkEnableAnimation.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkEnableAnimationItemStateChanged(evt);
lblProxyUserName.setText(C.i18n("proxy.username")); // NOI18N
txtProxyUsername.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyUsernameFocusLost(evt);
}
});
lblProxyPassword.setText(C.i18n("proxy.password")); // NOI18N
txtProxyPassword.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtProxyPasswordFocusLost(evt);
}
});
cboDownloadSource.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboDownloadSourceItemStateChanged(evt);
}
});
lblDownloadSource.setText(C.i18n("launcher.download_source")); // NOI18N
lblCommonPath.setText(C.i18n("launcher.common_location")); // NOI18N
txtCommonPath.setToolTipText(C.i18n("launcher.commpath_tooltip")); // NOI18N
@@ -274,97 +210,101 @@ public class LauncherSettingsPanel extends RepaintPage {
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
btnCheckUpdate.setText(C.i18n("launcher.update_launcher")); // NOI18N
btnCheckUpdate.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnCheckUpdateActionPerformed(evt);
}
});
btnMCBBS.setText("MCBBS");
btnMCBBS.setToolTipText("");
btnMCBBS.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnMCBBSActionPerformed(evt);
}
});
cboLang.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")}));
cboLang.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboLangItemStateChanged(evt);
}
});
lblLang.setText(C.i18n("launcher.lang")); // NOI18N
cboProxyType.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Direct", "HTTP", "Socks" }));
cboProxyType.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboProxyTypeItemStateChanged(evt);
}
});
javax.swing.GroupLayout pnlGeneralLayout = new javax.swing.GroupLayout(pnlGeneral);
pnlGeneral.setLayout(pnlGeneralLayout);
pnlGeneralLayout.setHorizontalGroup(
pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlGeneralLayout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(lblProxy)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lblProxyHost)
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlGeneralLayout.createSequentialGroup()
.addComponent(btnCheckUpdate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyHost, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblProxyPort)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyPort, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblProxyUserName)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyUsername, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblProxyPassword)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(chkDecorated)
.addGroup(layout.createSequentialGroup()
.addComponent(chkEnableShadow)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkEnableBlur)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkEnableAnimation))
.addComponent(lblAbout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addComponent(btnCheckUpdate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnMCBBS))
.addComponent(lblModpack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblRestart))
.addComponent(btnMCBBS)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlGeneralLayout.createSequentialGroup()
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblDownloadSource)
.addComponent(lblTheme)
.addComponent(lblBackground)
.addComponent(lblCommonPath)
.addComponent(lblLang)
.addComponent(lblCommonPath))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(lblProxy))
.addGap(28, 28, 28)
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlGeneralLayout.createSequentialGroup()
.addComponent(cboProxyType, 0, 156, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblProxyHost)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyHost, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblProxyPort)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyPort, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblProxyUserName)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyUsername, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblProxyPassword)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(cboLang, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(pnlGeneralLayout.createSequentialGroup()
.addComponent(txtCommonPath)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnSetCommonPath))
.addComponent(cboLang, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(txtBackgroundPath, javax.swing.GroupLayout.DEFAULT_SIZE, 585, Short.MAX_VALUE)
.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))))
.addComponent(cboDownloadSource, 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()
pnlGeneralLayout.setVerticalGroup(
pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlGeneralLayout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblCommonPath)
.addComponent(txtCommonPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnSetCommonPath, 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(lblBackground)
.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)
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblDownloadSource)
.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(lblTheme)
.addComponent(cboTheme, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cboLang, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblLang))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblLang)
.addComponent(cboLang, 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)
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(txtProxyHost, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblProxy)
.addComponent(lblProxyHost)
@@ -373,28 +313,201 @@ public class LauncherSettingsPanel extends RepaintPage {
.addComponent(txtProxyUsername, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblProxyUserName)
.addComponent(txtProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblProxyPassword))
.addComponent(lblProxyPassword)
.addComponent(cboProxyType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 69, Short.MAX_VALUE)
.addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnCheckUpdate, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnMCBBS, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
);
tabLauncherSettings.addTab(C.i18n("launcher.tab.general"), pnlGeneral); // NOI18N
lblTheme.setText(C.i18n("launcher.theme")); // NOI18N
cboTheme.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboThemeItemStateChanged(evt);
}
});
lblBackground.setText(C.i18n("launcher.background_location")); // NOI18N
lblBackground.setToolTipText("");
txtBackgroundPath.setToolTipText(C.i18n("launcher.background_tooltip")); // NOI18N
txtBackgroundPath.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
txtBackgroundPathFocusLost(evt);
}
});
chkEnableShadow.setText(C.i18n("launcher.enable_shadow")); // NOI18N
chkEnableShadow.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkEnableShadowItemStateChanged(evt);
}
});
chkEnableBlur.setText(C.i18n("launcher.enable_blur")); // NOI18N
chkEnableBlur.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkEnableBlurItemStateChanged(evt);
}
});
chkEnableAnimation.setText(C.i18n("launcher.enable_animation")); // NOI18N
chkEnableAnimation.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkEnableAnimationItemStateChanged(evt);
}
});
chkDecorated.setText(C.i18n("launcher.decorated")); // NOI18N
chkDecorated.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
chkDecoratedItemStateChanged(evt);
}
});
btnSelBackgroundPath.setText(C.i18n("ui.button.explore")); // NOI18N
btnSelBackgroundPath.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnSelBackgroundPathActionPerformed(evt);
}
});
cboFontFamily.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboFontFamilyItemStateChanged(evt);
}
});
lblFont.setText(C.i18n("launcher.log_font")); // NOI18N
spinnerFontSize.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
spinnerFontSizeStateChanged(evt);
}
});
lblExample.setText("19:10:10 [Client Thread/INFO]: Example");
javax.swing.GroupLayout pnlUILayout = new javax.swing.GroupLayout(pnlUI);
pnlUI.setLayout(pnlUILayout);
pnlUILayout.setHorizontalGroup(
pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlUILayout.createSequentialGroup()
.addContainerGap()
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlUILayout.createSequentialGroup()
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(chkDecorated)
.addGroup(pnlUILayout.createSequentialGroup()
.addComponent(chkEnableShadow)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkEnableBlur)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkEnableAnimation)))
.addGap(0, 163, Short.MAX_VALUE))
.addGroup(pnlUILayout.createSequentialGroup()
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblBackground)
.addComponent(lblFont)
.addComponent(lblTheme))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(cboTheme, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(pnlUILayout.createSequentialGroup()
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(txtBackgroundPath)
.addComponent(cboFontFamily, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(btnSelBackgroundPath, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(spinnerFontSize)))
.addGroup(pnlUILayout.createSequentialGroup()
.addComponent(lblExample)
.addGap(0, 0, Short.MAX_VALUE)))))
.addContainerGap())
);
pnlUILayout.setVerticalGroup(
pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlUILayout.createSequentialGroup()
.addContainerGap()
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblBackground)
.addComponent(txtBackgroundPath, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnSelBackgroundPath, 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)
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cboFontFamily, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblFont)
.addComponent(spinnerFontSize, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblExample)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cboTheme, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblTheme))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 63, Short.MAX_VALUE)
.addGroup(pnlUILayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(chkEnableShadow)
.addComponent(chkEnableBlur)
.addComponent(chkEnableAnimation))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkDecorated)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(btnCheckUpdate, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addComponent(btnMCBBS, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE)
.addGap(1, 1, 1)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblRestart)
.addContainerGap())
);
tabLauncherSettings.addTab(C.i18n("launcher.tab.ui"), pnlUI); // NOI18N
lblAbout.setText(C.i18n("launcher.about")); // NOI18N
lblAbout.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
javax.swing.GroupLayout pnlAboutLayout = new javax.swing.GroupLayout(pnlAbout);
pnlAbout.setLayout(pnlAboutLayout);
pnlAboutLayout.setHorizontalGroup(
pnlAboutLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlAboutLayout.createSequentialGroup()
.addContainerGap()
.addComponent(lblAbout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(252, Short.MAX_VALUE))
);
pnlAboutLayout.setVerticalGroup(
pnlAboutLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnlAboutLayout.createSequentialGroup()
.addContainerGap()
.addComponent(lblAbout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
tabLauncherSettings.addTab(C.i18n("launcher.tab.about"), pnlAbout); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblModpack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblRestart))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(tabLauncherSettings)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(tabLauncherSettings)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblModpack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblAbout, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblRestart)
.addContainerGap())
);
tabLauncherSettings.getAccessibleContext().setAccessibleName("");
}// </editor-fold>//GEN-END:initComponents
private void cboDownloadSourceItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboDownloadSourceItemStateChanged
@@ -501,13 +614,29 @@ public class LauncherSettingsPanel extends RepaintPage {
Settings.getInstance().setCommonpath(txtCommonPath.getText());
}//GEN-LAST:event_txtCommonPathFocusLost
private void cboFontFamilyItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboFontFamilyItemStateChanged
Settings.getInstance().setFontFamily(evt.getItem().toString());
lblExample.setFont(Settings.getInstance().getConsoleFont());
}//GEN-LAST:event_cboFontFamilyItemStateChanged
private void spinnerFontSizeStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerFontSizeStateChanged
Settings.getInstance().setFontSize(MathUtils.parseInt(spinnerFontSize.getValue().toString(), 12));
lblExample.setFont(Settings.getInstance().getConsoleFont());
}//GEN-LAST:event_spinnerFontSizeStateChanged
private void cboProxyTypeItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboProxyTypeItemStateChanged
Settings.getInstance().setProxyType(Proxy.Type.values()[cboProxyType.getSelectedIndex()]);
}//GEN-LAST:event_cboProxyTypeItemStateChanged
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnCheckUpdate;
private javax.swing.JButton btnMCBBS;
private javax.swing.JButton btnSelBackgroundPath;
private javax.swing.JButton btnSetCommonPath;
private javax.swing.JComboBox cboDownloadSource;
private javax.swing.JComboBox cboFontFamily;
private javax.swing.JComboBox cboLang;
private javax.swing.JComboBox<String> cboProxyType;
private javax.swing.JComboBox cboTheme;
private javax.swing.JCheckBox chkDecorated;
private javax.swing.JCheckBox chkEnableAnimation;
@@ -517,6 +646,8 @@ public class LauncherSettingsPanel extends RepaintPage {
private javax.swing.JLabel lblBackground;
private javax.swing.JLabel lblCommonPath;
private javax.swing.JLabel lblDownloadSource;
private javax.swing.JLabel lblExample;
private javax.swing.JLabel lblFont;
private javax.swing.JLabel lblLang;
private javax.swing.JLabel lblModpack;
private javax.swing.JLabel lblProxy;
@@ -526,6 +657,11 @@ public class LauncherSettingsPanel extends RepaintPage {
private javax.swing.JLabel lblProxyUserName;
private javax.swing.JLabel lblRestart;
private javax.swing.JLabel lblTheme;
private javax.swing.JPanel pnlAbout;
private javax.swing.JPanel pnlGeneral;
private javax.swing.JPanel pnlUI;
private javax.swing.JSpinner spinnerFontSize;
private javax.swing.JTabbedPane tabLauncherSettings;
private javax.swing.JTextField txtBackgroundPath;
private javax.swing.JTextField txtCommonPath;
private javax.swing.JTextField txtProxyHost;
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.ui;
import java.io.File;
import java.io.IOException;
import javax.swing.JOptionPane;
import java.io.PipedOutputStream;
import javax.swing.SwingUtilities;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.process.JVMLaunchFailedEvent;
@@ -35,14 +35,17 @@ import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.util.HMCLGameLauncher;
import org.jackhuang.hmcl.util.MinecraftCrashAdvicer;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.api.func.Consumer;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.api.event.launch.LaunchingState;
import org.jackhuang.hmcl.util.DefaultPlugin;
import org.jackhuang.hmcl.util.Log4jHandler;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.PrintlnEvent;
import org.jackhuang.hmcl.util.sys.ProcessMonitor;
import org.xml.sax.XMLReader;
/**
*
@@ -50,6 +53,9 @@ import org.jackhuang.hmcl.util.sys.ProcessMonitor;
*/
public class LaunchingUIDaemon {
XMLReader reader;
Log4jHandler logHandler;
public LaunchingUIDaemon() {
HMCLApi.EVENT_BUS.channel(LaunchingStateChangedEvent.class).register(LAUNCHING_STATE_CHANGED);
HMCLApi.EVENT_BUS.channel(LaunchEvent.class).register(p -> {
@@ -68,8 +74,19 @@ public class LaunchingUIDaemon {
// We promise that JavaProcessMonitor.tag is LauncherVisibility
// See events below.
ProcessMonitor monitor = new ProcessMonitor(p.getValue());
monitor.registerPrintlnEvent(PRINTLN);
PipedOutputStream os = new PipedOutputStream();
monitor.setTag(obj);
try {
logHandler = new Log4jHandler(monitor, os, false);
logHandler.addForbiddenToken(obj.getLoginResult().getAccessToken(), "<access token>");
logHandler.addForbiddenToken(obj.getLoginResult().getSession(), "<session>");
logHandler.addForbiddenToken(obj.getLoginResult().getUserId(), "<uuid>");
logHandler.addForbiddenToken("Setting user: " + obj.getLoginResult().getUserName(), "<player>");
logHandler.start();
} catch(Exception e) {
HMCLog.err("", e);
}
monitor.registerPrintlnEvent(new PrintlnProcessor(obj, os));
monitor.start();
});
HMCLApi.EVENT_BUS.channel(LaunchSucceededEvent.class).register(p -> {
@@ -80,8 +97,10 @@ public class LaunchingUIDaemon {
LAUNCH_SCRIPT_FINISHER.accept(p);
});
HMCLApi.EVENT_BUS.channel(JavaProcessStoppedEvent.class).register(event -> {
GameLauncher launcher = ((GameLauncher) ((ProcessMonitor) event.getSource()).getTag());
HMCLApi.EVENT_BUS.fireChannel(new LaunchingStateChangedEvent(launcher, LaunchingState.Done));
checkExit(unpackProcessMonitor(event.getSource()));
});
});
HMCLApi.EVENT_BUS.channel(JavaProcessExitedAbnormallyEvent.class).register(event -> {
ProcessMonitor monitor = (ProcessMonitor) event.getSource();
int exitCode = event.getValue().getExitCode();
@@ -110,7 +129,7 @@ public class LaunchingUIDaemon {
noExitThisTime = true;
});
}
static LauncherVisibility unpackProcessMonitor(Object obj) {
GameLauncher launcher = ((GameLauncher) ((ProcessMonitor) obj).getTag());
HMCLGameLauncher.GameLauncherTag tag = (HMCLGameLauncher.GameLauncherTag) launcher.getTag();
@@ -144,18 +163,37 @@ public class LaunchingUIDaemon {
}, MainFrame.INSTANCE::failed, Settings.getInstance().getAuthenticator().getPassword());
}
private static final Consumer<PrintlnEvent> PRINTLN = t -> {
GameLauncher launcher = ((GameLauncher) ((ProcessMonitor) t.getSource()).getTag());
HMCLGameLauncher.GameLauncherTag tag = (HMCLGameLauncher.GameLauncherTag) launcher.getTag();
LauncherVisibility l = tag.launcherVisibility;
if (t.getLine().contains("LWJGL Version: ") && l != LauncherVisibility.KEEP)
if (l != LauncherVisibility.HIDE_AND_REOPEN)
MainFrame.INSTANCE.dispose();
else { // If current state is 'hide and reopen', closes the main window and reset the state to normal.
MainFrame.INSTANCE.setVisible(false);
HMCLApi.EVENT_BUS.fireChannel(new LaunchingStateChangedEvent(launcher, LaunchingState.Done));
class PrintlnProcessor implements Consumer<PrintlnEvent> {
GameLauncher launcher;
PipedOutputStream os;
public PrintlnProcessor(GameLauncher launcher, PipedOutputStream os) {
this.launcher = launcher;
this.os = os;
}
@Override
public void accept(PrintlnEvent t) {
String log = t.getLine();
if (!t.isError() && logHandler != null) {
logHandler.newLogLine(log + C.LINE_SEPARATOR);
}
};
else System.err.println(t.getLine());
HMCLGameLauncher.GameLauncherTag tag = (HMCLGameLauncher.GameLauncherTag) launcher.getTag();
LauncherVisibility l = tag.launcherVisibility;
if (t.getLine().contains("LWJGL Version: ") && l != LauncherVisibility.KEEP)
SwingUtilities.invokeLater(() -> {
if (l != LauncherVisibility.HIDE_AND_REOPEN)
MainFrame.INSTANCE.dispose();
else { // If current state is 'hide and reopen', closes the main window and reset the state to normal.
MainFrame.INSTANCE.setVisible(false);
HMCLApi.EVENT_BUS.fireChannel(new LaunchingStateChangedEvent(launcher, LaunchingState.Done));
}
});
}
}
private static final Consumer<LaunchingStateChangedEvent> LAUNCHING_STATE_CHANGED = t -> {
String message = null;
@@ -193,21 +231,18 @@ public class LaunchingUIDaemon {
private void checkExit(LauncherVisibility v) {
if (v == LauncherVisibility.HIDE_AND_REOPEN) {
HMCLog.log("Launcher will not exit now.");
MainFrame.INSTANCE.setVisible(true);
} else if (v != LauncherVisibility.KEEP && !LogWindow.INSTANCE.isVisible() && !noExitThisTime) {
HMCLog.log("Launcher will exit now.");
System.exit(0);
} else {
HMCLog.log("Launcher will not exit now.");
noExitThisTime = false;
}
}
private static final Consumer<LaunchSucceededEvent> LAUNCH_SCRIPT_FINISHER = event -> {
try {
String s = JOptionPane.showInputDialog(C.i18n("mainwindow.enter_script_name"));
if (s != null)
String s = MessageBox.showInputDialog(C.i18n("mainwindow.enter_script_name"));
if (StrUtils.isNotBlank(s))
MessageBox.show(C.i18n("mainwindow.make_launch_succeed") + " " + ((GameLauncher) event.getSource()).makeLauncher(s, event.getValue()).getAbsolutePath());
} catch (IOException ex) {
MessageBox.show(C.i18n("mainwindow.make_launch_script_failed"));
@@ -215,4 +250,5 @@ public class LaunchingUIDaemon {
}
MainFrame.INSTANCE.closeMessage();
};
}
@@ -29,19 +29,12 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="pnlLog" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="btnTieBa" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnMCBBS" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnMCF" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnGitHub" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="btnContact" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="144" max="32767" attributes="0"/>
<Component id="btnTerminateGame" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCopy" min="-2" max="-2" attributes="0"/>
@@ -51,6 +44,26 @@
<Component id="btnClose" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lblCrash" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="lblShowLines" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cboShowLines" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblSearch" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtSearch" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnFatal" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnError" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnWarn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnInfo" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnDebug" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="pnlLog" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@@ -59,20 +72,29 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblShowLines" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cboShowLines" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnDebug" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnInfo" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnWarn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnError" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnFatal" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtSearch" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblSearch" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblCrash" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pnlLog" pref="356" max="32767" attributes="0"/>
<Component id="pnlLog" pref="281" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnClear" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnClose" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnCopy" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnMCBBS" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnTieBa" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnMCF" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnContact" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnTerminateGame" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnGitHub" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@@ -117,30 +139,14 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnMCBBS">
<Properties>
<Property name="text" type="java.lang.String" value="MCBBS"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnMCBBSActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnTieBa">
<Component class="javax.swing.JButton" name="btnContact">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/I18N.properties" key="logwindow.tieba" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="logwindow.contact" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnTieBaActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnMCF">
<Properties>
<Property name="text" type="java.lang.String" value="Minecraft Forum"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnMCFActionPerformed"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnContactActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnTerminateGame">
@@ -153,14 +159,6 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnTerminateGameActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnGitHub">
<Properties>
<Property name="text" type="java.lang.String" value="GitHub"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnGitHubActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="pnlLog">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
@@ -169,8 +167,79 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextPane" name="txtLog">
<Properties>
<Property name="editable" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JLineWrapTextPane()"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="lblShowLines">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="logwindow.show_lines" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="cboShowLines">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="3">
<StringItem index="0" value="100"/>
<StringItem index="1" value="1000"/>
<StringItem index="2" value="5000"/>
</StringArray>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cboShowLinesItemStateChanged"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JToggleButton" name="btnDebug">
<Properties>
<Property name="text" type="java.lang.String" value="0 debug"/>
</Properties>
</Component>
<Component class="javax.swing.JToggleButton" name="btnInfo">
<Properties>
<Property name="text" type="java.lang.String" value="0 info"/>
</Properties>
</Component>
<Component class="javax.swing.JToggleButton" name="btnWarn">
<Properties>
<Property name="text" type="java.lang.String" value="0 warn"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnError">
<Properties>
<Property name="text" type="java.lang.String" value="0 error"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnFatal">
<Properties>
<Property name="text" type="java.lang.String" value="0 fatal"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="txtSearch">
<Properties>
<Property name="toolTipText" type="java.lang.String" value=""/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="txtSearchActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblSearch">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/jackhuang/hmcl/lang/I18N.properties" key="logwindow.search" replaceFormat="C.i18n(&quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>
@@ -18,14 +18,19 @@
package org.jackhuang.hmcl.ui;
import java.io.PrintStream;
import java.util.Deque;
import java.util.LinkedList;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.log.Level;
import org.jackhuang.hmcl.api.func.NonFunction;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.util.DoubleOutputStream;
import org.jackhuang.hmcl.util.Utils;
import org.jackhuang.hmcl.util.sys.ProcessMonitor;
@@ -37,6 +42,8 @@ import org.jackhuang.hmcl.util.ui.SwingUtils;
*/
public class LogWindow extends javax.swing.JFrame {
public static LogWindowOutputStream outputStream;
NonFunction<Boolean> listener;
/**
@@ -45,14 +52,16 @@ public class LogWindow extends javax.swing.JFrame {
public LogWindow() {
initComponents();
DoubleOutputStream out = new DoubleOutputStream(new LogWindowOutputStream(this, Level.INFO), System.out);
DoubleOutputStream out = new DoubleOutputStream(outputStream = new LogWindowOutputStream(this, Level.INFO), System.out);
System.setOut(new PrintStream(out));
DoubleOutputStream err = new DoubleOutputStream(new LogWindowOutputStream(this, Level.ERROR), System.err);
System.setErr(new PrintStream(err));
SwingUtilities.invokeLater(() -> {
setLocationRelativeTo(null);
txtLog.setEditable(false);
//txtLog.setEditable(false);
txtLog.setFont(Settings.getInstance().getConsoleFont());
cboShowLines.setSelectedItem(String.valueOf(Settings.getInstance().getLogLines()));
});
}
@@ -71,13 +80,19 @@ public class LogWindow extends javax.swing.JFrame {
btnClose = new javax.swing.JButton();
btnCopy = new javax.swing.JButton();
lblCrash = new javax.swing.JLabel();
btnMCBBS = new javax.swing.JButton();
btnTieBa = new javax.swing.JButton();
btnMCF = new javax.swing.JButton();
btnContact = new javax.swing.JButton();
btnTerminateGame = new javax.swing.JButton();
btnGitHub = new javax.swing.JButton();
pnlLog = new javax.swing.JScrollPane();
txtLog = new javax.swing.JTextPane();
txtLog = new JLineWrapTextPane();
lblShowLines = new javax.swing.JLabel();
cboShowLines = new javax.swing.JComboBox<>();
btnDebug = new javax.swing.JToggleButton();
btnInfo = new javax.swing.JToggleButton();
btnWarn = new javax.swing.JToggleButton();
btnError = new javax.swing.JButton();
btnFatal = new javax.swing.JButton();
txtSearch = new javax.swing.JTextField();
lblSearch = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle(C.i18n("logwindow.title")); // NOI18N
@@ -110,24 +125,10 @@ public class LogWindow extends javax.swing.JFrame {
lblCrash.setText(C.i18n("ui.label.crashing")); // NOI18N
btnMCBBS.setText("MCBBS");
btnMCBBS.addActionListener(new java.awt.event.ActionListener() {
btnContact.setText(C.i18n("logwindow.contact")); // NOI18N
btnContact.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnMCBBSActionPerformed(evt);
}
});
btnTieBa.setText(C.i18n("logwindow.tieba")); // NOI18N
btnTieBa.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnTieBaActionPerformed(evt);
}
});
btnMCF.setText("Minecraft Forum");
btnMCF.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnMCFActionPerformed(evt);
btnContactActionPerformed(evt);
}
});
@@ -138,32 +139,48 @@ public class LogWindow extends javax.swing.JFrame {
}
});
btnGitHub.setText("GitHub");
btnGitHub.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnGitHubActionPerformed(evt);
txtLog.setEditable(false);
pnlLog.setViewportView(txtLog);
lblShowLines.setText(C.i18n("logwindow.show_lines")); // NOI18N
lblShowLines.setToolTipText("");
cboShowLines.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "100", "1000", "5000" }));
cboShowLines.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cboShowLinesItemStateChanged(evt);
}
});
pnlLog.setViewportView(txtLog);
btnDebug.setText("0 debug");
btnInfo.setText("0 info");
btnWarn.setText("0 warn");
btnError.setText("0 error");
btnFatal.setText("0 fatal");
txtSearch.setToolTipText("");
txtSearch.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
txtSearchActionPerformed(evt);
}
});
lblSearch.setText(C.i18n("logwindow.search")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(pnlLog)
.addGroup(layout.createSequentialGroup()
.addComponent(btnTieBa)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnMCBBS)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnMCF)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnGitHub)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(btnContact)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 144, Short.MAX_VALUE)
.addComponent(btnTerminateGame)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCopy)
@@ -171,26 +188,53 @@ public class LogWindow extends javax.swing.JFrame {
.addComponent(btnClear)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnClose))
.addComponent(lblCrash, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(lblCrash, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(lblShowLines)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cboShowLines, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblSearch)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtSearch)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnFatal)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnError)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnWarn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnInfo)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnDebug))
.addComponent(pnlLog))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblShowLines)
.addComponent(cboShowLines, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnDebug)
.addComponent(btnInfo)
.addComponent(btnWarn)
.addComponent(btnError)
.addComponent(btnFatal)
.addComponent(txtSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblSearch))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblCrash)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pnlLog, javax.swing.GroupLayout.DEFAULT_SIZE, 356, Short.MAX_VALUE)
.addComponent(pnlLog, javax.swing.GroupLayout.DEFAULT_SIZE, 281, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnClear)
.addComponent(btnClose)
.addComponent(btnCopy)
.addComponent(btnMCBBS)
.addComponent(btnTieBa)
.addComponent(btnMCF)
.addComponent(btnTerminateGame)
.addComponent(btnGitHub))
.addComponent(btnContact)
.addComponent(btnTerminateGame))
.addContainerGap())
);
@@ -208,50 +252,123 @@ public class LogWindow extends javax.swing.JFrame {
}//GEN-LAST:event_btnClearActionPerformed
private void btnCopyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCopyActionPerformed
Utils.setClipborad(this.txtLog.getText());
Utils.setClipboard(this.txtLog.getText());
}//GEN-LAST:event_btnCopyActionPerformed
private void btnMCBBSActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMCBBSActionPerformed
SwingUtils.openLink(C.URL_PUBLISH);
}//GEN-LAST:event_btnMCBBSActionPerformed
private void btnTieBaActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTieBaActionPerformed
SwingUtils.openLink(C.URL_TIEBA);
}//GEN-LAST:event_btnTieBaActionPerformed
private void btnMCFActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMCFActionPerformed
SwingUtils.openLink(C.URL_MINECRAFTFORUM);
}//GEN-LAST:event_btnMCFActionPerformed
private void btnContactActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnContactActionPerformed
SwingUtils.openLink(C.URL_CONTACT);
}//GEN-LAST:event_btnContactActionPerformed
private void btnTerminateGameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTerminateGameActionPerformed
terminateGames();
}//GEN-LAST:event_btnTerminateGameActionPerformed
private void btnGitHubActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnGitHubActionPerformed
SwingUtils.openLink(C.URL_GITHUB);
}//GEN-LAST:event_btnGitHubActionPerformed
private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
if (listener != null && listener.apply())
terminateGames();
SwingUtils.exitIfNoWindow(this);
}//GEN-LAST:event_formWindowClosing
private void cboShowLinesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cboShowLinesItemStateChanged
Settings.getInstance().setLogLines(Integer.parseInt(cboShowLines.getSelectedItem().toString()));
}//GEN-LAST:event_cboShowLinesItemStateChanged
private void txtSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtSearchActionPerformed
if (evt.getSource() != txtSearch || txtSearch.getText().isEmpty())
return;
try {
if (!s.equals(txtSearch.getText()))
pos = 0;
StyledDocument d = txtLog.getStyledDocument();
s = txtSearch.getText();
String content = d.getText(0, d.getLength());
int x = content.indexOf(s, pos);
if (x == -1) {
pos = 0;
x = content.indexOf(s, pos);
}
if (x == -1)
return;
int len = s.length();
pos = x + len;
SimpleAttributeSet set = new SimpleAttributeSet(d.getLogicalStyle(x).copyAttributes());
StyleConstants.setBackground(set, txtLog.getSelectionColor());
StyleConstants.setForeground(set, txtLog.getSelectedTextColor());
d.setCharacterAttributes(x, len, set, true);
set = new SimpleAttributeSet(d.getLogicalStyle(lastX).copyAttributes());
StyleConstants.setBackground(set, txtLog.getBackground());
StyleConstants.setForeground(set, txtLog.getForeground());
d.setCharacterAttributes(lastX, lastLen, set, true);
txtLog.select(x, pos);
lastX = x;
lastLen = len;
} catch (BadLocationException e) {
}
}//GEN-LAST:event_txtSearchActionPerformed
String s = "";
int pos = 0, lastX = 0, lastLen = 0;
void terminateGames() {
ProcessMonitor.stopAll();
}
int removedLength = 0;
Deque<Integer> offsets = new LinkedList<>();
int fatals = 0, errors = 0, warns = 0, infos = 0, debugs = 0;
public void log(final String status, final Level c) {
if (!isVisible())
return;
SwingUtilities.invokeLater(() -> {
String newStatus = status.replace("\t", " ");
Document d = txtLog.getStyledDocument();
try { // prevent too much memory used.
if (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() > 1l * 1024 * 1024 * 256) {
d.remove(0, d.getLength());
offsets.clear();
removedLength = 0;
}
} catch (Exception ex) {
HMCLog.err("Failed to clear the text component", ex);
}
String newStatus = status.replace("\t", " ");
SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setForeground(sas, c.COLOR);
try {
offsets.add(d.getLength() + removedLength);
d.insertString(d.getLength(), newStatus, sas);
} catch (Exception ex) {
HMCLog.err("Failed to insert \"" + newStatus + "\" to " + d.getLength(), ex);
}
switch (c) {
case FATAL:
btnFatal.setText(++fatals + " fatals");
break;
case ERROR:
btnError.setText(++errors + " errors");
break;
case WARN:
btnWarn.setText(++warns + " warns");
break;
case INFO:
btnInfo.setText(++infos + " infos");
break;
case DEBUG:
btnDebug.setText(++debugs + " debugs");
break;
}
int maxLines = Integer.parseInt(cboShowLines.getSelectedItem().toString());
while (offsets.size() > maxLines) {
int start = offsets.pollFirst();
int end = offsets.peekFirst();
try {
d.remove(start - removedLength, end - start); // start - removedLength must become 0
removedLength = end;
} catch (Exception ignore) {
}
}
});
}
@@ -265,25 +382,21 @@ public class LogWindow extends javax.swing.JFrame {
@Override
public void setVisible(boolean b) {
txtLog.setFont(Settings.getInstance().getConsoleFont());
lblCrash.setVisible(false);
btnMCBBS.setVisible(false);
btnTieBa.setVisible(false);
btnMCF.setVisible(false);
btnContact.setVisible(false);
super.setVisible(b);
}
public void showAsCrashWindow(boolean out_date) {
txtLog.setFont(Settings.getInstance().getConsoleFont());
if (out_date) {
lblCrash.setVisible(false);
btnMCBBS.setVisible(false);
btnTieBa.setVisible(false);
btnMCF.setVisible(false);
btnContact.setVisible(false);
lblCrash.setText(C.i18n("ui.label.crashing_out_dated"));
} else {
lblCrash.setVisible(true);
btnMCBBS.setVisible(true);
btnTieBa.setVisible(true);
btnMCF.setVisible(true);
btnContact.setVisible(true);
lblCrash.setText(C.i18n("ui.label.crashing"));
}
@@ -293,14 +406,20 @@ public class LogWindow extends javax.swing.JFrame {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnClear;
private javax.swing.JButton btnClose;
private javax.swing.JButton btnContact;
private javax.swing.JButton btnCopy;
private javax.swing.JButton btnGitHub;
private javax.swing.JButton btnMCBBS;
private javax.swing.JButton btnMCF;
private javax.swing.JToggleButton btnDebug;
private javax.swing.JButton btnError;
private javax.swing.JButton btnFatal;
private javax.swing.JToggleButton btnInfo;
private javax.swing.JButton btnTerminateGame;
private javax.swing.JButton btnTieBa;
private javax.swing.JToggleButton btnWarn;
private javax.swing.JComboBox<String> cboShowLines;
private javax.swing.JLabel lblCrash;
private javax.swing.JLabel lblSearch;
private javax.swing.JLabel lblShowLines;
private javax.swing.JScrollPane pnlLog;
private javax.swing.JTextPane txtLog;
private javax.swing.JTextField txtSearch;
// End of variables declaration//GEN-END:variables
}
@@ -45,10 +45,19 @@ public class LogWindowOutputStream extends OutputStream {
public final void write(byte[] arr, int off, int len) {
append(new String(arr, off, len));
}
final Object obj = new Object();
Level lastLevel = null;
private void append(final String str) {
synchronized(this) {
if (manual) return;
}
SwingUtilities.invokeLater(() -> {
txt.log(str, Level.guessLevel(str, sas));
Level level = Level.guessLevel(str);
if (level == null) level = lastLevel;
else lastLevel = level;
txt.log(str, Level.mergeLevel(sas, level));
});
}
@@ -56,4 +65,19 @@ public class LogWindowOutputStream extends OutputStream {
public final void write(int i) {
append(new String(new byte[] { (byte) i }));
}
}
boolean manual = false;
public void log(String s, Level l) {
synchronized(this) {
manual = true;
System.out.print(s);
manual = false;
}
if (l == null) append(s);
else
SwingUtilities.invokeLater(() -> {
txt.log(s, l);
});
}
}
@@ -57,7 +57,7 @@ import org.jackhuang.hmcl.api.PluginManager;
import org.jackhuang.hmcl.api.event.config.ThemeChangedEvent;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.ui.BasicColors;
@@ -69,7 +69,6 @@ import org.jackhuang.hmcl.api.auth.IAuthenticator;
import org.jackhuang.hmcl.api.ui.Theme;
import org.jackhuang.hmcl.api.ui.TopTabPage;
/**
*
* @author huangyuhui
@@ -256,7 +255,7 @@ public final class MainFrame extends DraggableFrame implements IRepaint {
realPanel.setBounds(1, 0, 800, 511);
add(realPanel);
reloadColor(Settings.getInstance().getTheme());
}
@@ -394,11 +393,12 @@ public final class MainFrame extends DraggableFrame implements IRepaint {
}
public void reloadColor(Theme t) {
for (Map.Entry<String, String> entry : t.settings.entrySet()) {
if (isShowedMessage)
return;
for (Map.Entry<String, String> entry : t.settings.entrySet())
if (entry.getValue().startsWith("#"))
UIManager.put(entry.getKey(), GraphicsUtils.getWebColor(entry.getValue()));
}
initBorderColor();
if (border != null)
border.setColor(borderColor);
@@ -421,14 +421,14 @@ public final class MainFrame extends DraggableFrame implements IRepaint {
@Override
public void paint(Graphics g) {
if (!enableShadow)
int off = enableShadow ? 16 : 0;
int width = this.getWidth();
int height = this.getHeight();
int contentWidth = width - off - off;
int contentHeight = height - off - off;
if (!enableShadow || contentWidth <= 0 || contentHeight <= 0)
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();
@@ -26,7 +26,6 @@ import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.jackhuang.hmcl.api.HMCLApi;
@@ -55,6 +54,7 @@ import org.jackhuang.hmcl.util.ui.SwingUtils;
import org.jackhuang.hmcl.util.ui.wizard.api.WizardDisplayer;
import org.jackhuang.hmcl.api.auth.IAuthenticator;
import org.jackhuang.hmcl.laf.button.CustomButtonUI;
import org.jackhuang.hmcl.util.ui.MessageBox;
/**
*
@@ -68,6 +68,8 @@ public class MainPagePanel extends Page {
void initGui() {
initComponents();
animationEnabled = Settings.getInstance().isEnableAnimation();
pnlButtons = new javax.swing.JPanel();
pnlButtons.setLayout(null);
@@ -400,7 +402,9 @@ public class MainPagePanel extends Page {
fc.showOpenDialog(this);
if (fc.getSelectedFile() == null)
return;
String suggestedModpackId = JOptionPane.showInputDialog("Please enter your favourite game name", FileUtils.getBaseName(fc.getSelectedFile().getName()));
String suggestedModpackId = MessageBox.showInputDialog(C.i18n("modpack.enter_name"), FileUtils.getBaseName(fc.getSelectedFile().getName()));
if (StrUtils.isBlank(suggestedModpackId))
return;
TaskWindow.factory().append(ModpackManager.install(MainFrame.INSTANCE, fc.getSelectedFile(), Settings.getLastProfile().service(), suggestedModpackId)).execute();
Settings.getLastProfile().service().version().refreshVersions();
}//GEN-LAST:event_btnImportModpackActionPerformed
@@ -517,7 +521,7 @@ public class MainPagePanel extends Page {
isLoading = false;
}
final Consumer<LaunchingStateChangedEvent> launchingStateChanged = t -> SwingUtils.setEnabled(MainFrame.INSTANCE.getRootPane(), t.getValue() == LaunchingState.Done);
final Consumer<LaunchingStateChangedEvent> launchingStateChanged = t -> SwingUtilities.invokeLater(() -> SwingUtils.setEnabled(MainFrame.INSTANCE.getRootPane(), t.getValue() == LaunchingState.Done));
void versionChanged(String selectedVersion) {
isLoading = true;
@@ -23,7 +23,7 @@ import java.io.IOException;
import java.util.Map;
import javax.swing.JOptionPane;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.net.WebPage;
import org.jackhuang.hmcl.util.ui.wizard.spi.WizardController;
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -33,7 +33,7 @@ public class HMCLAssetService extends MinecraftAssetService {
}
private boolean useSelf(String assetId) {
return new File(service.baseDirectory(), "assets/indexes/" + assetId + ".json").exists() || ((HMCLMinecraftService) service).p.isNoCommon();
return new File(service.baseDirectory(), "assets/indexes/" + assetId + ".json").exists() || ((HMCLMinecraftService) service).getProfile().isNoCommon();
}
@Override
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.setting.VersionSetting;
*
* @author huangyuhui
*/
public class HMCLGameProvider extends MinecraftVersionManager {
public class HMCLGameProvider extends MinecraftVersionManager<HMCLMinecraftService> {
public HMCLGameProvider(HMCLMinecraftService p) {
super(p);
@@ -37,9 +37,9 @@ public class HMCLGameProvider extends MinecraftVersionManager {
@Override
public File getLibraryFile(MinecraftVersion version, IMinecraftLibrary lib) {
VersionSetting vs = ((HMCLMinecraftService) service).getVersionSetting(version.id);
VersionSetting vs = service.getProfile().getVersionSetting(version.id);
File self = super.getLibraryFile(version, lib);
if (self.exists() || (vs != null && ((HMCLMinecraftService) service).p.isNoCommon()))
if (self == null || self.exists() || (vs != null && service.getProfile().isNoCommon()))
return self;
else
return lib.getFilePath(new File(Settings.getInstance().getCommonpath()));
@@ -47,7 +47,7 @@ public class HMCLGameProvider extends MinecraftVersionManager {
@Override
public File getRunDirectory(String id) {
VersionSetting vs = ((HMCLMinecraftService) service).getVersionSetting(id);
VersionSetting vs = service.getProfile().getVersionSetting(id);
if (vs == null)
return super.getRunDirectory(id);
else
@@ -0,0 +1,46 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.api.game.LaunchOptions;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.launch.MinecraftLoader;
import org.jackhuang.hmcl.core.service.IMinecraftService;
/**
*
* @author huang
*/
public class HMCLMinecraftLoader extends MinecraftLoader {
public HMCLMinecraftLoader(LaunchOptions p, IMinecraftService provider, UserProfileProvider lr) throws GameException {
super(p, provider, lr);
}
@Override
protected Map<String, String> getConfigurations() {
Map<String, String> map = super.getConfigurations();
map.put("${launcher_version}", Main.LAUNCHER_VERSION);
map.put("${launcher_name}", Main.LAUNCHER_NAME);
return map;
}
}
@@ -18,12 +18,8 @@
package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.service.IMinecraftAssetService;
import org.jackhuang.hmcl.core.service.IMinecraftLoader;
import org.jackhuang.hmcl.core.service.IMinecraftDownloadService;
import org.jackhuang.hmcl.core.service.IMinecraftProvider;
import org.jackhuang.hmcl.core.service.IMinecraftModService;
import org.jackhuang.hmcl.core.service.IMinecraftInstallerService;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.util.HashMap;
@@ -35,7 +31,6 @@ import org.jackhuang.hmcl.api.event.version.RefreshedVersionsEvent;
import org.jackhuang.hmcl.api.event.version.RefreshingVersionsEvent;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.install.MinecraftInstallerService;
import org.jackhuang.hmcl.core.asset.MinecraftAssetService;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.core.download.MinecraftDownloadService;
import org.jackhuang.hmcl.api.game.LaunchOptions;
@@ -55,13 +50,13 @@ import org.jackhuang.hmcl.util.task.TaskWindow;
*/
public class HMCLMinecraftService extends IMinecraftService {
Profile p;
private Profile p;
final Map<String, VersionSetting> versionSettings = new HashMap<>();
public HMCLMinecraftService(Profile p) {
this.p = p;
this.provider = new HMCLGameProvider(this);
provider.initializeMiencraft();
provider.initializeMinecraft();
HMCLApi.EVENT_BUS.channel(RefreshingVersionsEvent.class).register(versionSettings::clear);
HMCLApi.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerFirst(() -> {
if (!checkingModpack) {
@@ -143,10 +138,10 @@ public class HMCLMinecraftService extends IMinecraftService {
return p.getGameDir();
}
protected IMinecraftProvider provider;
protected HMCLGameProvider provider;
@Override
public IMinecraftProvider version() {
public HMCLGameProvider version() {
return provider;
}
@@ -160,27 +155,27 @@ public class HMCLMinecraftService extends IMinecraftService {
protected MinecraftDownloadService mds;
@Override
public IMinecraftDownloadService download() {
public MinecraftDownloadService download() {
return mds;
}
final MinecraftAssetService mas;
final HMCLAssetService mas;
@Override
public IMinecraftAssetService asset() {
public HMCLAssetService asset() {
return mas;
}
protected MinecraftInstallerService mis;
@Override
public IMinecraftInstallerService install() {
public MinecraftInstallerService install() {
return mis;
}
@Override
public IMinecraftLoader launch(LaunchOptions options, UserProfileProvider p) throws GameException {
MinecraftLoader l = new MinecraftLoader(options, this, p);
MinecraftLoader l = new HMCLMinecraftLoader(options, this, p);
l.setAssetProvider(mas.ASSET_PROVIDER_IMPL);
return l;
}
@@ -0,0 +1,149 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
import java.io.PipedOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.process.JavaProcessStoppedEvent;
import org.jackhuang.hmcl.api.func.Consumer;
import org.jackhuang.hmcl.ui.LogWindow;
import org.jackhuang.hmcl.util.log.Level;
import org.jackhuang.hmcl.util.sys.ProcessMonitor;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
*
* @author huang
*/
public class Log4jHandler extends Thread implements Consumer<JavaProcessStoppedEvent> {
ProcessMonitor monitor;
List<Pair<String, String>> forbiddenTokens = new LinkedList<>();
AtomicBoolean interrupted = new AtomicBoolean(false);
boolean enabled = true;
public Log4jHandler(ProcessMonitor monitor, PipedOutputStream outputStream, boolean enabled) {
this.monitor = monitor;
this.enabled = enabled;
HMCLApi.EVENT_BUS.channel(JavaProcessStoppedEvent.class).register((Consumer<JavaProcessStoppedEvent>) this);
}
public void addForbiddenToken(String token, String replacement) {
forbiddenTokens.add(new Pair<>(token, replacement));
}
@Override
public void run() {
}
@Override
public void accept(JavaProcessStoppedEvent t) {
}
/**
* Always use this instance method to write to the log,
* to make sure only this Thread writes to the PipedOutputStream.
*
* It will also flush the buffer, directly after write.
*
* This prevents java.io.IOException: Read end dead
* See also https://techtavern.wordpress.com/2008/07/16/whats-this-ioexception-write-end-dead/
*
* @param content The content to be written to the log
*/
public Future newLogLine(String content) {
printlnImpl(content, Level.guessLevel(content));
return null;
}
class Log4jHandlerImpl extends DefaultHandler {
private final SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
String date = "", thread = "", logger = "";
StringBuilder message = null;
Level l = null;
boolean readingMessage = false;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (localName) {
case "log4j_Event":
message = new StringBuilder();
Date d = new Date(Long.valueOf(attributes.getValue("timestamp")));
date = df.format(d);
try {
l = Level.valueOf(attributes.getValue("level"));
} catch (IllegalArgumentException e) {
l = Level.INFO;
}
thread = attributes.getValue("thread");
logger = attributes.getValue("logger");
if ("STDERR".equals(logger))
l = Level.ERROR;
break;
case "log4j_Message":
readingMessage = true;
break;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
switch (localName) {
case "log4j_Event":
println("[" + date + "] [" + thread + "/" + l.name() + "] [" + logger + "] " + message.toString(), l);
break;
case "log4j_Message":
readingMessage = false;
break;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String line = new String(ch, start, length);
if (line.trim().isEmpty())
return;
if (readingMessage)
message.append(line).append(C.LINE_SEPARATOR);
else
println(line, Level.guessLevel(line));
}
public void println(String message, Level l) {
printlnImpl(message, l);
}
}
private void printlnImpl(String message, Level l) {
for (Pair<String, String> entry : forbiddenTokens)
message = message.replace(entry.key, entry.value);
if (LogWindow.outputStream != null)
LogWindow.outputStream.log(message, l);
}
}
@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.lang.SupportedLocales;
/**
@@ -34,7 +33,7 @@ public final class MinecraftCrashAdvicer {
public static String getAdvice(String trace, boolean selfCrash) {
if (trace == null)
return C.i18n("crash.advice.no");
trace = trace.toLowerCase(SupportedLocales.NOW_LOCALE.self);
trace = trace.toLowerCase(SupportedLocales.getNowLocale().self);
if (trace.contains("pixel format not accelerated"))
return C.i18n("crash.advice.LWJGLException");
else if (trace.contains("unsupportedclassversionrrror"))
@@ -46,8 +46,7 @@ import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskWindow;
import org.jackhuang.hmcl.util.net.FileDownloadTask;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.UpdateChecker;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.Utils;
import org.jackhuang.hmcl.api.VersionNumber;
import org.jackhuang.hmcl.util.StrUtils;
@@ -137,17 +136,17 @@ public class AppDataUpgrader extends IUpgrader {
else {
String url = C.URL_PUBLISH;
if (map != null)
if (map.containsKey(OS.os().checked_name))
url = map.get(OS.os().checked_name);
else if (map.containsKey(OS.UNKOWN.checked_name))
url = map.get(OS.UNKOWN.checked_name);
if (map.containsKey(OS.os().checkedName))
url = map.get(OS.os().checkedName);
else if (map.containsKey(OS.UNKNOWN.checkedName))
url = map.get(OS.UNKNOWN.checkedName);
if (url == null)
url = C.URL_PUBLISH;
try {
java.awt.Desktop.getDesktop().browse(new URI(url));
} catch (URISyntaxException | IOException e) {
HMCLog.err("Failed to browse uri: " + url, e);
Utils.setClipborad(url);
Utils.setClipboard(url);
MessageBox.show(C.i18n("update.no_browser"));
}
}
@@ -15,8 +15,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
package org.jackhuang.hmcl.util.upgrade;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.api.VersionNumber;
import org.jackhuang.hmcl.api.event.EventHandler;
import org.jackhuang.hmcl.util.net.NetUtils;
@@ -27,6 +28,9 @@ import java.util.Map;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.SimpleEvent;
import org.jackhuang.hmcl.api.event.OutOfDateEvent;
import org.jackhuang.hmcl.util.AbstractSwingWorker;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.IUpdateChecker;
/**
*
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="SysOut" target="SYSTEM_OUT">
<LegacyXMLLayout />
<XMLLayout />
</Console>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<OnStartupTriggeringPolicy />
</Policies>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<filters>
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
</filters>
<AppenderRef ref="SysOut"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
+1
View File
@@ -18,6 +18,7 @@
plugins {
id "me.tatarka.retrolambda" version "3.5.0"
//id "org.jetbrains.kotlin.jvm" version "1.1.2-2"
}
if (System.getenv("BUILD_NUMBER") != null)
@@ -37,8 +37,8 @@ public interface IMinecraftLibrary extends Cloneable {
File getFilePath(File gameDir);
String getName();
boolean isRequiredToUnzip();
boolean isNative();
String getDownloadURL(String downloadSource);
@@ -29,7 +29,6 @@ public class LaunchOptions {
private String proxyHost, proxyPort, proxyUser, proxyPass, javaDir, launchVersion, type, precalledCommand;
private boolean fullscreen, noJVMArgs, notCheckGame;
private File gameDir;
private GameDirType gameDirType;
public String getVersionName() {
return versionName;
@@ -115,14 +114,6 @@ public class LaunchOptions {
this.fullscreen = fullscreen;
}
public GameDirType getGameDirType() {
return gameDirType;
}
public void setGameDirType(GameDirType gameDirType) {
this.gameDirType = gameDirType;
}
public String getPermSize() {
return permSize;
}
+1
View File
@@ -18,6 +18,7 @@
plugins {
id "me.tatarka.retrolambda" version "3.5.0"
//id "org.jetbrains.kotlin.jvm" version "1.1.2-2"
}
if (System.getenv("BUILD_NUMBER") != null)
@@ -30,12 +30,14 @@ import org.jackhuang.hmcl.core.service.IMinecraftAssetService;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.version.AssetIndexDownloadInfo;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.core.version.LoggingInfo;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskWindow;
import org.jackhuang.hmcl.util.net.FileDownloadTask;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.task.ParallelTask;
import org.jackhuang.hmcl.util.task.TaskInfo;
/**
@@ -56,7 +58,21 @@ public class MinecraftAssetService extends IMinecraftAssetService {
public Task downloadAssets(final MinecraftVersion mv) throws GameException {
if (mv == null)
return null;
return IAssetsHandler.ASSETS_HANDLER.getList(mv.resolve(service.version()), service.asset()).with(IAssetsHandler.ASSETS_HANDLER.getDownloadTask(service.getDownloadType().getProvider()));
Task task = IAssetsHandler.ASSETS_HANDLER.getList(mv.resolve(service.version()), service.asset())
.with(IAssetsHandler.ASSETS_HANDLER.getDownloadTask(service.getDownloadType().getProvider()));
if (mv.logging != null && mv.logging.containsKey("client")) {
LoggingInfo info = mv.logging.get("client");
File file = getLoggingObject(mv.assetIndex.getId(), info);
if (!file.exists())
return new ParallelTask().addTask(task)
.addTask(new FileDownloadTask(info.file.url, file, info.file.sha1));
}
return task;
}
@Override
public File getLoggingObject(String assetId, LoggingInfo logging) {
return new File(getAssets(assetId), "log_configs/" + logging.file.getId());
}
@Override
@@ -104,6 +120,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
HMCLog.warn("Failed to make directories: " + assetsDir);
File assetsIndex = getIndexFile(assetIndex.getId());
File renamed = null;
// This is unnecessary to do such backup job, FileDownloadTask can do this.
if (assetsIndex.exists()) {
renamed = new File(assetsDir, "indexes/" + assetIndex.getId() + "-renamed.json");
if (assetsIndex.renameTo(renamed))
@@ -134,14 +151,16 @@ public class MinecraftAssetService extends IMinecraftAssetService {
@Override
public File getAssetObject(String assetId, String name) throws IOException {
try {
AssetsIndex index = (AssetsIndex) C.GSON.fromJson(FileUtils.read(getIndexFile(assetId), "UTF-8"), AssetsIndex.class);
return getAssetObject(assetId, (AssetsObject) index.getFileMap().get(name));
AssetsIndex index = C.GSON.fromJson(FileUtils.read(getIndexFile(assetId), "UTF-8"), AssetsIndex.class);
if (index == null || index.getFileMap() == null || index.getFileMap().get(name) == null)
throw new IOException("Assets file format malformed.");
return getAssetObject(assetId, index.getFileMap().get(name));
} catch (JsonSyntaxException e) {
throw new IOException("Assets file format malformed.", e);
}
}
protected boolean checkAssetsExistance(AssetIndexDownloadInfo assetIndex) {
protected boolean checkAssetsExistence(AssetIndexDownloadInfo assetIndex, LoggingInfo info) {
String assetId = assetIndex.getId();
File indexFile = getIndexFile(assetId);
File assetDir = getAssets(assetId);
@@ -155,6 +174,10 @@ public class MinecraftAssetService extends IMinecraftAssetService {
if (index == null)
return false;
if (info != null && !getLoggingObject(assetId, info).exists())
return false;
for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet())
if (!assetObjectPath(assetDir, (AssetsObject) entry.getValue()).exists())
return false;
@@ -186,8 +209,8 @@ public class MinecraftAssetService extends IMinecraftAssetService {
HMCLog.log("Reconstructing virtual assets folder at " + virtualRoot);
int tot = index.getFileMap().entrySet().size();
for (Map.Entry<String, AssetsObject> entry : index.getFileMap().entrySet()) {
File target = new File(virtualRoot, (String) entry.getKey());
File original = assetObjectPath(assetsDir, (AssetsObject) entry.getValue());
File target = new File(virtualRoot, entry.getKey());
File original = assetObjectPath(assetsDir, entry.getValue());
if (original.exists()) {
cnt++;
if (!target.isFile())
@@ -215,7 +238,10 @@ public class MinecraftAssetService extends IMinecraftAssetService {
}
public final IAssetProvider ASSET_PROVIDER_IMPL = (t, allow) -> {
if (allow && !checkAssetsExistance(t.getAssetsIndex()))
LoggingInfo logging = null;
if (t.logging != null)
logging = t.logging.get("client");
if (allow && !checkAssetsExistence(t.getAssetsIndex(), logging))
if (MessageBox.show(C.i18n("assets.no_assets"), MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
TaskWindow.factory().execute(downloadAssets(t));
return reconstructAssets(t.getAssetsIndex()).getAbsolutePath();
@@ -59,7 +59,12 @@ public final class OfflineAuthenticator extends AbstractAuthenticator {
public UserProfileProvider login(LoginInfo info) throws AuthenticationException {
if (StrUtils.isBlank(info.username))
throw new AuthenticationException(C.i18n("login.no_Player007"));
String uuid = getUUIDFromUserName(info.username);
String uuid;
try {
uuid = getUUIDFromUserName(info.username);
} catch(IllegalArgumentException e) {
throw new AuthenticationException("Could not find md5 digest algorithm, please reinstall Java or even your OS.", e);
}
if (uuidMap != null && uuidMap.containsKey(info.username) && uuidMap.get(info.username) instanceof String)
uuid = (String) uuidMap.get(info.username);
else {
@@ -23,13 +23,13 @@ import org.jackhuang.hmcl.api.auth.AuthenticationException;
import com.google.gson.GsonBuilder;
import java.net.Proxy;
import java.util.Map;
import javax.swing.JOptionPane;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.core.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.core.auth.yggdrasil.UUIDTypeAdapter;
import org.jackhuang.hmcl.core.auth.yggdrasil.PropertyMap;
import org.jackhuang.hmcl.core.auth.yggdrasil.YggdrasilAuthentication;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.ui.SwingUtils;
/**
@@ -49,7 +49,7 @@ public final class YggdrasilAuthenticator extends AbstractAuthenticator {
public UserProfileProvider login(LoginInfo info) throws AuthenticationException {
UserProfileProvider result = new UserProfileProvider();
if (ua.canPlayOnline()) {
result.setUserName(info.username)
result.setUserName(ua.getSelectedProfile().name)
.setUserId(UUIDTypeAdapter.fromUUID(ua.getSelectedProfile().id));
} else {
String usr = info.username;
@@ -75,9 +75,10 @@ public final class YggdrasilAuthenticator extends AbstractAuthenticator {
if (sel == -1)
throw new AuthenticationException("No selection");
selectedProfile = profiles[sel];
ua.setSelectedProfile(profiles[sel]);
username = names[sel];
} else
username = JOptionPane.showInputDialog(C.i18n("login.no_charactor"));
username = MessageBox.showInputDialog(C.i18n("login.no_charactor"));
else
username = selectedProfile.name;
if (username == null)
@@ -81,7 +81,7 @@ public class GameProfile {
if (!(json instanceof JsonObject))
throw new JsonParseException("The json element is not a JsonObject.");
JsonObject object = (JsonObject) json;
UUID id = object.has("id") ? (UUID) context.deserialize(object.get("id"), UUID.class) : null;
UUID id = object.has("id") ? context.deserialize(object.get("id"), UUID.class) : null;
String name = object.has("name") ? object.getAsJsonPrimitive("name").getAsString() : null;
return new GameProfile(id, name);
}
@@ -71,8 +71,7 @@ public class PropertyMap extends HashMap<String, Property> {
for (Map.Entry<String, JsonElement> entry : object.entrySet())
if (entry.getValue() instanceof JsonArray)
for (JsonElement element : (JsonArray) entry.getValue())
result.put(entry.getKey(),
new Property((String) entry.getKey(), element.getAsString()));
result.put(entry.getKey(), new Property(entry.getKey(), element.getAsString()));
} else if ((json instanceof JsonArray))
for (JsonElement element : (JsonArray) json)
if ((element instanceof JsonObject)) {
@@ -36,7 +36,7 @@ public enum UserType {
}
public static UserType byName(String name) {
return (UserType) BY_NAME.get(name.toLowerCase());
return BY_NAME.get(name.toLowerCase());
}
public static UserType byLegacy(boolean isLegacy) {
@@ -27,6 +27,7 @@ import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.util.C;
@@ -87,6 +88,13 @@ public class YggdrasilAuthentication {
public GameProfile getSelectedProfile() {
return this.selectedProfile;
}
public void setSelectedProfile(GameProfile profile) {
if (profiles != null)
for (GameProfile p : profiles)
if (Objects.equals(profile, p))
selectedProfile = profile;
}
public String getUserId() {
return this.userid;
@@ -133,7 +141,7 @@ public class YggdrasilAuthentication {
if (StrUtils.isNotBlank(getAuthenticatedToken())) {
if (StrUtils.isBlank(getUserId()))
if (StrUtils.isBlank(username))
if (StrUtils.isNotBlank(username))
userid = username;
else
throw new AuthenticationException(C.i18n("login.invalid_uuid_and_username"));
@@ -57,7 +57,7 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
for (IMinecraftLibrary l : v.getLibraries())
if (l != null && l.allow()) {
File ff = service.version().getLibraryFile(mv, l);
if (!ff.exists() && l.getDownloadURL(service.getDownloadType().name()) != null) {
if (ff != null && !ff.exists() && l.getDownloadURL(service.getDownloadType().name()) != null) {
String libURL = l.getDownloadURL(service.getDownloadType().name());
if (libURL != null)
downloadLibraries.add(new DownloadLibraryJob(l, libURL, ff));
@@ -23,7 +23,6 @@ import java.util.Arrays;
import java.util.Collection;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.net.HTTPGetTask;
import org.jackhuang.hmcl.util.net.NetUtils;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskWorker;
@@ -79,7 +79,7 @@ public class MojangDownloadProvider extends IDownloadProvider {
else if (str.contains("scala-swing") || str.contains("scala-xml") || str.contains("scala-parser-combinators"))
return str.replace("http://files.minecraftforge.net/maven", "http://ftb.cursecdn.com/FTB2/maven/");
else if (str.contains("typesafe") || str.contains("scala"))
if (SupportedLocales.NOW_LOCALE.self == Locale.CHINA)
if (SupportedLocales.getNowLocale().self == Locale.CHINA)
return str.replace("http://files.minecraftforge.net/maven", "http://maven.aliyun.com/nexus/content/groups/public");
else
return str.replace("http://files.minecraftforge.net/maven", "http://repo1.maven.org/maven2");
@@ -59,7 +59,7 @@ public abstract class InstallerVersionList {
*
* @return cached result.
*/
public List<InstallerVersion> getVersionsImpl(String mcVersion) {
private List<InstallerVersion> getVersionsImpl(String mcVersion) {
if (versions == null || versionMap == null)
return null;
if (StrUtils.isBlank(mcVersion))
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.sys.IOUtils;
/**
@@ -78,7 +78,6 @@ public class MinecraftForgeVersionList extends InstallerVersionList {
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;
@@ -105,7 +104,7 @@ public class MinecraftForgeVersionList extends InstallerVersionList {
versions.add(iv);
}
versionMap.put(StrUtils.formatVersion(mcver), al);
versionMap.put(mcver, al);
}
Collections.sort(versions, new InstallerVersionComparator());
@@ -0,0 +1,35 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
/**
*
* @author huang
*/
public class LiteLoaderBranch {
@SerializedName("libraries")
public List<MinecraftLibrary> libraries;
@SerializedName("com.mumfrey:liteloader")
public Map<String, LiteLoaderVersion> liteLoader;
}
@@ -66,7 +66,7 @@ public class LiteLoaderInstaller extends Task implements PreviousResultRegistrar
mv.libraries = new ArrayList<>(Arrays.asList(version.libraries));
MinecraftLibrary ml = new MinecraftLibrary("com.mumfrey:liteloader:" + version.selfVersion);
ml.url = "http://dl.liteloader.com/versions";
ml.url = "http://dl.liteloader.com/versions/";
mv.libraries.add(0, ml);
// We use options from the old vanilla version.
@@ -30,7 +30,7 @@ public class LiteLoaderInstallerVersion extends InstallerVersionList.InstallerVe
public MinecraftLibrary[] libraries;
public String tweakClass;
public LiteLoaderInstallerVersion(String selfVersion, String mcVersion) {
super(selfVersion, mcVersion);
}
@@ -18,14 +18,19 @@
package org.jackhuang.hmcl.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
/**
*
* @author huangyuhui
*/
public class LiteLoaderMCVersions {
@SerializedName("repo")
public LiteLoaderRepo repo;
@SerializedName("artefacts")
public Map<String, Map<String, LiteLoaderVersion>> artefacts;
public LiteLoaderBranch artefacts;
@SerializedName("snapshots")
public LiteLoaderBranch snapshots;
}
@@ -0,0 +1,35 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
/**
*
* @author huang
*/
public class LiteLoaderRepo {
@SerializedName("stream")
public String stream;
@SerializedName("type")
public String type;
@SerializedName("url")
public String url;
@SerializedName("classifier")
public String classifier;
}
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.core.install.liteloader;
import com.google.gson.annotations.SerializedName;
import java.util.List;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
/**
@@ -36,6 +37,8 @@ public class LiteLoaderVersion {
public String md5;
@SerializedName("timestamp")
public String timestamp;
@SerializedName("lastSuccessfulBuild")
public int lastSuccessfulBuild;
@SerializedName("libraries")
public MinecraftLibrary[] libraries;
public List<MinecraftLibrary> libraries;
}
@@ -22,11 +22,14 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.core.download.DownloadType;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.core.install.InstallerVersionList;
import org.jackhuang.hmcl.core.install.InstallerVersionNewerComparator;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.task.Task;
import org.jackhuang.hmcl.util.task.TaskInfo;
@@ -60,6 +63,27 @@ public class LiteLoaderVersionList extends InstallerVersionList {
return Arrays.asList(task.setTag("Official Liteloader Download Site"));
}
private void doBranch(String version, LiteLoaderRepo repo, LiteLoaderBranch branch, List<InstallerVersion> result, boolean snapshot) {
if (branch == null || branch.liteLoader == null)
return;
for (Map.Entry<String, LiteLoaderVersion> entry : branch.liteLoader.entrySet()) {
if ("latest".equals(entry.getKey()))
continue;
LiteLoaderVersion v = entry.getValue();
LiteLoaderInstallerVersion iv = new LiteLoaderInstallerVersion(v.version.replace("SNAPSHOT", "SNAPSHOT-" + v.lastSuccessfulBuild), StrUtils.formatVersion(version));
if (snapshot)
iv.universal = String.format("http://jenkins.liteloader.com/view/%s/job/LiteLoader %s/lastSuccessfulBuild/artifact/build/libs/liteloader-%s-release.jar", version, version, v.version);
else
iv.universal = DownloadType.getSuggestedDownloadType().getProvider().getParsedDownloadURL(repo.url + "com/mumfrey/liteloader/" + version + "/" + v.file);
iv.tweakClass = v.tweakClass;
HashSet<MinecraftLibrary> set = new HashSet<>();
if (v.libraries != null)
set.addAll(v.libraries);
iv.libraries = set.toArray(new MinecraftLibrary[set.size()]);
result.add(iv);
}
}
@Override
public void executeTask(boolean areDependTasksSucceeded) throws Exception {
if (!areDependTasksSucceeded)
@@ -74,21 +98,10 @@ public class LiteLoaderVersionList extends InstallerVersionList {
for (Map.Entry<String, LiteLoaderMCVersions> arr : root.versions.entrySet()) {
ArrayList<InstallerVersion> al = new ArrayList<>();
LiteLoaderMCVersions mcv = arr.getValue();
if (mcv == null || mcv.artefacts == null || mcv.artefacts.get("com.mumfrey:liteloader") == null)
continue;
for (Map.Entry<String, LiteLoaderVersion> 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 = DownloadType.getSuggestedDownloadType().getProvider().getParsedDownloadURL("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);
}
doBranch(arr.getKey(), mcv.repo, mcv.artefacts, al, false);
doBranch(arr.getKey(), mcv.repo, mcv.snapshots, al, true);
Collections.sort(al, new InstallerVersionNewerComparator());
versions.addAll(al);
versionMap.put(StrUtils.formatVersion(arr.getKey()), al);
}
@@ -27,7 +27,7 @@ import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.JdkVersion;
import org.jackhuang.hmcl.util.MathUtils;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.sys.OS;
import org.jackhuang.hmcl.util.sys.Platform;
import org.jackhuang.hmcl.util.StrUtils;
@@ -86,12 +86,9 @@ public abstract class AbstractMinecraftLoader implements IMinecraftLoader {
appendJVMArgs(res);
if (jv == null || !jv.isEarlyAccess()) {
if (OS.os() == OS.WINDOWS)
res.add("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump");
if (jv != null && jv.getParsedVersion() >= JdkVersion.JAVA_17)
res.add("-XX:+UseG1GC");
else
res.add("-Xincgc");
res.add("-XX:-UseAdaptiveSizePolicy");
res.add("-XX:-OmitStackTraceInFastThrow");
@@ -131,7 +128,6 @@ public abstract class AbstractMinecraftLoader implements IMinecraftLoader {
res.add(a);
}
res.add("-Djava.library.path=" + service.version().getDecompressNativesToLocation(version).getAbsolutePath());
res.add("-Dfml.ignoreInvalidMinecraftCertificates=true");
res.add("-Dfml.ignorePatchDiscrepancies=true");
@@ -150,13 +146,6 @@ public abstract class AbstractMinecraftLoader implements IMinecraftLoader {
HMCLog.log("On making launcher args.");
if (StrUtils.isNotBlank(options.getHeight()) && StrUtils.isNotBlank(options.getWidth())) {
res.add("--height");
res.add(options.getHeight());
res.add("--width");
res.add(options.getWidth());
}
String serverIp = options.getServerIp();
if (StrUtils.isNotBlank(serverIp)) {
String[] args = serverIp.split(":");
@@ -177,7 +166,7 @@ public abstract class AbstractMinecraftLoader implements IMinecraftLoader {
if (StrUtils.isNotBlank(options.getProxyUser()) && StrUtils.isNotBlank(options.getProxyPass())) {
res.add("--proxyUser");
res.add(options.getProxyUser());
res.add("-=proxyPass");
res.add("--proxyPass");
res.add(options.getProxyPass());
}
}
@@ -19,13 +19,12 @@ package org.jackhuang.hmcl.core.launch;
import org.jackhuang.hmcl.api.game.LaunchOptions;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import org.jackhuang.hmcl.core.download.DownloadLibraryJob;
import org.jackhuang.hmcl.api.auth.LoginInfo;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.CompressingUtils;
import org.jackhuang.hmcl.util.task.ParallelTask;
@@ -43,13 +42,8 @@ public class DefaultGameLauncher extends GameLauncher {
public boolean downloadLibraries(List<DownloadLibraryJob> jobs) {
final TaskWindow.TaskWindowFactory dw = TaskWindow.factory();
ParallelTask parallelTask = new ParallelTask();
HashSet<String> names = new HashSet<>();
for (DownloadLibraryJob s : jobs) {
if (names.contains(s.lib.getName()))
continue;
names.add(s.lib.getName());
for (DownloadLibraryJob s : jobs)
parallelTask.addTask(new LibraryDownloadTask(s, service.getDownloadType()));
}
dw.append(parallelTask);
boolean flag = true;
if (jobs.size() > 0)
@@ -163,7 +163,11 @@ public abstract class GameLauncher {
}
}
HMCLog.log("Starting process");
HMCLog.log(str.toString());
String s = StrUtils.makeCommand(str);
s = s.replace(result.getAccessToken(), "<access token>");
s = s.replace(result.getSession(), "<session>");
s = s.replace(result.getUserId(), "<uuid>");
HMCLog.log(s);
ProcessBuilder builder = new ProcessBuilder(str);
if (options.getLaunchVersion() == null || service.baseDirectory() == null)
throw new Error("Fucking bug!");
@@ -190,7 +194,7 @@ public abstract class GameLauncher {
boolean isWin = OS.os() == OS.WINDOWS;
File f = new File(launcherName + (isWin ? ".bat" : ".sh"));
if (!f.exists() && !f.createNewFile())
HMCLog.warn("Failed to create " + f);
throw new IOException("Script file " + f.getAbsolutePath() + " does not exist but cannot be created.");
BufferedWriter writer;
try (FileOutputStream fos = FileUtils.openOutputStream(f)) {
writer = new BufferedWriter(new OutputStreamWriter(fos, Charsets.toCharset()));
@@ -21,8 +21,11 @@ import org.jackhuang.hmcl.api.game.LaunchOptions;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.sys.OS;
@@ -30,6 +33,8 @@ import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.api.auth.UserProfileProvider;
import org.jackhuang.hmcl.core.version.MinecraftLibrary;
import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.core.version.Argument;
import org.jackhuang.hmcl.core.version.Arguments;
/**
*
@@ -39,14 +44,32 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
public MinecraftLoader(LaunchOptions p, IMinecraftService provider, UserProfileProvider lr) throws GameException {
super(p, provider, p.getLaunchVersion(), lr);
if (version.arguments == null)
version.arguments = new Arguments();
if (version.arguments.game == null)
version.arguments.game = getDefaultGameArguments();
if (version.arguments.jvm == null)
version.arguments.jvm = getDefaultJVMArguments();
}
protected List<Argument> getDefaultJVMArguments() {
return Arguments.DEFAULT_JVM_ARGUMENTS;
}
protected List<Argument> getDefaultGameArguments() {
return Arguments.DEFAULT_GAME_ARGUMENTS;
}
@Override
protected void makeSelf(List<String> res) throws GameException {
String game_assets = assetProvider.provide(version, !options.isNotCheckGame());
Map<String, String> configuration = getConfigurations();
StringBuilder library = new StringBuilder("");
ArrayList<MinecraftLibrary> opt = new ArrayList<>();
for (MinecraftLibrary l : version.libraries)
if (l.allow() && !l.isRequiredToUnzip()) {
if (l.allow() && !l.isNative()) {
if (l.getName().toLowerCase(Locale.US).contains("optifine")) {
opt.add(l);
continue;
@@ -64,39 +87,46 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
}
File f = version.getJar(service.baseDirectory());
if (!f.exists())
throw new GameException("Minecraft jar does not exists");
throw new GameException("Minecraft jar does not exist");
library.append(f.getAbsolutePath()).append(File.pathSeparator);
res.add("-cp");
res.add(library.toString().substring(0, library.length() - File.pathSeparator.length()));
String classpath = library.toString().substring(0, library.length() - File.pathSeparator.length());
configuration.put("${classpath}", classpath);
configuration.put("${natives_directory}", service.version().getDecompressNativesToLocation(version).getAbsolutePath());
configuration.put("${game_assets}", game_assets);
res.addAll(Arguments.parseArguments(version.arguments.jvm, configuration));
res.add(version.mainClass);
if (version.minecraftArguments == null)
throw new GameException(new NullPointerException("Minecraft Arguments can not be null."));
String[] splitted = StrUtils.tokenize(version.minecraftArguments);
Map<String, Boolean> features = getFeatures();
String game_assets = assetProvider.provide(version, !options.isNotCheckGame());
res.addAll(Arguments.parseArguments(version.arguments.game, configuration, features));
res.addAll(Arguments.parseStringArguments(Arrays.asList(StrUtils.tokenize(version.minecraftArguments)), configuration));
}
protected Map<String, Boolean> getFeatures() {
Map<String, Boolean> features = new HashMap<>();
features.put("has_custom_resolution", StrUtils.isNotBlank(options.getHeight()) && StrUtils.isNotBlank(options.getWidth()));
return features;
}
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}", options.getVersionName());
t = t.replace("${profile_name}", options.getName());
t = t.replace("${version_type}", options.getType());
t = t.replace("${game_directory}", service.version().getRunDirectory(version.id).getAbsolutePath());
t = t.replace("${game_assets}", game_assets);
t = t.replace("${assets_root}", service.asset().getAssets(version.getAssetsIndex().getId()).getAbsolutePath());
t = t.replace("${auth_access_token}", lr.getAccessToken());
t = t.replace("${user_type}", lr.getUserType());
t = t.replace("${assets_index_name}", version.getAssetsIndex().getId());
t = t.replace("${user_properties}", lr.getUserProperties());
res.add(t);
}
if (res.indexOf("--gameDir") != -1 && res.indexOf("--workDir") != -1) {
res.add("--workDir");
res.add(gameDir.getAbsolutePath());
}
protected Map<String, String> getConfigurations() {
HashMap<String, String> map = new HashMap();
map.put("${auth_player_name}", lr.getUserName());
map.put("${auth_session}", lr.getSession());
map.put("${auth_uuid}", lr.getUserId());
map.put("${version_name}", options.getVersionName());
map.put("${profile_name}", options.getName());
map.put("${version_type}", options.getType());
map.put("${game_directory}", service.version().getRunDirectory(version.id).getAbsolutePath());
map.put("${assets_root}", service.asset().getAssets(version.getAssetsIndex().getId()).getAbsolutePath());
map.put("${auth_access_token}", lr.getAccessToken());
map.put("${user_type}", lr.getUserType());
map.put("${assets_index_name}", version.getAssetsIndex().getId());
map.put("${user_properties}", lr.getUserProperties());
map.put("${resolution_width}", options.getWidth());
map.put("${resolution_height}", options.getHeight());
map.put("${natives_directory}", service.version().getDecompressNativesToLocation(version).getAbsolutePath());
return map;
}
@Override
@@ -111,6 +141,17 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
} catch (IOException e) {
HMCLog.err("Failed to append jvm arguments when searching for asset objects.", e);
}
list.add("-Dminecraft.client.jar=" + version.getJar(service.baseDirectory()).getAbsolutePath());
/*
if (version.logging != null && version.logging.containsKey("client")) {
LoggingInfo logging = version.logging.get("client");
File file = service.asset().getLoggingObject(version.getAssetsIndex().getId(), logging);
if (file.exists())
list.add(logging.argument.replace("${path}", file.getAbsolutePath()));
}
*/
}
private final IAssetProvider DEFAULT_ASSET_PROVIDER = (t, allow) -> {
@@ -82,7 +82,7 @@ public class MinecraftModService extends IMinecraftModService {
HMCLog.warn("Failed to make directories: " + modsFolder);
File newf = new File(modsFolder, f.getName());
FileUtils.copyFile(f, newf);
ModInfo i = ModInfo.readModInfo(f);
ModInfo i = ModInfo.readModInfo(newf);
modCache.get(id).add(i);
return true;
} catch (IOException ex) {
@@ -22,6 +22,7 @@ import java.io.IOException;
import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.asset.AssetsObject;
import org.jackhuang.hmcl.core.version.AssetIndexDownloadInfo;
import org.jackhuang.hmcl.core.version.LoggingInfo;
import org.jackhuang.hmcl.util.task.Task;
/**
@@ -56,4 +57,6 @@ public abstract class IMinecraftAssetService extends IMinecraftBasicService {
public abstract File getAssetObject(String assetVersion, String name) throws IOException;
public abstract File getAssetObject(String assetId, AssetsObject assetsObject);
public abstract File getLoggingObject(String assetId, LoggingInfo id);
}
@@ -33,18 +33,18 @@ import org.jackhuang.hmcl.api.game.IMinecraftLibrary;
* org.jackhuang.hmcl.core.version.MinecraftVersionManager
* @author huangyuhui
*/
public abstract class IMinecraftProvider {
public abstract class IMinecraftProvider<T extends IMinecraftService> {
protected IMinecraftService service;
protected T service;
public IMinecraftProvider(IMinecraftService service) {
public IMinecraftProvider(T service) {
this.service = service;
}
/**
* To download mod packs.
*/
public abstract void initializeMiencraft();
public abstract void initializeMinecraft();
/**
* Get the run directory of given version.
@@ -26,7 +26,7 @@ import org.jackhuang.hmcl.api.game.IMinecraftLibrary;
*/
public abstract class AbstractMinecraftLibrary implements IMinecraftLibrary {
private String name;
private final String name;
public AbstractMinecraftLibrary(String name) {
this.name = name;
@@ -41,7 +41,11 @@ public abstract class AbstractMinecraftLibrary implements IMinecraftLibrary {
@Override
public String getDownloadURL(String downloadSource) {
return getDownloadInfo().getUrl(DownloadType.valueOf(downloadSource));
LibraryDownloadInfo info = getDownloadInfo();
if (info == null)
return null;
else
return info.getUrl(DownloadType.valueOf(downloadSource));
}
@Override
@@ -0,0 +1,69 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.core.version;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/**
*
* @author huang
*/
public interface Argument extends Cloneable {
/**
* Parse this argument in form: ${key name} or simply a string.
*
* @param keys the parse map
* @param features the map that contains some features such as 'is_demo_user', 'has_custom_resolution'
* @return parsed argument element, empty if this argument is ignored and will not be added.
*/
List<String> toString(Map<String, String> keys, Map<String, Boolean> features);
public Object clone();
public static class ArgumentSerializer implements JsonDeserializer<Argument>, JsonSerializer<Argument> {
@Override
public Argument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive())
return new StringArgument(json.getAsString());
else
return context.deserialize(json, RuledArgument.class);
}
@Override
public JsonElement serialize(Argument src, Type typeOfSrc, JsonSerializationContext context) {
if (src instanceof StringArgument)
return new JsonPrimitive(((StringArgument) src).argument);
else if (src instanceof RuledArgument)
return context.serialize(src, RuledArgument.class);
else
throw new AssertionError("Unrecognized argument type: " + src);
}
}
}
@@ -0,0 +1,89 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.core.version;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.util.CollectionUtils;
/**
*
* @author huang
*/
public class Arguments {
@SerializedName("game")
public List<Argument> game;
@SerializedName("jvm")
public List<Argument> jvm;
public static Arguments clone(Arguments original) {
if (original == null)
return null;
Arguments ret = new Arguments();
ret.game = CollectionUtils.deepCopy(original.game, value -> (Argument) value.clone());
ret.jvm = CollectionUtils.deepCopy(original.jvm, value -> (Argument) value.clone());
return ret;
}
public static Arguments merge(Arguments a, Arguments b) {
if (a == null) return b;
else if (b == null) return a;
Arguments ret = new Arguments();
ret.game = ArrayUtils.merge(a.game, b.game);
ret.jvm = ArrayUtils.merge(a.jvm, b.jvm);
if (ret.game == null && ret.jvm == null) return null;
return ret;
}
public static List<String> parseStringArguments(List<String> arguments, Map<String, String> keys) {
return CollectionUtils.map(arguments, str -> keys.getOrDefault(str, str));
}
public static List<String> parseArguments(List<Argument> arguments, Map<String, String> keys) {
return parseArguments(arguments, keys, Collections.EMPTY_MAP);
}
public static List<String> parseArguments(List<Argument> arguments, Map<String, String> keys, Map<String, Boolean> features) {
return CollectionUtils.flatMap(arguments, arg -> arg.toString(keys, features));
}
public static final List<Argument> DEFAULT_JVM_ARGUMENTS;
public static final List<Argument> DEFAULT_GAME_ARGUMENTS;
static {
List<Argument> jvm = new LinkedList<>();
jvm.add(new RuledArgument(Collections.singletonList(new Rules("allow", new OSRestriction("windows"))), Collections.singletonList("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump")));
jvm.add(new RuledArgument(Collections.singletonList(new Rules("allow", new OSRestriction("windows", "^10\\."))), Arrays.asList("-Dos.name=Windows 10", "-Dos.version=10.0")));
jvm.add(new StringArgument("-Djava.library.path=${natives_directory}"));
jvm.add(new StringArgument("-Dminecraft.launcher.brand=${launcher_name}"));
jvm.add(new StringArgument("-Dminecraft.launcher.version=${launcher_version}"));
jvm.add(new StringArgument("-cp"));
jvm.add(new StringArgument("${classpath}"));
DEFAULT_JVM_ARGUMENTS = Collections.unmodifiableList(jvm);
List<Argument> game = new LinkedList<>();
game.add(new RuledArgument(Collections.singletonList(new Rules("allow", Collections.singletonMap("has_custom_resolution", true))), Arrays.asList("--width", "${resolution_width}", "--height", "${resolution_height}")));
DEFAULT_GAME_ARGUMENTS = Collections.unmodifiableList(game);
}
}
@@ -36,7 +36,9 @@ public class GameDownloadInfo implements Cloneable {
/**
* Ready for AssetIndexDownloadInfo, and GameDownloadInfo also need this.
* And LoggingInfo will have this field in json.
*/
@SerializedName("id")
protected String id;
/**
@@ -0,0 +1,41 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.core.version;
/**
*
* @author huang
*/
public class LoggingInfo implements Cloneable {
public GameDownloadInfo file;
public String argument;
public String type;
@Override
public Object clone() {
try {
LoggingInfo info = (LoggingInfo) super.clone();
info.file = (GameDownloadInfo) file.clone();
return info;
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
}
@@ -48,7 +48,7 @@ public class MinecraftClassicVersion extends MinecraftVersion {
}
@Override
public MinecraftVersion resolve(IMinecraftProvider manager, Set<String> resolvedSoFar) {
public MinecraftVersion resolve(IMinecraftProvider<?> manager, Set<String> resolvedSoFar) {
return this;
}
@@ -54,16 +54,7 @@ public class MinecraftLibrary extends AbstractMinecraftLibrary {
*/
@Override
public boolean allow() {
if (rules != null) {
boolean flag = false;
for (Rules r : rules)
if ("disallow".equals(r.action()))
return false;
else if ("allow".equals(r.action()))
flag = true;
return flag;
} else
return true;
return Rules.allow(rules);
}
private String formatArch(String nati) {
@@ -84,7 +75,7 @@ public class MinecraftLibrary extends AbstractMinecraftLibrary {
}
@Override
public boolean isRequiredToUnzip() {
public boolean isNative() {
return natives != null && allow();
}
@@ -123,13 +114,11 @@ public class MinecraftLibrary extends AbstractMinecraftLibrary {
if (natives != null) {
if (downloads.classifiers == null)
downloads.classifiers = new HashMap<>();
if (StrUtils.isBlank(getNative()))
return null;
if (!downloads.classifiers.containsKey(getNative()))
downloads.classifiers.put(getNative(), info = new LibraryDownloadInfo());
else {
info = downloads.classifiers.get(getNative());
if (info == null)
info = new LibraryDownloadInfo();
}
else info = downloads.classifiers.get(getNative());
} else {
if (downloads.artifact == null)
downloads.artifact = new LibraryDownloadInfo();
@@ -30,7 +30,7 @@ public class MinecraftOldLibrary extends MinecraftLibrary {
}
@Override
public boolean isRequiredToUnzip() {
public boolean isNative() {
return false;
}
@@ -21,8 +21,10 @@ import org.jackhuang.hmcl.api.game.IMinecraftLibrary;
import com.google.gson.annotations.SerializedName;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -32,6 +34,7 @@ import org.jackhuang.hmcl.core.GameException;
import org.jackhuang.hmcl.core.service.IMinecraftProvider;
import org.jackhuang.hmcl.core.asset.AssetsIndex;
import org.jackhuang.hmcl.util.ArrayUtils;
import org.jackhuang.hmcl.util.CollectionUtils;
/**
*
@@ -41,6 +44,8 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
@SerializedName("minecraftArguments")
public String minecraftArguments;
@SerializedName("arguments")
public Arguments arguments;
@SerializedName("mainClass")
public String mainClass;
@SerializedName("time")
@@ -71,12 +76,15 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
private Map<String, GameDownloadInfo> downloads;
@SerializedName("libraries")
public ArrayList<MinecraftLibrary> libraries;
@SerializedName("logging")
public Map<String, LoggingInfo> logging;
public MinecraftVersion() {
}
public MinecraftVersion(String minecraftArguments, String mainClass, String time, String id, String type, String processArguments, String releaseTime, String assets, String jar, String inheritsFrom, String runDir, int minimumLauncherVersion, List<MinecraftLibrary> libraries, boolean hidden, Map<String, GameDownloadInfo> downloads, AssetIndexDownloadInfo assetIndexDownloadInfo) {
public MinecraftVersion(Arguments arguments, String minecraftArguments, String mainClass, String time, String id, String type, String processArguments, String releaseTime, String assets, String jar, String inheritsFrom, String runDir, int minimumLauncherVersion, List<MinecraftLibrary> libraries, boolean hidden, Map<String, GameDownloadInfo> downloads, AssetIndexDownloadInfo assetIndexDownloadInfo, Map<String, LoggingInfo> logging) {
this();
this.arguments = Arguments.clone(arguments);
this.minecraftArguments = minecraftArguments;
this.mainClass = mainClass;
this.time = time;
@@ -94,21 +102,9 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
this.assetIndex = null;
else
this.assetIndex = (AssetIndexDownloadInfo) assetIndexDownloadInfo.clone();
if (libraries == null)
this.libraries = new ArrayList<>();
else {
this.libraries = new ArrayList<>(libraries.size());
for (MinecraftLibrary library : libraries)
if (library != null)
this.libraries.add((MinecraftLibrary) library.clone());
}
if (downloads == null)
this.downloads = null;
else {
this.downloads = new HashMap<>(downloads.size());
for (Map.Entry<String, GameDownloadInfo> entry : downloads.entrySet())
this.downloads.put(entry.getKey(), (GameDownloadInfo) entry.getValue().clone());
}
this.libraries = CollectionUtils.deepCopy(libraries, value -> (MinecraftLibrary) value.clone());
this.downloads = CollectionUtils.deepCopy(downloads, value -> (GameDownloadInfo) value.clone());
this.logging = CollectionUtils.deepCopy(logging, value -> (LoggingInfo) value.clone());
}
@Override
@@ -122,11 +118,11 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
}
}
public MinecraftVersion resolve(IMinecraftProvider provider) throws GameException {
public MinecraftVersion resolve(IMinecraftProvider<?> provider) throws GameException {
return resolve(provider, new HashSet<>());
}
protected MinecraftVersion resolve(IMinecraftProvider provider, Set<String> resolvedSoFar) throws GameException {
protected MinecraftVersion resolve(IMinecraftProvider<?> provider, Set<String> resolvedSoFar) throws GameException {
if (inheritsFrom == null)
return this;
if (!resolvedSoFar.add(id))
@@ -140,15 +136,17 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
}
parent = parent.resolve(provider, resolvedSoFar);
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, this.runDir, parent.minimumLauncherVersion,
this.libraries != null ? ArrayUtils.merge(this.libraries, parent.libraries) : parent.libraries, this.hidden,
this.downloads != null ? this.downloads : parent.downloads,
this.assetIndex != null ? this.assetIndex : parent.assetIndex);
Arguments.merge(parent.arguments, this.arguments),
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, this.runDir, parent.minimumLauncherVersion,
this.libraries != null ? ArrayUtils.merge(this.libraries, parent.libraries) : parent.libraries, this.hidden,
this.downloads != null ? this.downloads : parent.downloads,
this.assetIndex != null ? this.assetIndex : parent.assetIndex,
this.logging != null ? this.logging : parent.logging);
return result;
}
@@ -164,8 +162,7 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
}
public File getNatives(File gameDir) {
return new File(gameDir, "versions/" + id + "/" + id
+ "-natives");
return new File(gameDir, "versions/" + id + "/" + id + "-natives");
}
public boolean isAllowedToUnpackNatives() {
@@ -192,8 +189,6 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
return false;
return Objects.equals(this.id, ((MinecraftVersion) obj).id);
}
public AssetIndexDownloadInfo getAssetsIndex() {
if (assetIndex == null)
@@ -212,7 +207,7 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
return i;
}
public Set<IMinecraftLibrary> getLibraries() {
return libraries == null ? new HashSet<>() : new HashSet<>(libraries);
public List<IMinecraftLibrary> getLibraries() {
return libraries == null ? new LinkedList<>() : Collections.unmodifiableList(libraries);
}
}
@@ -39,7 +39,7 @@ import org.jackhuang.hmcl.core.service.IMinecraftService;
import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.core.MCUtils;
import org.jackhuang.hmcl.util.task.TaskWindow;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.api.func.Consumer;
import org.jackhuang.hmcl.api.func.Predicate;
@@ -50,7 +50,7 @@ import org.jackhuang.hmcl.util.ui.SwingUtils;
*
* @author huangyuhui
*/
public class MinecraftVersionManager extends IMinecraftProvider {
public class MinecraftVersionManager<T extends IMinecraftService> extends IMinecraftProvider<T> {
final Map<String, MinecraftVersion> versions = new TreeMap<>();
@@ -58,7 +58,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
*
* @param p
*/
public MinecraftVersionManager(IMinecraftService p) {
public MinecraftVersionManager(T p) {
super(p);
}
@@ -108,7 +108,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
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)
if (!jsons[0].renameTo(new File(jsons[0].getParent(), id + ".json")))
if (!jsons[0].renameTo(jsonFile))
HMCLog.warn("Failed to rename version json " + jsons[0]);
}
if (!jsonFile.exists()) {
@@ -247,11 +247,13 @@ public class MinecraftVersionManager extends IMinecraftProvider {
throw new GameException("Wrong format: minecraft.json");
ArrayList<File> unzippings = new ArrayList<>();
ArrayList<Extract> extractRules = new ArrayList<>();
for (IMinecraftLibrary l : v.libraries)
if (l.isRequiredToUnzip() && v.isAllowedToUnpackNatives()) {
unzippings.add(getLibraryFile(v, l));
for (IMinecraftLibrary l : v.libraries) {
final File f = getLibraryFile(v, l);
if (l.isNative() && v.isAllowedToUnpackNatives() && l.allow() && f != null) {
unzippings.add(f);
extractRules.add(l.getDecompressExtractRules());
}
}
return new DecompressLibraryJob(unzippings.toArray(new File[unzippings.size()]), extractRules.toArray(new Extract[extractRules.size()]), getDecompressNativesToLocation(v));
}
@@ -283,6 +285,7 @@ public class MinecraftVersionManager extends IMinecraftProvider {
@Override
public boolean onLaunch(String id) {
// Fix bug in Minecraft 1.8
File resourcePacks = new File(getRunDirectory(id), "resourcepacks");
if (!FileUtils.makeDirectory(resourcePacks))
HMCLog.warn("Failed to make resourcePacks: " + resourcePacks);
@@ -305,6 +308,6 @@ public class MinecraftVersionManager extends IMinecraftProvider {
}
@Override
public void initializeMiencraft() {
public void initializeMinecraft() {
}
}
@@ -18,19 +18,40 @@
package org.jackhuang.hmcl.core.version;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.StrUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jackhuang.hmcl.util.sys.OS;
/**
*
* @author huangyuhui
*/
public class OSRestriction {
public class OSRestriction implements Cloneable {
@SerializedName("version")
private String version;
@SerializedName("name")
public String name;
@SerializedName("arch")
public String arch;
public OSRestriction() {
this(null);
}
public OSRestriction(String name) {
this(name, null);
}
public OSRestriction(String name, String version) {
this(name, version, null);
}
public OSRestriction(String name, String version, String arch) {
this.name = name;
this.version = version;
this.arch = arch;
}
public String getVersion() {
return version;
@@ -49,6 +70,33 @@ public class OSRestriction {
}
public boolean isCurrentOS() {
return StrUtils.isBlank(getName()) || OS.os().name().equalsIgnoreCase(getName());
if (name != null && !OS.os().name().equalsIgnoreCase(name))
return false;
if (version != null) {
try {
Pattern pattern = Pattern.compile(version);
Matcher matcher = pattern.matcher(System.getProperty("os.version"));
if (!matcher.matches())
return false;
} catch (Throwable t) {}
}
if (arch != null) {
try {
Pattern pattern = Pattern.compile(arch);
Matcher matcher = pattern.matcher(System.getProperty("os.arch"));
if (!matcher.matches())
return false;
} catch (Throwable t) {}
}
return true;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new AssertionError(ex);
}
}
}
@@ -0,0 +1,93 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.core.version;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.util.CollectionUtils;
/**
*
* @author huang
*/
public class RuledArgument implements Argument {
public List<Rules> rules;
public List<String> value;
public RuledArgument() {
}
public RuledArgument(List<Rules> rules, List<String> args) {
this.rules = rules;
this.value = args;
}
@Override
public Object clone() {
try {
RuledArgument ret = (RuledArgument) super.clone();
ret.rules = CollectionUtils.deepCopy(rules, value -> (Rules) value.clone());
ret.value = CollectionUtils.copy(value);
return ret;
} catch (CloneNotSupportedException ex) {
throw new AssertionError(ex);
}
}
@Override
public List<String> toString(Map<String, String> keys, Map<String, Boolean> features) {
if (!Rules.allow(rules, features))
return Collections.EMPTY_LIST;
else
return CollectionUtils.map(value, a -> new StringArgument(a).toString(keys, features).get(0));
}
public static class RuledArgumentSerializer implements JsonSerializer<RuledArgument>, JsonDeserializer<RuledArgument> {
@Override
public JsonElement serialize(RuledArgument src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.add("rules", context.serialize(src.rules));
obj.add("value", context.serialize(src.value));
return obj;
}
@Override
public RuledArgument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject obj = json.getAsJsonObject();
RuledArgument a = new RuledArgument();
a.rules = context.deserialize(obj.get("rules"), new TypeToken<List<Rules>>(){}.getType());
if (obj.get("value").isJsonPrimitive())
a.value = Collections.singletonList(obj.get("value").getAsString());
else
a.value = context.<List<String>>deserialize(obj.get("value"), new TypeToken<List<String>>(){}.getType());
return a;
}
}
}
@@ -18,23 +18,77 @@
package org.jackhuang.hmcl.core.version;
import com.google.gson.annotations.SerializedName;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.jackhuang.hmcl.util.CollectionUtils;
/**
*
* @author huangyuhui
*/
public class Rules {
public class Rules implements Cloneable {
@SerializedName("action")
private String action;
@SerializedName("os")
private OSRestriction os;
@SerializedName("features")
private Map<String, Boolean> features;
public Rules() {
}
public Rules(String action, OSRestriction os) {
this.action = action;
this.os = os;
}
public Rules(String action, Map<String, Boolean> features) {
this.action = action;
this.features = features;
}
public String action() {
return os == null || os.isCurrentOS() ? action : null;
@Override
public Object clone() {
try {
Rules rules = (Rules) super.clone();
rules.os = (OSRestriction) os.clone();
rules.features = CollectionUtils.copy(features);
return rules;
} catch (CloneNotSupportedException ex) {
throw new AssertionError(ex);
}
}
public String action(Map<String, Boolean> features) {
if (os != null && !os.isCurrentOS()) return null;
if (this.features != null) {
for (Map.Entry<String, Boolean> entry : this.features.entrySet())
if (!features.containsKey(entry.getKey()))
return null;
else
if (!features.get(entry.getKey()).equals(entry.getValue()))
return null;
}
return action;
}
public static boolean allow(Collection<Rules> c) {
return allow(c, Collections.EMPTY_MAP);
}
public static boolean allow(Collection<Rules> c, Map<String, Boolean> features) {
if (c != null) {
boolean flag = false;
for (Rules r : c)
if ("disallow".equals(r.action(features)))
return false;
else if ("allow".equals(r.action(features)))
flag = true;
return flag;
} else
return true;
}
}
@@ -0,0 +1,59 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.core.version;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author huang
*/
public class StringArgument implements Argument {
public String argument;
public StringArgument(String argument) {
this.argument = argument;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new AssertionError(ex);
}
}
@Override
public List<String> toString(Map<String, String> keys, Map<String, Boolean> features) {
String res = argument;
Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");
Matcher m = pattern.matcher(argument);
while (m.find()) {
String entry = m.group();
res = res.replace(entry, keys.getOrDefault(entry, entry));
}
return Collections.singletonList(res);
}
}
@@ -73,6 +73,9 @@ public final class ArrayUtils {
}
public static <T> ArrayList<T> merge(List<T> a, List<T> b) {
if (a == null && b == null) return null;
else if (a == null) return new ArrayList<>(b);
else if (b == null) return new ArrayList<>(a);
ArrayList<T> al = new ArrayList<>(a.size() + b.size());
al.addAll(a);
al.addAll(b);
@@ -86,7 +89,7 @@ public final class ArrayUtils {
return l;
}
public static <T> int matchArray(byte[] a, byte[] b) {
public static int matchArray(byte[] a, byte[] b) {
for (int i = 0; i < a.length - b.length; i++) {
int j = 1;
for (int k = 0; k < b.length; k++) {
@@ -20,6 +20,8 @@ package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.util.lang.SupportedLocales;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.jackhuang.hmcl.core.version.Argument;
import org.jackhuang.hmcl.core.version.RuledArgument;
/**
*
@@ -27,22 +29,25 @@ import com.google.gson.GsonBuilder;
*/
public final class C {
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(RuledArgument.class, new RuledArgument.RuledArgumentSerializer())
.registerTypeAdapter(Argument.class, new Argument.ArgumentSerializer())
.setPrettyPrinting().create();
//http://repo1.maven.org/maven2
public static final String URL_PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
public static final String URL_TIEBA = "http://tieba.baidu.com/f?kw=hellominecraftlauncher";
public static final String URL_GITHUB = "https://github.com/huanghongxun/HMCL/issues";
public static final String URL_MINECRAFTFORUM = "http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-tools/1265720-hello-minecraft-launcher-1-9-3-mc-1-7-4-auto";
public static final String URL_CONTACT = "http://huangyuhui.duapp.com/hmcl.php";
public static final String URL_FORGE_LIST = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/json";
public static final String URL_LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
private C() {
}
public static String i18n(String a, Object... format) {
return SupportedLocales.NOW_LOCALE.translate(a, format);
return SupportedLocales.getNowLocale().translate(a, format);
}
}
@@ -20,7 +20,11 @@ package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.api.func.Predicate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jackhuang.hmcl.api.func.Function;
/**
*
@@ -31,7 +35,7 @@ public final class CollectionUtils {
private CollectionUtils() {
}
public static <T> ArrayList<T> filter(Collection<T> coll, Predicate<T> p) {
public static <T> List<T> filter(Collection<T> coll, Predicate<T> p) {
ArrayList<T> newColl = new ArrayList<>();
for (T t : coll)
if (p.apply(t))
@@ -39,6 +43,20 @@ public final class CollectionUtils {
return newColl;
}
public static <U, V> List<V> map(Collection<U> coll, Function<U, V> p) {
ArrayList<V> newColl = new ArrayList<>(coll.size());
for (U t : coll)
newColl.add(p.apply(t));
return newColl;
}
public static <U, V> List<V> flatMap(Collection<U> coll, Function<U, Collection<V>> p) {
ArrayList<V> newColl = new ArrayList<>(coll.size());
for (U t : coll)
newColl.addAll(p.apply(t));
return newColl;
}
public static <T> boolean removeIf(Collection<T> coll, Predicate<T> p) {
boolean removed = false;
final Iterator<T> each = coll.iterator();
@@ -49,4 +67,40 @@ public final class CollectionUtils {
}
return removed;
}
public static <T extends Cloneable> ArrayList<T> deepCopy(List<T> original, Function<T, T> clone) {
if (original == null)
return null;
ArrayList<T> ret = new ArrayList<>(original.size());
for (T x : original)
ret.add(clone.apply(x));
return ret;
}
public static <T> ArrayList<T> copy(List<T> original) {
if (original == null)
return null;
ArrayList<T> ret = new ArrayList<>(original.size());
for (T x : original)
ret.add(x);
return ret;
}
public static <K, V extends Cloneable> Map<K, V> deepCopy(Map<K, V> original, Function<V, V> clone) {
if (original == null)
return null;
HashMap<K, V> ret = new HashMap<>(original.size());
for (HashMap.Entry<K, V> x : original.entrySet())
ret.put(x.getKey(), clone.apply(x.getValue()));
return ret;
}
public static <K, V> Map<K, V> copy(Map<K, V> original) {
if (original == null)
return null;
HashMap<K, V> ret = new HashMap<>(original.size());
for (HashMap.Entry<K, V> x : original.entrySet())
ret.put(x.getKey(), x.getValue());
return ret;
}
}
@@ -25,7 +25,6 @@ import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import org.jackhuang.hmcl.api.func.Function;
import org.jackhuang.hmcl.api.func.Predicate;
/**
*
@@ -87,10 +86,10 @@ public final class StrUtils {
return false;
}
public static boolean containsOne(List<String> base, List<String> match, Predicate<String> pred) {
public static boolean containsOne(Collection<String> base, String... match) {
for (String a : base)
for (String b : match)
if (pred.apply(a) && a.toLowerCase().contains(b.toLowerCase()))
if (a.toLowerCase().contains(b.toLowerCase()))
return true;
return false;
}
@@ -154,8 +153,11 @@ public final class StrUtils {
return sb.toString();
}
public static String[] tokenize(String paramString1) {
return tokenize(paramString1, " \t\n\r\f");
public static String[] tokenize(String str) {
if (str == null)
return new String[0];
else
return tokenize(str, " \t\n\r\f");
}
public static String[] tokenize(String str, String delim) {
@@ -38,7 +38,7 @@ public final class Utils {
return ((URLClassLoader) Utils.class.getClassLoader()).getURLs();
}
public static void setClipborad(String text) {
public static void setClipboard(String text) {
try {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null);
} catch(HeadlessException ignored) {
@@ -24,7 +24,12 @@ import java.util.Locale;
* @author huangyuhui
*/
public enum SupportedLocales {
def(Locale.getDefault(), "lang.default"), en(Locale.ENGLISH, null), zh(Locale.TRADITIONAL_CHINESE, null), zh_CN(Locale.SIMPLIFIED_CHINESE, null), vi(new Locale("vi"), null);
def(Locale.getDefault(), "lang.default"),
en(Locale.ENGLISH, null),
zh(Locale.TRADITIONAL_CHINESE, null),
zh_CN(Locale.SIMPLIFIED_CHINESE, null),
vi(new Locale("vi"), null),
ru(new Locale("ru"), null);
public Locale self;
private String showString, customized;
@@ -48,7 +53,15 @@ public enum SupportedLocales {
return NOW_LOCALE.translate(customized);
}
public static SupportedLocales NOW_LOCALE = def;
private static SupportedLocales NOW_LOCALE = def;
public static SupportedLocales getNowLocale() {
return NOW_LOCALE;
}
public static void setNowLocale(SupportedLocales newLocale) {
NOW_LOCALE = newLocale;
}
public String translate(String key, Object... format) {
try {
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.util.log;
import java.awt.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jackhuang.hmcl.util.ui.GraphicsUtils;
/**
*
@@ -27,12 +28,12 @@ import java.util.regex.Pattern;
*/
public enum Level {
FATAL(1, Color.red),
ERROR(2, Color.red),
WARN(3, Color.orange),
FATAL(1, GraphicsUtils.getWebColor("#C0392B")),
ERROR(2, GraphicsUtils.getWebColor("#D35400")),
WARN(3, GraphicsUtils.getWebColor("#F39C12")),
INFO(4, Color.black),
DEBUG(5, Color.blue),
TRACE(6, Color.blue),
DEBUG(5, GraphicsUtils.getWebColor("#2980B9")),
TRACE(6, GraphicsUtils.getWebColor("#16A085")),
ALL(2147483647, Color.black);
public final int level;
@@ -48,40 +49,54 @@ public enum Level {
}
public static final Pattern MINECRAFT_LOGGER = Pattern.compile("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]");
public static final Pattern MINECRAFT_LOGGER_CATEGORY = Pattern.compile("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\] \\[(?<category>[^\\]]+)\\]");
public static final String JAVA_SYMBOL = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$][a-zA-Z\\d_$]*";
public static Level guessLevel(String line, Level preLevel) {
Level level = preLevel;
public static Level guessLevel(String line) {
Level level = null;
Matcher m = MINECRAFT_LOGGER.matcher(line);
if (m.find()) {
// New style logs from log4j
String levelStr = m.group("level");
if (null != levelStr)
switch (levelStr) {
case "INFO":
level = INFO;
break;
case "WARN":
level = WARN;
break;
case "ERROR":
level = ERROR;
break;
case "FATAL":
level = FATAL;
break;
case "TRACE":
level = TRACE;
break;
case "DEBUG":
level = DEBUG;
break;
default:
break;
case "INFO":
level = INFO;
break;
case "WARN":
level = WARN;
break;
case "ERROR":
level = ERROR;
break;
case "FATAL":
level = FATAL;
break;
case "TRACE":
level = TRACE;
break;
case "DEBUG":
level = DEBUG;
break;
default:
break;
}
Matcher m2 = MINECRAFT_LOGGER_CATEGORY.matcher(line);
if (m2.find()) {
String level2Str = m2.group("category");
if (null != level2Str)
switch (level2Str) {
case "STDOUT":
level = INFO;
break;
case "STDERR":
level = ERROR;
break;
}
}
} else {
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]")
|| line.contains("[FINER]") || line.contains("[FINEST]"))
|| line.contains("[FINER]") || line.contains("[FINEST]"))
level = INFO;
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
level = ERROR;
@@ -93,13 +108,23 @@ public enum Level {
if (line.contains("overwriting existing"))
return FATAL;
if (line.contains("Exception in thread")
|| line.matches("\\s+at " + JAVA_SYMBOL)
|| line.matches("Caused by: " + JAVA_SYMBOL)
|| line.matches("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)")
|| line.matches("... \\d+ more$"))
return ERROR;
return preLevel.level < level.level ? preLevel : level;
/*if (line.contains("Exception in thread")
|| line.matches("\\s+at " + JAVA_SYMBOL)
|| line.matches("Caused by: " + JAVA_SYMBOL)
|| line.matches("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)")
|| line.matches("... \\d+ more$"))
return ERROR;*/
return level;
}
public static boolean isError(Level a) {
return a == null ? false : a.lessOrEqual(Level.ERROR);
}
public static Level mergeLevel(Level a, Level b) {
if (a == null) return b;
else if (b == null) return a;
else return a.level < b.level ? a : b;
}
}
@@ -40,6 +40,7 @@ public final class NetUtils {
public static HttpURLConnection createConnection(URL url, Proxy proxy) throws IOException {
HttpURLConnection con = (HttpURLConnection) url.openConnection(proxy);
con.setRequestProperty("User-Agent", "Hello Minecraft!");
con.setDoInput(true);
con.setUseCaches(false);
con.setConnectTimeout(15000);
@@ -48,7 +49,9 @@ public final class NetUtils {
}
public static String get(String url, String encoding) throws IOException {
return IOUtils.toString(new URL(url).openConnection().getInputStream());
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestProperty("User-Agent", "Hello Minecraft!");
return IOUtils.toString(con.getInputStream());
}
public static String get(String url) throws IOException {
@@ -32,7 +32,7 @@ import org.jackhuang.hmcl.util.ui.SwingUtils;
public class WebFrame extends JDialog {
public WebFrame(String... strs) {
this(("<html>" + StrUtils.parseParams(t -> ("<font color='#" + GraphicsUtils.getColor(Level.guessLevel((String) t, Level.INFO).COLOR) + "'>"), strs, t -> "</font><br />") + "</html>")
this(("<html>" + StrUtils.parseParams(t -> ("<font color='#" + GraphicsUtils.getColor(Level.mergeLevel(Level.INFO, Level.guessLevel((String) t)).COLOR) + "'>"), strs, t -> "</font><br />") + "</html>")
.replace(" ", "&nbsp;").replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;"));
}
@@ -0,0 +1,79 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util.sys;
import java.util.Collection;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.IProcess;
import org.jackhuang.hmcl.api.event.process.JVMLaunchFailedEvent;
import org.jackhuang.hmcl.api.event.process.JavaProcessExitedAbnormallyEvent;
import org.jackhuang.hmcl.api.event.process.JavaProcessStoppedEvent;
import org.jackhuang.hmcl.util.CollectionUtils;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.log.Level;
/**
*
* @author huang
*/
public class ExitWaiter extends Thread {
private final ProcessMonitor monitor;
private final IProcess process;
private final Runnable callback;
/**
* Constructor
* @param monitor
* @param callback nullable, will be called when process exited.
*/
public ExitWaiter(ProcessMonitor monitor, Runnable callback) {
this.monitor = monitor;
this.process = monitor.getProcess();
this.callback = callback;
setName("exit-waitor");
}
@Override
public void run() {
try {
int exitCode = process.getRawProcess().waitFor();
monitor.errorThread.join();
monitor.inputThread.join();
Collection<String> errorLines = CollectionUtils.filter(process.getStdOutLines(), str -> Level.isError(Level.guessLevel(str)));
// LaunchWrapper will terminate the application with exit code 0, though this is error state.
if (exitCode != 0 || StrUtils.containsOne(errorLines, "Unable to launch"))
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessExitedAbnormallyEvent(monitor, process));
else if (exitCode != 0 && StrUtils.containsOne(errorLines,
"Could not create the Java Virtual Machine.",
"Error occurred during initialization of VM",
"A fatal exception has occurred. Program will exit.",
"Unable to launch"))
HMCLApi.EVENT_BUS.fireChannel(new JVMLaunchFailedEvent(monitor, process));
else
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessStoppedEvent(this, process));
if (callback != null)
callback.run();
} catch (InterruptedException ex) {
}
}
}
@@ -27,6 +27,7 @@ import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import org.jackhuang.hmcl.api.HMCLog;
@@ -121,6 +121,8 @@ public class Java {
try {
queryJava(ans, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\");
queryJava(ans, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\");
queryJava(ans, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\JRE\\");
queryJava(ans, "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\JDK\\");
} catch (IOException | InterruptedException ex) {
HMCLog.err("Faield to query java", ex);
}
@@ -31,14 +31,14 @@ public enum OS {
LINUX('/', "linux"),
WINDOWS('\\', "windows"),
OSX('/', "osx"),
UNKOWN('/', "universal");
UNKNOWN('/', "universal");
public final char fileSeparator;
public final String checked_name;
public final String checkedName;
private OS(char fileSeparator, String n) {
this.fileSeparator = fileSeparator;
checked_name = n;
checkedName = n;
}
public static OS os() {
@@ -55,7 +55,7 @@ public enum OS {
return OS.LINUX;
if (str.contains("unix"))
return OS.LINUX;
return OS.UNKOWN;
return OS.UNKNOWN;
}
/**
@@ -17,19 +17,11 @@
*/
package org.jackhuang.hmcl.util.sys;
import java.util.Arrays;
import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.process.JVMLaunchFailedEvent;
import org.jackhuang.hmcl.api.event.process.JavaProcessExitedAbnormallyEvent;
import org.jackhuang.hmcl.api.event.process.JavaProcessStartingEvent;
import org.jackhuang.hmcl.api.event.process.JavaProcessStoppedEvent;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.log.Level;
import org.jackhuang.hmcl.api.IProcess;
import org.jackhuang.hmcl.api.event.SimpleEvent;
import org.jackhuang.hmcl.api.func.Consumer;
/**
@@ -41,16 +33,17 @@ public class ProcessMonitor {
public static final HashSet<ProcessMonitor> MONITORS = new HashSet<>();
private final CountDownLatch latch = new CountDownLatch(2);
ProcessThread inputThread, errorThread;
ProcessThread inputThread;
ProcessThread errorThread;
ExitWaiter waitorThread;
WaitForThread waitForThread;
private final IProcess p;
public ProcessMonitor(IProcess p) {
this.p = p;
inputThread = new ProcessThread(this, false);
errorThread = new ProcessThread(this, true);
inputThread.stopEvent.register(this::threadStopped);
inputThread.stopEvent.register(event -> processThreadStopped((ProcessThread) event.getSource()));
errorThread.stopEvent.register(this::threadStopped);
waitorThread = new ExitWaiter(this, this::processThreadStopped);
}
public IProcess getProcess() {
@@ -73,46 +66,14 @@ public class ProcessMonitor {
}
public void start() {
hasFired = false;
MONITORS.add(this);
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessStartingEvent(this, p));
inputThread.start();
errorThread.start();
}
private void threadStopped(SimpleEvent<IProcess> event) {
latch.countDown();
ProcessThread t = (ProcessThread) event.getSource();
HMCLog.log("Process exit code: " + p.getExitCode());
if (p.getExitCode() != 0 || StrUtils.containsOne(t.getLines(),
Arrays.asList("Unable to launch"),
x -> Level.guessLevel(x, Level.INFO).lessOrEqual(Level.ERROR)))
synchronized (this) {
if (!hasFired) {
hasFired = true;
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessExitedAbnormallyEvent(ProcessMonitor.this, p));
}
}
if (p.getExitCode() != 0 && StrUtils.containsOne(t.getLines(),
Arrays.asList("Could not create the Java Virtual Machine.",
"Error occurred during initialization of VM",
"A fatal exception has occurred. Program will exit.",
"Unable to launch"),
x -> Level.guessLevel(x, Level.INFO).lessOrEqual(Level.ERROR)))
synchronized (this) {
if (!hasFired) {
hasFired = true;
HMCLApi.EVENT_BUS.fireChannel(new JVMLaunchFailedEvent(ProcessMonitor.this, p));
}
}
}
boolean hasFired = false;
private void processThreadStopped(ProcessThread t1) {
private void processThreadStopped() {
MONITORS.remove(this);
errorThread.interrupt();
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessStoppedEvent(this, p));
}
public static void stopAll() {
@@ -120,6 +81,7 @@ public class ProcessMonitor {
monitor.getProcess().getRawProcess().destroy();
monitor.inputThread.interrupt();
monitor.errorThread.interrupt();
monitor.waitorThread.interrupt();
}
}
@@ -127,7 +89,6 @@ public class ProcessMonitor {
try {
latch.await();
} catch (InterruptedException ignore) {
HMCLog.warn("Thread has been interrupted.", ignore);
}
}
}
@@ -71,11 +71,20 @@ public class ProcessThread extends Thread {
br = new BufferedReader(new InputStreamReader(in, Charsets.toCharset()));
String line;
while (p.isRunning())
while ((line = br.readLine()) != null)
while (p.isRunning()) {
if (isInterrupted())
return;
while ((line = br.readLine()) != null) {
if (isInterrupted())
return;
println(line);
while ((line = br.readLine()) != null)
}
}
while ((line = br.readLine()) != null) {
if (isInterrupted())
return;
println(line);
}
} catch (IOException e) {
HMCLog.err("An error occured when reading process stdout/stderr.", e);
} finally {
@@ -86,7 +95,6 @@ public class ProcessThread extends Thread {
protected void println(String line) {
printlnEvent.fire(new PrintlnEvent(monitor, line, readError));
(readError ? System.err : System.out).println("MC: " + line);
lines.add(line);
p.getStdOutLines().add(line);
}
@@ -26,7 +26,7 @@ import java.security.PrivilegedExceptionAction;
import sun.misc.Unsafe;
/**
*
* This class is for breaking the module system of Java 9.
* @author huang
*/
public class ReflectionHelper {
@@ -0,0 +1,47 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util.sys;
import java.util.concurrent.CountDownLatch;
import org.jackhuang.hmcl.api.HMCLog;
/**
*
* @author huang
*/
public class WaitForThread extends Thread {
CountDownLatch latch;
Runnable done;
public WaitForThread(CountDownLatch latch, Runnable done) {
this.latch = latch;
}
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException ex) {
HMCLog.err("Interrupted latch waiting");
}
done.run();
}
}
@@ -79,10 +79,9 @@ public class ZipEngine implements Closeable {
File[] files;
if (source.isDirectory())
files = source.listFiles();
else {
files = new File[1];
files[0] = source;
}
else
files = new File[] { source };
if (files == null)
return;
String pathName;//存相对路径(相对于待压缩的根目录)
@@ -27,15 +27,21 @@ import java.util.Collection;
public class DoubleTask extends TaskInfo {
Task a, b;
boolean reliant;
public DoubleTask(Task a, Task b) {
this(a, b, "Double Task");
this(a, b, true);
}
public DoubleTask(Task a, Task b, String info) {
public DoubleTask(Task a, Task b, boolean reliant) {
this(a, b, "Double Task", reliant);
}
public DoubleTask(Task a, Task b, String info, boolean reliant) {
super(info);
this.a = a;
this.b = b;
this.reliant = reliant;
hidden = true;
}
@@ -51,7 +57,7 @@ public class DoubleTask extends TaskInfo {
@Override
public void executeTask(boolean areDependTasksSucceeded) throws IllegalStateException {
if (!areDependTasksSucceeded)
if (!areDependTasksSucceeded && reliant)
throw new IllegalStateException("Depend tasks failed.");
}
@@ -33,7 +33,9 @@ public class ParallelTask extends Task {
}
@Override
public void executeTask(boolean areDependTasksSucceeded) {
public void executeTask(boolean areDependTasksSucceeded) throws Exception {
if (!areDependTasksSucceeded)
throw new Exception("Tasks failed");
}
@Override
@@ -46,8 +48,9 @@ public class ParallelTask extends Task {
return tasks;
}
public void addTask(Task t) {
public ParallelTask addTask(Task t) {
tasks.add(t);
return this;
}
}
@@ -103,7 +103,7 @@ public class TaskList extends Thread {
}
Invoker thread = new Invoker(t2, counter, bool);
invokers.add(thread);
if (!EXECUTOR_SERVICE.isShutdown() && !EXECUTOR_SERVICE.isTerminated())
if (!EXECUTOR_SERVICE.isShutdown())
futures.put(thread, EXECUTOR_SERVICE.submit(thread));
}
try {
@@ -24,7 +24,7 @@ import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;
import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.util.ui.MessageBox;
import org.jackhuang.hmcl.util.StrUtils;
import org.jackhuang.hmcl.util.ui.SwingUtils;
@@ -0,0 +1,71 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util.ui;
import java.awt.Component;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
/**
*
* @author huang
*/
public class JFontComboBox extends JComboBox<String> implements ItemListener {
private static DefaultComboBoxModel<String> defaultModel = null;
private int fontSize;
protected static DefaultComboBoxModel<String> getDefaultModel() {
if (defaultModel == null)
defaultModel = new DefaultComboBoxModel<>(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
return defaultModel;
}
public JFontComboBox() {
super();
setRenderer(new JFontComboBoxCellRenderer());
setModel(getDefaultModel());
addItemListener(this);
fontSize = getFont().getSize();
}
@Override
public void itemStateChanged(ItemEvent e) {
setFont(new Font(e.getItem().toString(), Font.PLAIN, fontSize));
}
public static class JFontComboBoxCellRenderer extends JLabel implements ListCellRenderer<String> {
@Override
public Component getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) {
Font font = new Font(value, Font.PLAIN, 14);
setEnabled(list.isEnabled());
setText(font.getFontName());
setFont(font);
return this;
}
}
}
@@ -15,10 +15,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
package org.jackhuang.hmcl.util.ui;
import org.jackhuang.hmcl.util.ui.SwingUtils;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import org.jackhuang.hmcl.util.C;
/**
* @author huangyuhui
@@ -103,6 +104,18 @@ public class MessageBox {
}
return 0;
}
public static String showInputDialog(String msg) {
return showInputDialog(msg, UIManager.getString("OptionPane.inputDialogTitle"));
}
public static String showInputDialog(String msg, String title) {
return showInputDialog(msg, title, null);
}
public static String showInputDialog(String msg, String title, String init) {
return (String) JOptionPane.showInputDialog(null, msg, title, JOptionPane.QUESTION_MESSAGE, null, null, init);
}
/**
* Show MsgBox with options
@@ -130,4 +143,11 @@ public class MessageBox {
public static int showLocalized(String msg) {
return show(C.i18n(msg));
}
static {
UIManager.put("OptionPane.cancelButtonText", C.i18n("button.cancel"));
UIManager.put("OptionPane.noButtonText", C.i18n("button.no"));
UIManager.put("OptionPane.okButtonText", C.i18n("button.ok"));
UIManager.put("OptionPane.yesButtonText", C.i18n("button.yes"));
}
}
@@ -148,6 +148,11 @@ public class Page extends TopTabPage {
Timer timer;
protected boolean animationEnabled = true;
public Page setAnimationEnabled(boolean a) {
animationEnabled = a;
return this;
}
public void animate() {
if (animationEnabled) {
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more