Fixed outputing crash advice twice

This commit is contained in:
huangyuhui
2017-03-12 13:50:20 +08:00
parent fd2a5a07a1
commit 4f03ec503f
9 changed files with 104 additions and 52 deletions

View File

@@ -38,10 +38,10 @@ import org.jackhuang.hmcl.util.C;
import org.jackhuang.hmcl.util.MessageBox; import org.jackhuang.hmcl.util.MessageBox;
import org.jackhuang.hmcl.api.func.Consumer; import org.jackhuang.hmcl.api.func.Consumer;
import org.jackhuang.hmcl.api.HMCLog; import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.api.event.SimpleEvent;
import org.jackhuang.hmcl.api.event.launch.LaunchingState; import org.jackhuang.hmcl.api.event.launch.LaunchingState;
import org.jackhuang.hmcl.util.DefaultPlugin; import org.jackhuang.hmcl.util.DefaultPlugin;
import org.jackhuang.hmcl.util.sys.FileUtils; import org.jackhuang.hmcl.util.sys.FileUtils;
import org.jackhuang.hmcl.util.sys.PrintlnEvent;
import org.jackhuang.hmcl.util.sys.ProcessMonitor; import org.jackhuang.hmcl.util.sys.ProcessMonitor;
/** /**
@@ -81,9 +81,10 @@ public class LaunchingUIDaemon {
}); });
HMCLApi.EVENT_BUS.channel(JavaProcessStoppedEvent.class).register(event -> checkExit((LauncherVisibility) ((ProcessMonitor) event.getSource()).getTag())); HMCLApi.EVENT_BUS.channel(JavaProcessStoppedEvent.class).register(event -> checkExit((LauncherVisibility) ((ProcessMonitor) event.getSource()).getTag()));
HMCLApi.EVENT_BUS.channel(JavaProcessExitedAbnormallyEvent.class).register(event -> { HMCLApi.EVENT_BUS.channel(JavaProcessExitedAbnormallyEvent.class).register(event -> {
ProcessMonitor monitor = (ProcessMonitor) event.getSource();
int exitCode = event.getValue().getExitCode(); int exitCode = event.getValue().getExitCode();
event.getValue().waitForCommandLineCompletion();
HMCLog.err("The game exited abnormally, exit code: " + exitCode); HMCLog.err("The game exited abnormally, exit code: " + exitCode);
monitor.waitForCommandLineCompletion();
String[] logs = event.getValue().getStdOutLines().toArray(new String[0]); String[] logs = event.getValue().getStdOutLines().toArray(new String[0]);
String errorText = null; String errorText = null;
for (String s : logs) { for (String s : logs) {
@@ -135,9 +136,9 @@ public class LaunchingUIDaemon {
}, MainFrame.INSTANCE::failed, Settings.getInstance().getAuthenticator().getPassword()); }, MainFrame.INSTANCE::failed, Settings.getInstance().getAuthenticator().getPassword());
} }
private static final Consumer<SimpleEvent<String>> PRINTLN = t -> { private static final Consumer<PrintlnEvent> PRINTLN = t -> {
LauncherVisibility l = ((LauncherVisibility) ((ProcessMonitor) t.getSource()).getTag()); LauncherVisibility l = ((LauncherVisibility) ((ProcessMonitor) t.getSource()).getTag());
if (t.getValue().contains("LWJGL Version: ") && l != LauncherVisibility.KEEP) if (t.getLine().contains("LWJGL Version: ") && l != LauncherVisibility.KEEP)
if (l != LauncherVisibility.HIDE_AND_REOPEN) if (l != LauncherVisibility.HIDE_AND_REOPEN)
MainFrame.INSTANCE.dispose(); MainFrame.INSTANCE.dispose();
else else

View File

@@ -39,6 +39,4 @@ public interface IProcess {
void stop(); void stop();
void waitForCommandLineCompletion();
} }

View File

@@ -17,12 +17,11 @@
*/ */
package org.jackhuang.hmcl.util.sys; package org.jackhuang.hmcl.util.sys;
import java.util.ArrayList;
import org.jackhuang.hmcl.api.IProcess; import org.jackhuang.hmcl.api.IProcess;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import org.jackhuang.hmcl.api.HMCLog;
/** /**
* *
@@ -30,10 +29,9 @@ import org.jackhuang.hmcl.api.HMCLog;
*/ */
public class JavaProcess implements IProcess { public class JavaProcess implements IProcess {
private final CountDownLatch latch = new CountDownLatch(2);
private final List<String> commands; private final List<String> commands;
private final Process process; private final Process process;
private final Vector<String> stdOutLines = new Vector<>(); private final List<String> stdOutLines = Collections.synchronizedList(new ArrayList<>());
public JavaProcess(List<String> commands, Process process) { public JavaProcess(List<String> commands, Process process) {
this.commands = commands; this.commands = commands;
@@ -75,19 +73,6 @@ public class JavaProcess implements IProcess {
return false; return false;
} }
CountDownLatch getLatch() {
return latch;
}
@Override
public void waitForCommandLineCompletion() {
try {
latch.await();
} catch (InterruptedException ignore) {
HMCLog.warn("Thread has been interrupted.", ignore);
}
}
@Override @Override
public int getExitCode() { public int getExitCode() {
try { try {

View File

@@ -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.sys;
import java.util.EventObject;
/**
* For ProcessThread.
* @author huang
*/
public class PrintlnEvent extends EventObject {
String line;
boolean error;
public PrintlnEvent(Object source, String line, boolean isError) {
super(source);
this.line = line;
this.error = isError;
}
public String getLine() {
return line;
}
public boolean isError() {
return error;
}
}

View File

@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.util.sys;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
import org.jackhuang.hmcl.api.HMCLApi; import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.process.JVMLaunchFailedEvent; import org.jackhuang.hmcl.api.event.process.JVMLaunchFailedEvent;
import org.jackhuang.hmcl.api.event.process.JavaProcessExitedAbnormallyEvent; import org.jackhuang.hmcl.api.event.process.JavaProcessExitedAbnormallyEvent;
@@ -39,6 +40,7 @@ public class ProcessMonitor {
public static final HashSet<ProcessMonitor> MONITORS = new HashSet<>(); public static final HashSet<ProcessMonitor> MONITORS = new HashSet<>();
private final CountDownLatch latch = new CountDownLatch(2);
ProcessThread inputThread, errorThread; ProcessThread inputThread, errorThread;
private final IProcess p; private final IProcess p;
@@ -64,13 +66,14 @@ public class ProcessMonitor {
public void setTag(Object tag) { public void setTag(Object tag) {
this.tag = tag; this.tag = tag;
} }
public void registerPrintlnEvent(Consumer<SimpleEvent<String>> c) { public void registerPrintlnEvent(Consumer<PrintlnEvent> c) {
inputThread.printlnEvent.register(c); inputThread.printlnEvent.register(c);
errorThread.printlnEvent.register(c); errorThread.printlnEvent.register(c);
} }
public void start() { public void start() {
hasFired = false;
MONITORS.add(this); MONITORS.add(this);
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessStartingEvent(this, p)); HMCLApi.EVENT_BUS.fireChannel(new JavaProcessStartingEvent(this, p));
inputThread.start(); inputThread.start();
@@ -78,21 +81,34 @@ public class ProcessMonitor {
} }
private void threadStopped(SimpleEvent<IProcess> event) { private void threadStopped(SimpleEvent<IProcess> event) {
latch.countDown();
ProcessThread t = (ProcessThread) event.getSource(); ProcessThread t = (ProcessThread) event.getSource();
HMCLog.log("Process exit code: " + p.getExitCode()); HMCLog.log("Process exit code: " + p.getExitCode());
if (p.getExitCode() != 0 || StrUtils.containsOne(t.getLines(), if (p.getExitCode() != 0 || StrUtils.containsOne(t.getLines(),
Arrays.asList("Unable to launch"), Arrays.asList("Unable to launch"),
x -> Level.guessLevel(x, Level.INFO).lessOrEqual(Level.ERROR))) x -> Level.guessLevel(x, Level.INFO).lessOrEqual(Level.ERROR)))
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessExitedAbnormallyEvent(ProcessMonitor.this, p)); synchronized (this) {
if (!hasFired) {
hasFired = true;
HMCLApi.EVENT_BUS.fireChannel(new JavaProcessExitedAbnormallyEvent(ProcessMonitor.this, p));
}
}
if (p.getExitCode() != 0 && StrUtils.containsOne(t.getLines(), if (p.getExitCode() != 0 && StrUtils.containsOne(t.getLines(),
Arrays.asList("Could not create the Java Virtual Machine.", Arrays.asList("Could not create the Java Virtual Machine.",
"Error occurred during initialization of VM", "Error occurred during initialization of VM",
"A fatal exception has occurred. Program will exit.", "A fatal exception has occurred. Program will exit.",
"Unable to launch"), "Unable to launch"),
x -> Level.guessLevel(x, Level.INFO).lessOrEqual(Level.ERROR))) x -> Level.guessLevel(x, Level.INFO).lessOrEqual(Level.ERROR)))
HMCLApi.EVENT_BUS.fireChannel(new JVMLaunchFailedEvent(ProcessMonitor.this, p)); 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(ProcessThread t1) {
MONITORS.remove(this); MONITORS.remove(this);
errorThread.interrupt(); errorThread.interrupt();
@@ -106,4 +122,12 @@ public class ProcessMonitor {
monitor.errorThread.interrupt(); monitor.errorThread.interrupt();
} }
} }
public void waitForCommandLineCompletion() {
try {
latch.await();
} catch (InterruptedException ignore) {
HMCLog.warn("Thread has been interrupted.", ignore);
}
}
} }

View File

@@ -21,8 +21,8 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Vector;
import org.jackhuang.hmcl.api.HMCLog; import org.jackhuang.hmcl.api.HMCLog;
import org.jackhuang.hmcl.api.event.EventHandler; import org.jackhuang.hmcl.api.event.EventHandler;
import org.jackhuang.hmcl.api.event.SimpleEvent; import org.jackhuang.hmcl.api.event.SimpleEvent;
@@ -36,24 +36,27 @@ import org.jackhuang.hmcl.api.IProcess;
*/ */
public class ProcessThread extends Thread { public class ProcessThread extends Thread {
Vector<String> lines = new Vector<>(); List<String> lines = new ArrayList<>();
ProcessMonitor monitor; ProcessMonitor monitor;
IProcess p;
boolean readError; boolean readError;
public final EventHandler<SimpleEvent<String>> printlnEvent = new EventHandler<>(); public final EventHandler<PrintlnEvent> printlnEvent = new EventHandler<>();
public final EventHandler<SimpleEvent<IProcess>> stopEvent = new EventHandler<>(); public final EventHandler<SimpleEvent<IProcess>> stopEvent = new EventHandler<>();
public ProcessThread(ProcessMonitor monitor, boolean readError) { public ProcessThread(ProcessMonitor monitor, boolean readError) {
this.monitor = monitor; this.monitor = monitor;
this.readError = readError; this.readError = readError;
p = monitor.getProcess();
setDaemon(readError); setDaemon(readError);
} }
public IProcess getProcess() { public IProcess getProcess() {
return monitor.getProcess(); return p;
} }
/** /**
* Only get stdout or stderr output according to readError(). * Only get stdout or stderr output according to readError().
* Invoke this method only if the process thread has stopped.
*/ */
public List<String> getLines() { public List<String> getLines() {
return lines; return lines;
@@ -63,32 +66,28 @@ public class ProcessThread extends Thread {
public void run() { public void run() {
setName("ProcessMonitor"); setName("ProcessMonitor");
BufferedReader br = null; BufferedReader br = null;
IProcess p = monitor.getProcess();
try { try {
InputStream in = readError ? p.getRawProcess().getErrorStream() : p.getRawProcess().getInputStream(); InputStream in = readError ? p.getRawProcess().getErrorStream() : p.getRawProcess().getInputStream();
br = new BufferedReader(new InputStreamReader(in, Charsets.toCharset())); br = new BufferedReader(new InputStreamReader(in, Charsets.toCharset()));
String line; String line;
while (p.isRunning()) while (p.isRunning())
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null)
printlnEvent.fire(new SimpleEvent<>(monitor, line)); println(line);
System.out.println("MC: " + line); while ((line = br.readLine()) != null)
lines.add(line); println(line);
p.getStdOutLines().add(line);
}
while ((line = br.readLine()) != null) {
printlnEvent.fire(new SimpleEvent<>(monitor, line));
System.out.println("MC: " + line);
lines.add(line);
p.getStdOutLines().add(line);
}
} catch (IOException e) { } catch (IOException e) {
HMCLog.err("An error occured when reading process stdout/stderr.", e); HMCLog.err("An error occured when reading process stdout/stderr.", e);
} finally { } finally {
IOUtils.closeQuietly(br); IOUtils.closeQuietly(br);
} }
if (p instanceof JavaProcess)
((JavaProcess) p).getLatch().countDown();
stopEvent.fire(new SimpleEvent<>(this, p)); stopEvent.fire(new SimpleEvent<>(this, p));
} }
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);
}
} }

View File

@@ -267,7 +267,7 @@ public class BEButtonUI extends BasicButtonUI implements Skin {
state = State.ROLLOVER; state = State.ROLLOVER;
else if (b instanceof JButton else if (b instanceof JButton
&& ((JButton) b).isDefaultButton()) && ((JButton) b).isDefaultButton())
state = State.DEFAULTED; state = State.DEFAULT;
break; break;
default: default:
state = State.NORMAL; state = State.NORMAL;

View File

@@ -98,7 +98,7 @@ public class AnimationController implements ActionListener, PropertyChangeListen
State oldState = controller.getState(c, part); State oldState = controller.getState(c, part);
if (oldState != newState) { if (oldState != newState) {
controller.putState(c, part, newState); controller.putState(c, part, newState);
if (newState == State.DEFAULTED) if (newState == State.DEFAULT)
// it seems for DEFAULTED button state Vista does animation from // it seems for DEFAULTED button state Vista does animation from
// HOT // HOT
oldState = State.ROLLOVER; oldState = State.ROLLOVER;
@@ -182,7 +182,7 @@ public class AnimationController implements ActionListener, PropertyChangeListen
State endState, State endState,
long millis) { long millis) {
boolean isForwardAndReverse = false; boolean isForwardAndReverse = false;
if (endState == State.DEFAULTED) if (endState == State.DEFAULT)
isForwardAndReverse = true; isForwardAndReverse = true;
Map<Part, AnimationState> map = animationStateMap.get(component); Map<Part, AnimationState> map = animationStateMap.get(component);
if (millis <= 0) { if (millis <= 0) {

View File

@@ -231,7 +231,7 @@ public class TMSchema {
BULLETNORMAL, BULLETNORMAL,
BULLETDISABLED, BULLETDISABLED,
CLOSED, CLOSED,
DEFAULTED, DEFAULT,
DISABLED, DISABLED,
DISABLEDHOT, DISABLEDHOT,
DISABLEDPUSHED, DISABLEDPUSHED,
@@ -304,7 +304,7 @@ public class TMSchema {
}); });
stateMap.put(Part.BP_PUSHBUTTON, stateMap.put(Part.BP_PUSHBUTTON,
new State[] { NORMAL, ROLLOVER, PRESSED, DISABLED, DEFAULTED }); new State[] { NORMAL, ROLLOVER, PRESSED, DISABLED, DEFAULT });
stateMap.put(Part.BP_RADIOBUTTON, stateMap.put(Part.BP_RADIOBUTTON,
new State[] { new State[] {