Compare commits

...
4 Commits
40 changed files with 827 additions and 195 deletions

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
<Configuration status="WARN">
<Appenders>
<Console name="SysOut" target="SYSTEM_OUT">
<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>
@@ -86,10 +86,13 @@ public final class Config implements Cloneable {
private String fontFamily;
@SerializedName("fontSize")
private int fontSize;
@SerializedName("logLines")
private int logLines;
public Config() {
clientToken = UUID.randomUUID().toString();
logintype = downloadtype = 0;
logLines = 100;
enableAnimation = enableBlur = true;
if (OS.os() == OS.WINDOWS)
enableShadow = true;
@@ -315,6 +318,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 {
@@ -187,7 +187,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"));
@@ -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());
}
}
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui;
import java.io.File;
import java.io.IOException;
import java.io.PipedOutputStream;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.jackhuang.hmcl.api.HMCLApi;
@@ -40,15 +41,19 @@ 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.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.PrintlnEvent;
import org.jackhuang.hmcl.util.sys.ProcessMonitor;
import org.xml.sax.XMLReader;
/**
*
* @author huangyuhui
*/
public class LaunchingUIDaemon {
XMLReader reader;
public LaunchingUIDaemon() {
HMCLApi.EVENT_BUS.channel(LaunchingStateChangedEvent.class).register(LAUNCHING_STATE_CHANGED);
@@ -68,8 +73,15 @@ 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 {
Log4jHandler handler = new Log4jHandler(os);
handler.start();
} catch(Exception e) {
HMCLog.err("", e);
}
monitor.registerPrintlnEvent(new PrintlnProcessor(obj, os));
monitor.start();
});
HMCLApi.EVENT_BUS.channel(LaunchSucceededEvent.class).register(p -> {
@@ -145,19 +157,39 @@ public class LaunchingUIDaemon {
((HMCLGameLauncher.GameLauncherTag) value.getTag()).state = 2;
}, MainFrame.INSTANCE::failed, Settings.getInstance().getAuthenticator().getPassword());
}
class PrintlnProcessor implements Consumer<PrintlnEvent> {
GameLauncher launcher;
PipedOutputStream os;
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));
}
};
public PrintlnProcessor(GameLauncher launcher, PipedOutputStream os) {
this.launcher = launcher;
this.os = os;
}
@Override
public void accept(PrintlnEvent t) {
if (!t.isError())
try {
os.write((t.getLine() + C.LINE_SEPARATOR).replace("log4j:Event", "log4j_Event").replace("log4j:Message", "log4j_Message").getBytes());
os.flush();
} catch(IOException e) {
HMCLog.err("", e);
}
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)
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;
@@ -217,4 +249,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="183" 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,22 @@
<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="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cboShowLines" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" 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 +68,27 @@
<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="jLabel1" 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"/>
</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 +133,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 +153,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 +161,59 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextPane" name="txtLog">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JLineWrapTextPane()"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" value="&#x663e;&#x793a;&#x884c;&#x6570;"/>
<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="debugs"/>
</Properties>
</Component>
<Component class="javax.swing.JToggleButton" name="btnInfo">
<Properties>
<Property name="text" type="java.lang.String" value="infos"/>
</Properties>
</Component>
<Component class="javax.swing.JToggleButton" name="btnWarn">
<Properties>
<Property name="text" type="java.lang.String" value="warns"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnError">
<Properties>
<Property name="text" type="java.lang.String" value="errors"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="btnFatal">
<Properties>
<Property name="text" type="java.lang.String" value="fatals"/>
</Properties>
</Component>
</SubComponents>
</Form>
@@ -17,8 +17,9 @@
*/
package org.jackhuang.hmcl.ui;
import java.awt.Font;
import java.io.PrintStream;
import java.util.Deque;
import java.util.LinkedList;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
@@ -38,7 +39,8 @@ import org.jackhuang.hmcl.util.ui.SwingUtils;
* @author huangyuhui
*/
public class LogWindow extends javax.swing.JFrame {
public static LogWindowOutputStream outputStream;
NonFunction<Boolean> listener;
/**
@@ -47,7 +49,7 @@ 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));
@@ -74,13 +76,17 @@ 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();
jLabel1 = 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();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle(C.i18n("logwindow.title")); // NOI18N
@@ -113,24 +119,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);
}
});
@@ -141,32 +133,38 @@ 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);
pnlLog.setViewportView(txtLog);
jLabel1.setText("显示行数");
jLabel1.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("debugs");
btnInfo.setText("infos");
btnWarn.setText("warns");
btnError.setText("errors");
btnFatal.setText("fatals");
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, 183, Short.MAX_VALUE)
.addComponent(btnTerminateGame)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCopy)
@@ -174,26 +172,47 @@ 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(jLabel1)
.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, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.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(jLabel1)
.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))
.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())
);
@@ -214,47 +233,74 @@ public class LogWindow extends javax.swing.JFrame {
Utils.setClipborad(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
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) {
}
}
});
}
@@ -268,27 +314,25 @@ public class LogWindow extends javax.swing.JFrame {
@Override
public void setVisible(boolean b) {
cboShowLines.setSelectedItem(Settings.getInstance().getLogLines());
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) {
cboShowLines.setSelectedItem(Settings.getInstance().getLogLines());
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"));
}
@@ -298,12 +342,16 @@ 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 jLabel1;
private javax.swing.JLabel lblCrash;
private javax.swing.JScrollPane pnlLog;
private javax.swing.JTextPane txtLog;
@@ -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);
});
}
}
@@ -0,0 +1,45 @@
/*
* 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 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 void appendJVMArgs(List<String> list) {
super.appendJVMArgs(list);
list.add("-Dminecraft.launcher.version=" + Main.LAUNCHER_VERSION);
list.add("-Dminecraft.launcher.brand=" + Main.LAUNCHER_NAME);
}
}
@@ -175,7 +175,7 @@ public class HMCLMinecraftService extends IMinecraftService {
@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,124 @@
/*
* 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.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.parsers.ParserConfigurationException;
import org.jackhuang.hmcl.ui.LogWindow;
import org.jackhuang.hmcl.util.log.Level;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
/**
*
* @author huang
*/
public class Log4jHandler extends Thread {
XMLReader reader;
PipedInputStream inputStream;
PipedOutputStream outputStream;
public Log4jHandler(PipedOutputStream outputStream) throws ParserConfigurationException, IOException, SAXException {
/*SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
.*/
reader = XMLReaderFactory.createXMLReader();
inputStream = new PipedInputStream(outputStream);
this.outputStream = outputStream;
}
@Override
public void run() {
try {
outputStream.write("<output>".getBytes());
outputStream.flush();
//reader.parse(inputStream, new Log4jHandlerImpl());
reader.setContentHandler(new Log4jHandlerImpl());
reader.parse(new InputSource(inputStream));
} catch (SAXException | IOException e) {
}
}
class Log4jHandlerImpl extends DefaultHandler {
private final SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
String message = "", date = "", thread = "", logger = "";
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 = "";
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, 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 += line + C.LINE_SEPARATOR;
else
println(line, Level.guessLevel(line));
}
public void println(String message, Level l) {
if (LogWindow.outputStream != null)
LogWindow.outputStream.log(message, l);
}
}
}
@@ -37,8 +37,8 @@ public interface IMinecraftLibrary extends Cloneable {
File getFilePath(File gameDir);
String getName();
boolean isRequiredToUnzip();
boolean isNative();
String getDownloadURL(String downloadSource);
@@ -32,10 +32,12 @@ import org.jackhuang.hmcl.core.version.AssetIndexDownloadInfo;
import org.jackhuang.hmcl.core.version.MinecraftVersion;
import org.jackhuang.hmcl.util.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
@@ -143,7 +159,7 @@ public class MinecraftAssetService extends IMinecraftAssetService {
}
}
protected boolean checkAssetsExistance(AssetIndexDownloadInfo assetIndex) {
protected boolean checkAssetsExistance(AssetIndexDownloadInfo assetIndex, LoggingInfo info) {
String assetId = assetIndex.getId();
File indexFile = getIndexFile(assetId);
File assetDir = getAssets(assetId);
@@ -157,6 +173,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;
@@ -217,7 +237,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 && !checkAssetsExistance(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();
@@ -30,6 +30,7 @@ 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.LoggingInfo;
/**
*
@@ -46,7 +47,7 @@ public class MinecraftLoader extends AbstractMinecraftLoader {
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;
@@ -111,6 +112,15 @@ 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) -> {
@@ -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);
}
@@ -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;
@@ -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);
}
}
}
@@ -84,7 +84,7 @@ public class MinecraftLibrary extends AbstractMinecraftLibrary {
}
@Override
public boolean isRequiredToUnzip() {
public boolean isNative() {
return natives != null && allow();
}
@@ -125,11 +125,7 @@ public class MinecraftLibrary extends AbstractMinecraftLibrary {
downloads.classifiers = new HashMap<>();
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;
@@ -71,11 +73,13 @@ 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(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.minecraftArguments = minecraftArguments;
this.mainClass = mainClass;
@@ -109,6 +113,13 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
for (Map.Entry<String, GameDownloadInfo> entry : downloads.entrySet())
this.downloads.put(entry.getKey(), (GameDownloadInfo) entry.getValue().clone());
}
if (logging == null)
this.logging = null;
else {
this.logging = new HashMap<>(logging.size());
for (Map.Entry<String, LoggingInfo> entry : logging.entrySet())
this.logging.put(entry.getKey(), (LoggingInfo) entry.getValue().clone());
}
}
@Override
@@ -148,7 +159,8 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
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.assetIndex != null ? this.assetIndex : parent.assetIndex,
this.logging != null ? this.logging : parent.logging);
return result;
}
@@ -164,8 +176,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() {
@@ -193,8 +204,6 @@ public class MinecraftVersion implements Cloneable, Comparable<MinecraftVersion>
return Objects.equals(this.id, ((MinecraftVersion) obj).id);
}
public AssetIndexDownloadInfo getAssetsIndex() {
if (assetIndex == null)
assetIndex = new AssetIndexDownloadInfo(assets == null ? AssetsIndex.DEFAULT_ASSET_NAME : assets);
@@ -212,7 +221,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);
}
}
@@ -248,7 +248,7 @@ public class MinecraftVersionManager<T extends IMinecraftService> extends IMinec
ArrayList<File> unzippings = new ArrayList<>();
ArrayList<Extract> extractRules = new ArrayList<>();
for (IMinecraftLibrary l : v.libraries)
if (l.isRequiredToUnzip() && v.isAllowedToUnpackNatives()) {
if (l.isNative() && v.isAllowedToUnpackNatives()) {
unzippings.add(getLibraryFile(v, l));
extractRules.add(l.getDecompressExtractRules());
}
@@ -31,12 +31,12 @@ public final class C {
//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() {
}
@@ -51,10 +51,8 @@ public enum 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) {
if (line.startsWith("MC:"))
line = line.substring("MC:".length());
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
@@ -109,13 +107,23 @@ public enum Level {
if (line.contains("overwriting existing"))
return FATAL;
if (line.contains("Exception in thread")
/*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;
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;
}
}
@@ -0,0 +1,26 @@
/*
* 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.log;
/**
*
* @author huang
*/
public class Parser {
}
@@ -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;"));
}
@@ -41,7 +41,9 @@ 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;
WaitForThread waitForThread;
private final IProcess p;
public ProcessMonitor(IProcess p) {
@@ -83,10 +85,15 @@ public class ProcessMonitor {
private void threadStopped(SimpleEvent<IProcess> event) {
latch.countDown();
ProcessThread t = (ProcessThread) event.getSource();
HMCLog.log("Process exit code: " + p.getExitCode());
int exitCode = Integer.MAX_VALUE;
try {
exitCode = p.getExitCode();
} catch(IllegalThreadStateException e) {
HMCLog.err("Failed to ");
}
if (p.getExitCode() != 0 || StrUtils.containsOne(t.getLines(),
Arrays.asList("Unable to launch"),
x -> Level.guessLevel(x, Level.INFO).lessOrEqual(Level.ERROR)))
Arrays.asList("Unable to launch"), // LaunchWrapper will terminate the application returning exit code 0, but this is an error state.
x -> Level.isError(Level.guessLevel(x))))
synchronized (this) {
if (!hasFired) {
hasFired = true;
@@ -98,7 +105,7 @@ public class ProcessMonitor {
"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)))
x -> Level.isError(Level.guessLevel(x))))
synchronized (this) {
if (!hasFired) {
hasFired = true;
@@ -127,7 +134,6 @@ public class ProcessMonitor {
try {
latch.await();
} catch (InterruptedException ignore) {
HMCLog.warn("Thread has been interrupted.", ignore);
}
}
}
@@ -86,7 +86,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);
}
@@ -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();
}
}
@@ -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.");
}
@@ -48,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;
}
}
@@ -123,10 +123,9 @@ public class InstructionsPanelImpl extends JComponent implements WizardObserver,
public InstructionsPanelImpl(BufferedImage img, Wizard wizard) {
if (img == null)
try {
img = ImageIO.read(InstructionsPanelImpl.class.getResourceAsStream(
"/org/jackhuang/hmcl/wizard.jpg"));
} catch (IOException ioe) {
HMCLog.err("Failed to load wizard.jpg, maybe you fucking modified the launcher", ioe);
img = ImageIO.read(InstructionsPanelImpl.class.getResourceAsStream("/org/jackhuang/hmcl/wizard.jpg"));
} catch (IOException | NullPointerException ioe) {
HMCLog.err("Failed to load wizard.jpg, maybe you have fuckingly modified the launcher file", ioe);
}
this.img = img;
this.wizard = wizard;
@@ -390,8 +390,8 @@ update.failed=Failed to check for updates.
update.found=(Found Update!)
logwindow.terminate_game=Terminate Game
logwindow.tieba=Baidu Tieba
logwindow.title=Log
logwindow.contact=Contact Us
selector.choose=Choose
@@ -390,8 +390,8 @@ update.failed=Failed to check for updates.
update.found=(Found Update!)
logwindow.terminate_game=Terminate Game
logwindow.tieba=Baidu Tieba
logwindow.title=Log
logwindow.contact=Contact Us
selector.choose=Choose
@@ -388,8 +388,8 @@ update.failed=Kiểm tra cập nhật thất bại.
update.found=(Đã tìm thấy bản cập nhật!)
logwindow.terminate_game=Tắt Game
logwindow.tieba=Baidu Tieba
logwindow.title=HMCL Error Log (Hãy đăng cái này lên forum!)
logwindow.contact=Contact Us
selector.choose=Chọn
@@ -388,8 +388,8 @@ update.failed=Ki\u1ec3m tra c\u1eadp nh\u1eadt th\u1ea5t b\u1ea1i.
update.found=(\u0110\u00e3 t\u00ecm th\u1ea5y b\u1ea3n c\u1eadp nh\u1eadt!)
logwindow.terminate_game=T\u1eaft Game
logwindow.tieba=Baidu Tieba
logwindow.title=HMCL Error Log (H\u00e3y \u0111\u0103ng c\u00e1i n\u00e0y l\u00ean forum!)
logwindow.contact=Contact Us
selector.choose=Ch\u1ecdn
@@ -390,8 +390,8 @@ update.failed=檢查更新失敗
update.found=(發現更新!)
logwindow.terminate_game=結束遊戲進程
logwindow.tieba=貼吧
logwindow.title=日誌
logwindow.contact=聯繫我們
selector.choose=選擇
@@ -390,8 +390,8 @@ update.failed=\u6aa2\u67e5\u66f4\u65b0\u5931\u6557
update.found=(\u767c\u73fe\u66f4\u65b0!)
logwindow.terminate_game=\u7d50\u675f\u904a\u6232\u9032\u7a0b
logwindow.tieba=\u8cbc\u5427
logwindow.title=\u65e5\u8a8c
logwindow.contact=\u806f\u7e6b\u6211\u5011
selector.choose=\u9078\u64c7
@@ -390,8 +390,8 @@ update.failed=检查更新失败
update.found=(发现更新!)
logwindow.terminate_game=结束游戏进程
logwindow.tieba=贴吧
logwindow.title=日志
logwindow.contact=联系我们
selector.choose=选择
@@ -390,8 +390,8 @@ update.failed=\u68c0\u67e5\u66f4\u65b0\u5931\u8d25
update.found=(\u53d1\u73b0\u66f4\u65b0!)
logwindow.terminate_game=\u7ed3\u675f\u6e38\u620f\u8fdb\u7a0b
logwindow.tieba=\u8d34\u5427
logwindow.title=\u65e5\u5fd7
logwindow.contact=\u8054\u7cfb\u6211\u4eec
selector.choose=\u9009\u62e9