diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java index c92d7bee0..0378279a2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -65,7 +65,6 @@ public class DefaultDependencyManager extends AbstractDependencyManager { public Task checkGameCompletionAsync(Version version) { return new ParallelTask( new GameAssetDownloadTask(this, version), - new GameLoggingDownloadTask(this, version), new GameLibrariesTask(this, version) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java index 0e0d53ed5..08559af84 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java @@ -55,7 +55,6 @@ public class DefaultGameBuilder extends GameBuilder { variables.set("version", version); Task result = new ParallelTask( new GameAssetDownloadTask(dependencyManager, version), - new GameLoggingDownloadTask(dependencyManager, version), downloadGameAsync(gameVersion, version), new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries. ).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version)); // using [with] because download failure here are tolerant. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLoggingDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLoggingDownloadTask.java deleted file mode 100644 index 895191020..000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLoggingDownloadTask.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Hello Minecraft! Launcher. - * Copyright (C) 2018 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.download.game; - -import org.jackhuang.hmcl.download.DependencyManager; -import org.jackhuang.hmcl.game.DownloadType; -import org.jackhuang.hmcl.game.LoggingInfo; -import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.task.FileDownloadTask; -import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.NetworkUtils; - -import java.io.File; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -/** - * This task is to download log4j configuration file provided in minecraft.json. - * - * @author huangyuhui - */ -public final class GameLoggingDownloadTask extends Task { - - private final DependencyManager dependencyManager; - private final Version version; - private final List dependencies = new LinkedList<>(); - - /** - * Constructor. - * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} - * @param version the resolved version - */ - public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) { - this.dependencyManager = dependencyManager; - this.version = version; - setSignificance(TaskSignificance.MODERATE); - } - - @Override - public Collection getDependencies() { - return dependencies; - } - - @Override - public void execute() { - if (version.getLogging() == null || !version.getLogging().containsKey(DownloadType.CLIENT)) - return; - - LoggingInfo logging = version.getLogging().get(DownloadType.CLIENT); - File file = dependencyManager.getGameRepository().getLoggingObject(version.getId(), version.getAssetIndex().getId(), logging); - if (!file.exists()) - dependencies.add(new FileDownloadTask(NetworkUtils.toURL(logging.getFile().getUrl()), file)); - } - -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index c5d009e27..1448dc485 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -47,7 +47,7 @@ public class DefaultLauncher extends Launcher { super(repository, versionId, authInfo, options, listener, daemon); } - private CommandBuilder generateCommandLine(File nativeFolder, boolean enableLoggingInfo) throws IOException { + private CommandBuilder generateCommandLine(File nativeFolder) throws IOException { CommandBuilder res = new CommandBuilder(); // Executable @@ -70,16 +70,6 @@ public class DefaultLauncher extends Launcher { res.add("-Xdock:icon=" + repository.getAssetObject(version.getId(), version.getAssetIndex().getId(), "icons/minecraft.icns").getAbsolutePath()); } - Map logging = version.getLogging(); - if (logging != null && enableLoggingInfo) { - LoggingInfo loggingInfo = logging.get(DownloadType.CLIENT); - if (loggingInfo != null) { - File loggingFile = repository.getLoggingObject(version.getId(), version.getAssetIndex().getId(), loggingInfo); - if (loggingFile.exists()) - res.add(loggingInfo.getArgument().replace("${path}", loggingFile.getAbsolutePath())); - } - } - if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS) res.add("-Duser.home=" + options.getGameDir().getParent()); @@ -252,7 +242,7 @@ public class DefaultLauncher extends Launcher { File nativeFolder = Files.createTempDirectory("minecraft").toFile(); // To guarantee that when failed to generate launch command line, we will not call pre-launch command - List rawCommandLine = generateCommandLine(nativeFolder, isEnablingLoggingInfo()).asList(); + List rawCommandLine = generateCommandLine(nativeFolder).asList(); decompressNatives(nativeFolder); @@ -304,50 +294,17 @@ public class DefaultLauncher extends Launcher { writer.write(options.getPreLaunchCommand()); writer.newLine(); } - writer.write(generateCommandLine(nativeFolder, false).toString()); + writer.write(generateCommandLine(nativeFolder).toString()); } if (!scriptFile.setExecutable(true)) throw new PermissionException(); } - protected boolean isEnablingLoggingInfo() { - return version.getLogging() != null && version.getLogging().containsKey(DownloadType.CLIENT) - && !"net.minecraft.launchwrapper.Launch".equals(version.getMainClass()); - } - private void startMonitors(ManagedProcess managedProcess, ProcessListener processListener) { startMonitors(managedProcess, processListener, true); } private void startMonitors(ManagedProcess managedProcess, ProcessListener processListener, boolean isDaemon) { - if (isEnablingLoggingInfo()) - startMonitorsWithLoggingInfo(managedProcess, processListener, isDaemon); - else - startMonitorsWithoutLoggingInfo(managedProcess, processListener, isDaemon); - } - - private void startMonitorsWithLoggingInfo(ManagedProcess managedProcess, ProcessListener processListener, boolean isDaemon) { - processListener.setProcess(managedProcess); - Log4jHandler logHandler = new Log4jHandler((line, level) -> { - processListener.onLog(line, level); - managedProcess.addLine(line); - }); - logHandler.start(); - managedProcess.addRelatedThread(logHandler); - Thread stdout = Lang.thread(new StreamPump(managedProcess.getProcess().getInputStream(), logHandler::newLine), "stdout-pump", isDaemon); - managedProcess.addRelatedThread(stdout); - Thread stderr = Lang.thread(new StreamPump(managedProcess.getProcess().getErrorStream(), it -> { - processListener.onLog(it + OperatingSystem.LINE_SEPARATOR, Log4jLevel.ERROR); - managedProcess.addLine(it); - }), "stderr-pump", isDaemon); - managedProcess.addRelatedThread(stderr); - managedProcess.addRelatedThread(Lang.thread(new ExitWaiter(managedProcess, Arrays.asList(stdout, stderr), (exitCode, exitType) -> { - logHandler.onStopped(); - processListener.onExit(exitCode, exitType); - }), "exit-waiter", isDaemon)); - } - - private void startMonitorsWithoutLoggingInfo(ManagedProcess managedProcess, ProcessListener processListener, boolean isDaemon) { processListener.setProcess(managedProcess); Thread stdout = Lang.thread(new StreamPump(managedProcess.getProcess().getInputStream(), it -> { processListener.onLog(it + OperatingSystem.LINE_SEPARATOR, Optional.ofNullable(Log4jLevel.guessLevel(it)).orElse(Log4jLevel.INFO)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Log4jHandler.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Log4jHandler.java deleted file mode 100644 index 3055552d5..000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Log4jHandler.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Hello Minecraft! Launcher. - * Copyright (C) 2018 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.launch; - -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.util.*; -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; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.BiConsumer; -import java.util.logging.Level; - -/** - * This class is to parse log4j classic XML layout logging, - * since only vanilla Minecraft will enable this layout. - * - * Also supports plain logs. - * - * @author huangyuhui - */ -final class Log4jHandler extends Thread { - - private final XMLReader reader; - private final BiConsumer callback; - private final PipedOutputStream outputStream = new PipedOutputStream(); - private final PipedInputStream inputStream = Lang.invoke(() -> new PipedInputStream(outputStream)); - private final AtomicBoolean interrupted = new AtomicBoolean(false); - private final List logs = new LinkedList<>(); - private boolean broken = false; - - public Log4jHandler(BiConsumer callback) { - this.callback = callback; - newLine(""); - - reader = Lang.invoke((ExceptionalSupplier) XMLReaderFactory::createXMLReader); - reader.setContentHandler(new Log4jHandlerImpl()); - } - - @Override - public void run() { - setName("log4j-handler"); - - try { - InputSource inputSource = new InputSource(inputStream); - reader.parse(inputSource); - } catch (InterruptedIOException e) { - // Game has been interrupted. - interrupted.set(true); - } catch (SAXException | IOException e) { - Logging.LOG.log(Level.WARNING, "An error occurred when reading console lines", e); - } - } - - public void onStopped() { - if (interrupted.get()) - return; - - Lang.invoke(() -> Schedulers.newThread().schedule(() -> { - if (!interrupted.get()) { - newLine("").get(); - outputStream.close(); - join(); - } - }).get()); - } - - public Future newLine(String log) { - return Schedulers.computation().schedule(() -> { - try { - // Prevent from namespace parsing - String line = log.replace("log4j:", "log4j_"); - logs.add(line); - if (broken) - System.out.println(line); - - byte[] bytes = (line + OperatingSystem.LINE_SEPARATOR).getBytes(Constants.SYSTEM_CHARSET); - outputStream.write(bytes); - outputStream.flush(); - } catch (IOException e) { - // Ignoring IOException, including read end dead. - if (!broken) { - Logging.LOG.log(Level.WARNING, "An error occurred when writing console lines", e); - logs.forEach(System.out::println); - broken = true; - } else { - // Output plain XML to user - callback.accept(log, Log4jLevel.INFO); - } - } - }); - } - - private class Log4jHandlerImpl extends DefaultHandler { - - private final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); - - private String date = "", thread = "", logger = ""; - private StringBuilder message; - private Log4jLevel level; - private boolean readingMessage = false; - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) { - switch (localName) { - case "log4j_Event": - message = new StringBuilder(); - Date d = new Date(Long.parseLong(attributes.getValue("timestamp"))); - date = format.format(d); - try { - level = Log4jLevel.valueOf(attributes.getValue("level")); - } catch (IllegalArgumentException e) { - level = Log4jLevel.INFO; - } - thread = attributes.getValue("thread"); - logger = attributes.getValue("logger"); - if ("STDERR".equals(logger)) - level = Log4jLevel.ERROR; - break; - case "log4j_Message": - readingMessage = true; - break; - case "log4j_Throwable": - break; - } - } - - @Override - public void endElement(String uri, String localName, String qName) { - switch (localName) { - case "log4j_Event": - callback.accept("[" + date + "] [" + thread + "/" + level.name() + "] [" + logger + "] " + message.toString(), level); - break; - case "log4j_Message": - readingMessage = false; - break; - } - } - - @Override - public void characters(char[] ch, int start, int length) { - String line = new String(ch, start, length); - if (line.trim().isEmpty()) - return; - if (readingMessage) - message.append(line).append(OperatingSystem.LINE_SEPARATOR); - else - callback.accept(line, Optional.ofNullable(Log4jLevel.guessLevel(line)).orElse(Log4jLevel.INFO)); - } - } -}