Compare commits

...
10 Commits
Author SHA1 Message Date
huangyuhui 7a86881926 Fixed downloading libraries that are forbidden in specific OS. Closes #163 2017-08-26 17:45:40 +08:00
huangyuhui b255fd4601 CDATA 2017-08-26 09:04:10 +08:00
huangyuhui 562dd8d916 Fixed #190 2017-08-26 08:35:27 +08:00
huangyuhui 97d820446a Fixed Read end dead. Closes #186 2017-08-24 10:03:19 +08:00
huangyuhui 826d382ad5 Changed log colors. Closes #187 2017-08-24 09:37:33 +08:00
huangyuhui 59d7ac3c82 ProcessThread.interrupt 2017-08-20 12:07:13 +08:00
huanghongxun 566aed4ed6 Merge pull request #185 from ron-gh/master
Fix "java.io.IOException: Read end dead"
2017-08-18 21:32:53 +08:00
ron 1b90e94615 Fix "java.io.IOException: Read end dead" when different Threads are writing to a single PipedOutputStream 2017-08-18 14:54:55 +02:00
huanghongxun 22ede84c3d Merge pull request #183 from jp8900308/master
Set a custom User-Agent, fixes optifine.net downloads
2017-08-15 23:43:05 +08:00
Jean-Paul Bergeron 0c4da069b7 Set a custom User-Agent, fixes optifine.net downloads 2017-08-15 07:23:03 -07:00
12 changed files with 112 additions and 53 deletions

No files matched your search

@@ -52,8 +52,9 @@ import org.xml.sax.XMLReader;
* @author huangyuhui
*/
public class LaunchingUIDaemon {
XMLReader reader;
Log4jHandler logHandler;
public LaunchingUIDaemon() {
HMCLApi.EVENT_BUS.channel(LaunchingStateChangedEvent.class).register(LAUNCHING_STATE_CHANGED);
@@ -76,12 +77,12 @@ public class LaunchingUIDaemon {
PipedOutputStream os = new PipedOutputStream();
monitor.setTag(obj);
try {
Log4jHandler handler = new Log4jHandler(monitor, os);
handler.addForbiddenToken(obj.getLoginResult().getAccessToken(), "<access token>");
handler.addForbiddenToken(obj.getLoginResult().getSession(), "<session>");
handler.addForbiddenToken(obj.getLoginResult().getUserId(), "<uuid>");
handler.addForbiddenToken("Setting user: " + obj.getLoginResult().getUserName(), "<player>");
handler.start();
logHandler = new Log4jHandler(monitor, os);
logHandler.addForbiddenToken(obj.getLoginResult().getAccessToken(), "<access token>");
logHandler.addForbiddenToken(obj.getLoginResult().getSession(), "<session>");
logHandler.addForbiddenToken(obj.getLoginResult().getUserId(), "<uuid>");
logHandler.addForbiddenToken("Setting user: " + obj.getLoginResult().getUserName(), "<player>");
logHandler.start();
} catch(Exception e) {
HMCLog.err("", e);
}
@@ -128,7 +129,7 @@ public class LaunchingUIDaemon {
noExitThisTime = true;
});
}
static LauncherVisibility unpackProcessMonitor(Object obj) {
GameLauncher launcher = ((GameLauncher) ((ProcessMonitor) obj).getTag());
HMCLGameLauncher.GameLauncherTag tag = (HMCLGameLauncher.GameLauncherTag) launcher.getTag();
@@ -161,9 +162,9 @@ 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;
@@ -174,25 +175,27 @@ public class LaunchingUIDaemon {
@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);
String log = t.getLine();
if (!t.isError() && logHandler != null) {
if (!log.trim().startsWith("<")) { // without logging configuration.
log = "<![CDATA[" + log.replace("]]>", "") + "]]>";
}
logHandler.newLogLine(log + C.LINE_SEPARATOR);
}
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));
}
SwingUtilities.invokeLater(() -> {
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 -> {
@@ -231,13 +234,10 @@ public class LaunchingUIDaemon {
private void checkExit(LauncherVisibility v) {
if (v == LauncherVisibility.HIDE_AND_REOPEN) {
HMCLog.log("Launcher will not exit now.");
MainFrame.INSTANCE.setVisible(true);
} else if (v != LauncherVisibility.KEEP && !LogWindow.INSTANCE.isVisible() && !noExitThisTime) {
HMCLog.log("Launcher will exit now.");
System.exit(0);
} else {
HMCLog.log("Launcher will not exit now.");
noExitThisTime = false;
}
}
@@ -253,5 +253,5 @@ public class LaunchingUIDaemon {
}
MainFrame.INSTANCE.closeMessage();
};
}
@@ -39,7 +39,7 @@ public class HMCLGameProvider extends MinecraftVersionManager<HMCLMinecraftServi
public File getLibraryFile(MinecraftVersion version, IMinecraftLibrary lib) {
VersionSetting vs = service.getProfile().getVersionSetting(version.id);
File self = super.getLibraryFile(version, lib);
if (self.exists() || (vs != null && service.getProfile().isNoCommon()))
if (self == null || self.exists() || (vs != null && service.getProfile().isNoCommon()))
return self;
else
return lib.getFilePath(new File(Settings.getInstance().getCommonpath()));
@@ -18,12 +18,15 @@
package org.jackhuang.hmcl.util;
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.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.xml.parsers.ParserConfigurationException;
import org.jackhuang.hmcl.api.HMCLApi;
import org.jackhuang.hmcl.api.event.process.JavaProcessStoppedEvent;
@@ -49,6 +52,8 @@ public class Log4jHandler extends Thread implements Consumer<JavaProcessStoppedE
PipedInputStream inputStream;
PipedOutputStream outputStream;
List<Pair<String, String>> forbiddenTokens = new LinkedList<>();
AtomicBoolean interrupted = new AtomicBoolean(false);
ExecutorService executorService = Executors.newSingleThreadExecutor();
public Log4jHandler(ProcessMonitor monitor, PipedOutputStream outputStream) throws ParserConfigurationException, IOException, SAXException {
reader = XMLReaderFactory.createXMLReader();
@@ -66,25 +71,59 @@ public class Log4jHandler extends Thread implements Consumer<JavaProcessStoppedE
@Override
public void run() {
try {
outputStream.write("<output>".getBytes());
outputStream.flush();
setName("log4j-handler");
newLogLine("<output>");
reader.setContentHandler(new Log4jHandlerImpl());
reader.parse(new InputSource(inputStream));
} catch (InterruptedIOException e) {
interrupted.set(true);
} catch (SAXException | IOException e) {
throw new Error(e);
} finally {
executorService.shutdown();
}
}
@Override
public void accept(JavaProcessStoppedEvent t) {
if (!interrupted.get())
return;
if (t.getSource() == monitor) {
executorService.submit(() -> {
if (!interrupted.get()) {
newLogLine("</output>").get();
outputStream.close();
join();
}
return null;
});
}
}
/**
* Always use this instance method to write to the log,
* to make sure only this Thread writes to the PipedOutputStream.
*
* It will also flush the buffer, directly after write.
*
* This prevents java.io.IOException: Read end dead
* See also https://techtavern.wordpress.com/2008/07/16/whats-this-ioexception-write-end-dead/
*
* @param content The content to be written to the log
*/
public Future newLogLine(String content) {
return executorService.submit(() -> {
try {
outputStream.write("</output>".getBytes());
outputStream.close();
outputStream.write(content
.replace("log4j:Event", "log4j_Event")
.replace("log4j:Message", "log4j_Message")
.replace("log4j:Throwable", "log4j_Throwable")
.getBytes());
outputStream.flush();
} catch (IOException ignore) { // won't happen
throw new Error(ignore);
}
}
});
}
class Log4jHandlerImpl extends DefaultHandler {
@@ -57,7 +57,7 @@ public class MinecraftDownloadService extends IMinecraftDownloadService {
for (IMinecraftLibrary l : v.getLibraries())
if (l != null && l.allow()) {
File ff = service.version().getLibraryFile(mv, l);
if (!ff.exists() && l.getDownloadURL(service.getDownloadType().name()) != null) {
if (ff != null && !ff.exists() && l.getDownloadURL(service.getDownloadType().name()) != null) {
String libURL = l.getDownloadURL(service.getDownloadType().name());
if (libURL != null)
downloadLibraries.add(new DownloadLibraryJob(l, libURL, ff));
@@ -41,7 +41,11 @@ public abstract class AbstractMinecraftLibrary implements IMinecraftLibrary {
@Override
public String getDownloadURL(String downloadSource) {
return getDownloadInfo().getUrl(DownloadType.valueOf(downloadSource));
LibraryDownloadInfo info = getDownloadInfo();
if (info == null)
return null;
else
return info.getUrl(DownloadType.valueOf(downloadSource));
}
@Override
@@ -123,6 +123,8 @@ public class MinecraftLibrary extends AbstractMinecraftLibrary {
if (natives != null) {
if (downloads.classifiers == null)
downloads.classifiers = new HashMap<>();
if (StrUtils.isBlank(getNative()))
return null;
if (!downloads.classifiers.containsKey(getNative()))
downloads.classifiers.put(getNative(), info = new LibraryDownloadInfo());
else info = downloads.classifiers.get(getNative());
@@ -247,11 +247,13 @@ public class MinecraftVersionManager<T extends IMinecraftService> extends IMinec
throw new GameException("Wrong format: minecraft.json");
ArrayList<File> unzippings = new ArrayList<>();
ArrayList<Extract> extractRules = new ArrayList<>();
for (IMinecraftLibrary l : v.libraries)
if (l.isNative() && v.isAllowedToUnpackNatives()) {
unzippings.add(getLibraryFile(v, l));
for (IMinecraftLibrary l : v.libraries) {
final File f = getLibraryFile(v, l);
if (l.isNative() && v.isAllowedToUnpackNatives() && f != null) {
unzippings.add(f);
extractRules.add(l.getDecompressExtractRules());
}
}
return new DecompressLibraryJob(unzippings.toArray(new File[unzippings.size()]), extractRules.toArray(new Extract[extractRules.size()]), getDecompressNativesToLocation(v));
}
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.util.log;
import java.awt.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jackhuang.hmcl.util.ui.GraphicsUtils;
/**
*
@@ -27,12 +28,12 @@ import java.util.regex.Pattern;
*/
public enum Level {
FATAL(1, Color.red),
ERROR(2, Color.red),
WARN(3, Color.orange),
FATAL(1, GraphicsUtils.getWebColor("#C0392B")),
ERROR(2, GraphicsUtils.getWebColor("#D35400")),
WARN(3, GraphicsUtils.getWebColor("#F39C12")),
INFO(4, Color.black),
DEBUG(5, Color.blue),
TRACE(6, Color.blue),
DEBUG(5, GraphicsUtils.getWebColor("#2980B9")),
TRACE(6, GraphicsUtils.getWebColor("#16A085")),
ALL(2147483647, Color.black);
public final int level;
@@ -40,6 +40,7 @@ public final class NetUtils {
public static HttpURLConnection createConnection(URL url, Proxy proxy) throws IOException {
HttpURLConnection con = (HttpURLConnection) url.openConnection(proxy);
con.setRequestProperty("User-Agent", "Hello Minecraft!");
con.setDoInput(true);
con.setUseCaches(false);
con.setConnectTimeout(15000);
@@ -48,7 +49,9 @@ public final class NetUtils {
}
public static String get(String url, String encoding) throws IOException {
return IOUtils.toString(new URL(url).openConnection().getInputStream());
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestProperty("User-Agent", "Hello Minecraft!");
return IOUtils.toString(con.getInputStream());
}
public static String get(String url) throws IOException {
@@ -89,7 +89,7 @@ public class ProcessMonitor {
try {
exitCode = p.getExitCode();
} catch(IllegalThreadStateException e) {
HMCLog.err("Failed to ");
HMCLog.err("Failed to get exit code ", e);
}
if (p.getExitCode() != 0 || StrUtils.containsOne(t.getLines(),
Arrays.asList("Unable to launch"), // LaunchWrapper will terminate the application returning exit code 0, but this is an error state.
@@ -71,11 +71,20 @@ public class ProcessThread extends Thread {
br = new BufferedReader(new InputStreamReader(in, Charsets.toCharset()));
String line;
while (p.isRunning())
while ((line = br.readLine()) != null)
while (p.isRunning()) {
if (isInterrupted())
return;
while ((line = br.readLine()) != null) {
if (isInterrupted())
return;
println(line);
while ((line = br.readLine()) != null)
}
}
while ((line = br.readLine()) != null) {
if (isInterrupted())
return;
println(line);
}
} catch (IOException e) {
HMCLog.err("An error occured when reading process stdout/stderr.", e);
} finally {
@@ -79,10 +79,9 @@ public class ZipEngine implements Closeable {
File[] files;
if (source.isDirectory())
files = source.listFiles();
else {
files = new File[1];
files[0] = source;
}
else
files = new File[] { source };
if (files == null)
return;
String pathName;//存相对路径(相对于待压缩的根目录)