add: scroll to game crash report automatically
This commit is contained in:
@@ -566,28 +566,27 @@ public final class LauncherHelper {
|
|||||||
|
|
||||||
if (exitType != ExitType.NORMAL) {
|
if (exitType != ExitType.NORMAL) {
|
||||||
repository.markVersionLaunchedAbnormally(version);
|
repository.markVersionLaunchedAbnormally(version);
|
||||||
}
|
|
||||||
|
|
||||||
if (exitType != ExitType.NORMAL && logWindow == null)
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
logWindow = new LogWindow();
|
if (logWindow == null) {
|
||||||
|
logWindow = new LogWindow();
|
||||||
|
|
||||||
switch (exitType) {
|
switch (exitType) {
|
||||||
case JVM_ERROR:
|
case JVM_ERROR:
|
||||||
logWindow.setTitle(i18n("launch.failed.cannot_create_jvm"));
|
logWindow.setTitle(i18n("launch.failed.cannot_create_jvm"));
|
||||||
break;
|
break;
|
||||||
case APPLICATION_ERROR:
|
case APPLICATION_ERROR:
|
||||||
logWindow.setTitle(i18n("launch.failed.exited_abnormally"));
|
logWindow.setTitle(i18n("launch.failed.exited_abnormally"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
logWindow.show();
|
|
||||||
logWindow.onDone.register(() -> {
|
|
||||||
logWindow.logLine("Command: " + new CommandBuilder().addAll(process.getCommands()).toString(), Log4jLevel.INFO);
|
logWindow.logLine("Command: " + new CommandBuilder().addAll(process.getCommands()).toString(), Log4jLevel.INFO);
|
||||||
for (Map.Entry<String, Log4jLevel> entry : logs)
|
for (Map.Entry<String, Log4jLevel> entry : logs)
|
||||||
logWindow.logLine(entry.getKey(), entry.getValue());
|
logWindow.logLine(entry.getKey(), entry.getValue());
|
||||||
});
|
}
|
||||||
|
|
||||||
|
logWindow.showGameCrashReport();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
checkExit();
|
checkExit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.jfoenix.controls.JFXButton;
|
|||||||
import com.jfoenix.controls.JFXCheckBox;
|
import com.jfoenix.controls.JFXCheckBox;
|
||||||
import com.jfoenix.controls.JFXComboBox;
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
import com.jfoenix.controls.JFXListView;
|
import com.jfoenix.controls.JFXListView;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
@@ -33,8 +34,6 @@ import javafx.scene.control.*;
|
|||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.jackhuang.hmcl.event.Event;
|
|
||||||
import org.jackhuang.hmcl.event.EventManager;
|
|
||||||
import org.jackhuang.hmcl.game.LauncherHelper;
|
import org.jackhuang.hmcl.game.LauncherHelper;
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
import org.jackhuang.hmcl.util.Log4jLevel;
|
||||||
|
|
||||||
@@ -58,6 +57,7 @@ import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
|||||||
import static org.jackhuang.hmcl.ui.FXUtils.newImage;
|
import static org.jackhuang.hmcl.ui.FXUtils.newImage;
|
||||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
|
import static org.jackhuang.hmcl.util.StringUtils.parseEscapeSequence;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,7 +66,6 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
|||||||
*/
|
*/
|
||||||
public final class LogWindow extends Stage {
|
public final class LogWindow extends Stage {
|
||||||
|
|
||||||
public final EventManager<Event> onDone = new EventManager<>();
|
|
||||||
private final ArrayDeque<Log> logs = new ArrayDeque<>();
|
private final ArrayDeque<Log> logs = new ArrayDeque<>();
|
||||||
private final Map<Log4jLevel, SimpleIntegerProperty> levelCountMap = new EnumMap<Log4jLevel, SimpleIntegerProperty>(Log4jLevel.class) {
|
private final Map<Log4jLevel, SimpleIntegerProperty> levelCountMap = new EnumMap<Log4jLevel, SimpleIntegerProperty>(Log4jLevel.class) {
|
||||||
{
|
{
|
||||||
@@ -76,30 +75,46 @@ public final class LogWindow extends Stage {
|
|||||||
private final Map<Log4jLevel, SimpleBooleanProperty> levelShownMap = new EnumMap<Log4jLevel, SimpleBooleanProperty>(Log4jLevel.class) {
|
private final Map<Log4jLevel, SimpleBooleanProperty> levelShownMap = new EnumMap<Log4jLevel, SimpleBooleanProperty>(Log4jLevel.class) {
|
||||||
{
|
{
|
||||||
for (Log4jLevel level : Log4jLevel.values()) {
|
for (Log4jLevel level : Log4jLevel.values()) {
|
||||||
SimpleBooleanProperty property = new SimpleBooleanProperty();
|
SimpleBooleanProperty property = new SimpleBooleanProperty(true);
|
||||||
put(level, property);
|
put(level, property);
|
||||||
property.addListener((a, b, newValue) -> shakeLogs());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final LogWindowImpl impl = new LogWindowImpl();
|
private final LogWindowImpl impl = new LogWindowImpl();
|
||||||
private final WeakChangeListener<Number> logLinesListener = FXUtils.onWeakChange(config().logLinesProperty(), logLines -> checkLogCount());
|
private final WeakChangeListener<Number> logLinesListener = FXUtils.onWeakChange(config().logLinesProperty(), logLines -> checkLogCount());
|
||||||
|
|
||||||
|
private boolean stopCheckLogCount = false;
|
||||||
|
|
||||||
public LogWindow() {
|
public LogWindow() {
|
||||||
setScene(new Scene(impl, 800, 480));
|
setScene(new Scene(impl, 800, 480));
|
||||||
getScene().getStylesheets().addAll(config().getTheme().getStylesheets());
|
getScene().getStylesheets().addAll(config().getTheme().getStylesheets());
|
||||||
setTitle(i18n("logwindow.title"));
|
setTitle(i18n("logwindow.title"));
|
||||||
getIcons().add(newImage("/assets/img/icon.png"));
|
getIcons().add(newImage("/assets/img/icon.png"));
|
||||||
|
|
||||||
|
levelShownMap.values().forEach(property -> property.addListener((a, b, newValue) -> shakeLogs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logLine(String line, Log4jLevel level) {
|
public void logLine(String line, Log4jLevel level) {
|
||||||
Log log = new Log(line, level);
|
Log log = new Log(parseEscapeSequence(line), level);
|
||||||
logs.add(log);
|
logs.add(log);
|
||||||
if (levelShownMap.get(level).get())
|
if (levelShownMap.get(level).get())
|
||||||
impl.listView.getItems().add(log);
|
impl.listView.getItems().add(log);
|
||||||
|
|
||||||
levelCountMap.get(level).setValue(levelCountMap.get(level).getValue() + 1);
|
levelCountMap.get(level).setValue(levelCountMap.get(level).getValue() + 1);
|
||||||
checkLogCount();
|
if (!stopCheckLogCount) checkLogCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showGameCrashReport() {
|
||||||
|
stopCheckLogCount = true;
|
||||||
|
for (Log log : impl.listView.getItems()) {
|
||||||
|
if (log.log.contains("Minecraft Crash Report")) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
impl.listView.scrollTo(log);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shakeLogs() {
|
private void shakeLogs() {
|
||||||
@@ -131,7 +146,7 @@ public final class LogWindow extends Stage {
|
|||||||
private ListView<Log> listView = new JFXListView<>();
|
private ListView<Log> listView = new JFXListView<>();
|
||||||
private BooleanProperty autoScroll = new SimpleBooleanProperty();
|
private BooleanProperty autoScroll = new SimpleBooleanProperty();
|
||||||
private List<StringProperty> buttonText = IntStream.range(0, 5).mapToObj(x -> new SimpleStringProperty()).collect(Collectors.toList());
|
private List<StringProperty> buttonText = IntStream.range(0, 5).mapToObj(x -> new SimpleStringProperty()).collect(Collectors.toList());
|
||||||
private List<BooleanProperty> showLevel = IntStream.range(0, 5).mapToObj(x -> new SimpleBooleanProperty()).collect(Collectors.toList());
|
private List<BooleanProperty> showLevel = IntStream.range(0, 5).mapToObj(x -> new SimpleBooleanProperty(true)).collect(Collectors.toList());
|
||||||
private JFXComboBox<String> cboLines = new JFXComboBox<>();
|
private JFXComboBox<String> cboLines = new JFXComboBox<>();
|
||||||
|
|
||||||
LogWindowImpl() {
|
LogWindowImpl() {
|
||||||
@@ -193,6 +208,7 @@ public final class LogWindow extends Stage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class LogWindowSkin extends SkinBase<LogWindowImpl> {
|
private static class LogWindowSkin extends SkinBase<LogWindowImpl> {
|
||||||
|
private static PseudoClass EMPTY = PseudoClass.getPseudoClass("empty");
|
||||||
private static PseudoClass FATAL = PseudoClass.getPseudoClass("fatal");
|
private static PseudoClass FATAL = PseudoClass.getPseudoClass("fatal");
|
||||||
private static PseudoClass ERROR = PseudoClass.getPseudoClass("error");
|
private static PseudoClass ERROR = PseudoClass.getPseudoClass("error");
|
||||||
private static PseudoClass WARN = PseudoClass.getPseudoClass("warn");
|
private static PseudoClass WARN = PseudoClass.getPseudoClass("warn");
|
||||||
@@ -271,22 +287,17 @@ public final class LogWindow extends Stage {
|
|||||||
@Override
|
@Override
|
||||||
protected void updateItem(Log item, boolean empty) {
|
protected void updateItem(Log item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
|
pseudoClassStateChanged(EMPTY, empty);
|
||||||
|
pseudoClassStateChanged(FATAL, !empty && item.level == Log4jLevel.FATAL);
|
||||||
|
pseudoClassStateChanged(ERROR, !empty && item.level == Log4jLevel.ERROR);
|
||||||
|
pseudoClassStateChanged(WARN, !empty && item.level == Log4jLevel.WARN);
|
||||||
|
pseudoClassStateChanged(INFO, !empty && item.level == Log4jLevel.INFO);
|
||||||
|
pseudoClassStateChanged(DEBUG, !empty && item.level == Log4jLevel.DEBUG);
|
||||||
|
pseudoClassStateChanged(TRACE, !empty && item.level == Log4jLevel.TRACE);
|
||||||
if (empty) {
|
if (empty) {
|
||||||
setText(null);
|
setText(null);
|
||||||
pseudoClassStateChanged(FATAL, false);
|
|
||||||
pseudoClassStateChanged(ERROR, false);
|
|
||||||
pseudoClassStateChanged(WARN, false);
|
|
||||||
pseudoClassStateChanged(INFO, false);
|
|
||||||
pseudoClassStateChanged(DEBUG, false);
|
|
||||||
pseudoClassStateChanged(TRACE, false);
|
|
||||||
} else {
|
} else {
|
||||||
setText(item.log);
|
setText(item.log);
|
||||||
pseudoClassStateChanged(FATAL, item.level == Log4jLevel.FATAL);
|
|
||||||
pseudoClassStateChanged(ERROR, item.level == Log4jLevel.ERROR);
|
|
||||||
pseudoClassStateChanged(WARN, item.level == Log4jLevel.WARN);
|
|
||||||
pseudoClassStateChanged(INFO, item.level == Log4jLevel.INFO);
|
|
||||||
pseudoClassStateChanged(DEBUG, item.level == Log4jLevel.DEBUG);
|
|
||||||
pseudoClassStateChanged(TRACE, item.level == Log4jLevel.TRACE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -936,6 +936,10 @@
|
|||||||
-fx-border-color: #dddddd;
|
-fx-border-color: #dddddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.log:empty {
|
||||||
|
-fx-border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.log:fatal {
|
.log:fatal {
|
||||||
-fx-background-color: #F7A699;
|
-fx-background-color: #F7A699;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,4 +217,22 @@ public final class StringUtils {
|
|||||||
public static String parseColorEscapes(String original) {
|
public static String parseColorEscapes(String original) {
|
||||||
return original.replaceAll("\u00A7\\d", "");
|
return original.replaceAll("\u00A7\\d", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String parseEscapeSequence(String str) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
boolean inEscape = false;
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
char ch = str.charAt(i);
|
||||||
|
if (ch == '\033') {
|
||||||
|
inEscape = true;
|
||||||
|
}
|
||||||
|
if (!inEscape) {
|
||||||
|
builder.append(ch);
|
||||||
|
}
|
||||||
|
if (inEscape && ch == 'm') {
|
||||||
|
inEscape = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user