From 4f03ec503fc02ea717e1c6c161091104df007460 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Sun, 12 Mar 2017 13:50:20 +0800 Subject: [PATCH] Fixed outputing crash advice twice --- .../jackhuang/hmcl/ui/LaunchingUIDaemon.java | 9 ++-- .../java/org/jackhuang/hmcl/api/IProcess.java | 2 - .../jackhuang/hmcl/util/sys/JavaProcess.java | 21 ++------- .../jackhuang/hmcl/util/sys/PrintlnEvent.java | 45 +++++++++++++++++++ .../hmcl/util/sys/ProcessMonitor.java | 32 +++++++++++-- .../hmcl/util/sys/ProcessThread.java | 37 ++++++++------- .../jackhuang/hmcl/laf/button/BEButtonUI.java | 2 +- .../hmcl/laf/utils/AnimationController.java | 4 +- .../jackhuang/hmcl/laf/utils/TMSchema.java | 4 +- 9 files changed, 104 insertions(+), 52 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/PrintlnEvent.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LaunchingUIDaemon.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LaunchingUIDaemon.java index d74ef360e..bf869ab77 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LaunchingUIDaemon.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LaunchingUIDaemon.java @@ -38,10 +38,10 @@ import org.jackhuang.hmcl.util.C; import org.jackhuang.hmcl.util.MessageBox; import org.jackhuang.hmcl.api.func.Consumer; 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.util.DefaultPlugin; import org.jackhuang.hmcl.util.sys.FileUtils; +import org.jackhuang.hmcl.util.sys.PrintlnEvent; 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(JavaProcessExitedAbnormallyEvent.class).register(event -> { + ProcessMonitor monitor = (ProcessMonitor) event.getSource(); int exitCode = event.getValue().getExitCode(); - event.getValue().waitForCommandLineCompletion(); HMCLog.err("The game exited abnormally, exit code: " + exitCode); + monitor.waitForCommandLineCompletion(); String[] logs = event.getValue().getStdOutLines().toArray(new String[0]); String errorText = null; for (String s : logs) { @@ -135,9 +136,9 @@ public class LaunchingUIDaemon { }, MainFrame.INSTANCE::failed, Settings.getInstance().getAuthenticator().getPassword()); } - private static final Consumer> PRINTLN = t -> { + private static final Consumer PRINTLN = t -> { 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) MainFrame.INSTANCE.dispose(); else diff --git a/HMCLAPI/src/main/java/org/jackhuang/hmcl/api/IProcess.java b/HMCLAPI/src/main/java/org/jackhuang/hmcl/api/IProcess.java index a4dec1ca0..93f073733 100644 --- a/HMCLAPI/src/main/java/org/jackhuang/hmcl/api/IProcess.java +++ b/HMCLAPI/src/main/java/org/jackhuang/hmcl/api/IProcess.java @@ -39,6 +39,4 @@ public interface IProcess { void stop(); - void waitForCommandLineCompletion(); - } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/JavaProcess.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/JavaProcess.java index 2c6f25b37..fcd5a5546 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/JavaProcess.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/JavaProcess.java @@ -17,12 +17,11 @@ */ package org.jackhuang.hmcl.util.sys; +import java.util.ArrayList; import org.jackhuang.hmcl.api.IProcess; import java.util.Arrays; +import java.util.Collections; 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 { - private final CountDownLatch latch = new CountDownLatch(2); private final List commands; private final Process process; - private final Vector stdOutLines = new Vector<>(); + private final List stdOutLines = Collections.synchronizedList(new ArrayList<>()); public JavaProcess(List commands, Process process) { this.commands = commands; @@ -75,19 +73,6 @@ public class JavaProcess implements IProcess { return false; } - CountDownLatch getLatch() { - return latch; - } - - @Override - public void waitForCommandLineCompletion() { - try { - latch.await(); - } catch (InterruptedException ignore) { - HMCLog.warn("Thread has been interrupted.", ignore); - } - } - @Override public int getExitCode() { try { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/PrintlnEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/PrintlnEvent.java new file mode 100644 index 000000000..1a609b221 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/PrintlnEvent.java @@ -0,0 +1,45 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2013 huangyuhui + * + * 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; + } + +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessMonitor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessMonitor.java index 6bb61cbfc..27dcbcc9d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessMonitor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessMonitor.java @@ -19,6 +19,7 @@ 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; @@ -39,6 +40,7 @@ public class ProcessMonitor { public static final HashSet MONITORS = new HashSet<>(); + private final CountDownLatch latch = new CountDownLatch(2); ProcessThread inputThread, errorThread; private final IProcess p; @@ -64,13 +66,14 @@ public class ProcessMonitor { public void setTag(Object tag) { this.tag = tag; } - - public void registerPrintlnEvent(Consumer> c) { + + public void registerPrintlnEvent(Consumer c) { inputThread.printlnEvent.register(c); errorThread.printlnEvent.register(c); } public void start() { + hasFired = false; MONITORS.add(this); HMCLApi.EVENT_BUS.fireChannel(new JavaProcessStartingEvent(this, p)); inputThread.start(); @@ -78,21 +81,34 @@ public class ProcessMonitor { } private void threadStopped(SimpleEvent 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))) - 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(), 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))) - 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) { MONITORS.remove(this); errorThread.interrupt(); @@ -106,4 +122,12 @@ public class ProcessMonitor { monitor.errorThread.interrupt(); } } + + public void waitForCommandLineCompletion() { + try { + latch.await(); + } catch (InterruptedException ignore) { + HMCLog.warn("Thread has been interrupted.", ignore); + } + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessThread.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessThread.java index e25e492d7..ae9cb1608 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessThread.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/sys/ProcessThread.java @@ -21,8 +21,8 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.List; -import java.util.Vector; import org.jackhuang.hmcl.api.HMCLog; import org.jackhuang.hmcl.api.event.EventHandler; import org.jackhuang.hmcl.api.event.SimpleEvent; @@ -36,24 +36,27 @@ import org.jackhuang.hmcl.api.IProcess; */ public class ProcessThread extends Thread { - Vector lines = new Vector<>(); + List lines = new ArrayList<>(); ProcessMonitor monitor; + IProcess p; boolean readError; - public final EventHandler> printlnEvent = new EventHandler<>(); + public final EventHandler printlnEvent = new EventHandler<>(); public final EventHandler> stopEvent = new EventHandler<>(); public ProcessThread(ProcessMonitor monitor, boolean readError) { this.monitor = monitor; this.readError = readError; + p = monitor.getProcess(); setDaemon(readError); } public IProcess getProcess() { - return monitor.getProcess(); + return p; } /** * Only get stdout or stderr output according to readError(). + * Invoke this method only if the process thread has stopped. */ public List getLines() { return lines; @@ -63,32 +66,28 @@ public class ProcessThread extends Thread { public void run() { setName("ProcessMonitor"); BufferedReader br = null; - IProcess p = monitor.getProcess(); try { InputStream in = readError ? p.getRawProcess().getErrorStream() : p.getRawProcess().getInputStream(); br = new BufferedReader(new InputStreamReader(in, Charsets.toCharset())); String line; while (p.isRunning()) - while ((line = br.readLine()) != null) { - printlnEvent.fire(new SimpleEvent<>(monitor, line)); - System.out.println("MC: " + line); - lines.add(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); - } + while ((line = br.readLine()) != null) + println(line); + while ((line = br.readLine()) != null) + println(line); } catch (IOException e) { HMCLog.err("An error occured when reading process stdout/stderr.", e); } finally { IOUtils.closeQuietly(br); } - if (p instanceof JavaProcess) - ((JavaProcess) p).getLatch().countDown(); 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); + } } diff --git a/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/button/BEButtonUI.java b/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/button/BEButtonUI.java index c4385b0fa..3a475ed90 100644 --- a/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/button/BEButtonUI.java +++ b/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/button/BEButtonUI.java @@ -267,7 +267,7 @@ public class BEButtonUI extends BasicButtonUI implements Skin { state = State.ROLLOVER; else if (b instanceof JButton && ((JButton) b).isDefaultButton()) - state = State.DEFAULTED; + state = State.DEFAULT; break; default: state = State.NORMAL; diff --git a/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/AnimationController.java b/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/AnimationController.java index b2df78b01..cc1730300 100644 --- a/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/AnimationController.java +++ b/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/AnimationController.java @@ -98,7 +98,7 @@ public class AnimationController implements ActionListener, PropertyChangeListen State oldState = controller.getState(c, part); if (oldState != newState) { controller.putState(c, part, newState); - if (newState == State.DEFAULTED) + if (newState == State.DEFAULT) // it seems for DEFAULTED button state Vista does animation from // HOT oldState = State.ROLLOVER; @@ -182,7 +182,7 @@ public class AnimationController implements ActionListener, PropertyChangeListen State endState, long millis) { boolean isForwardAndReverse = false; - if (endState == State.DEFAULTED) + if (endState == State.DEFAULT) isForwardAndReverse = true; Map map = animationStateMap.get(component); if (millis <= 0) { diff --git a/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/TMSchema.java b/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/TMSchema.java index 345ac6a96..49ff758a9 100644 --- a/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/TMSchema.java +++ b/HMCLaF/src/main/java/org/jackhuang/hmcl/laf/utils/TMSchema.java @@ -231,7 +231,7 @@ public class TMSchema { BULLETNORMAL, BULLETDISABLED, CLOSED, - DEFAULTED, + DEFAULT, DISABLED, DISABLEDHOT, DISABLEDPUSHED, @@ -304,7 +304,7 @@ public class TMSchema { }); stateMap.put(Part.BP_PUSHBUTTON, - new State[] { NORMAL, ROLLOVER, PRESSED, DISABLED, DEFAULTED }); + new State[] { NORMAL, ROLLOVER, PRESSED, DISABLED, DEFAULT }); stateMap.put(Part.BP_RADIOBUTTON, new State[] {