From fca4441229808891f460d5fbc4affd51e8896aa5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Thu, 9 Jan 2014 01:22:34 +0100 Subject: Replace old launcher part with a shiny new one. No more garbage on the command line. --- .../org/multimc/onesix/OneSixLauncher.java | 196 +++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 depends/launcher/org/multimc/onesix/OneSixLauncher.java (limited to 'depends/launcher/org/multimc/onesix') diff --git a/depends/launcher/org/multimc/onesix/OneSixLauncher.java b/depends/launcher/org/multimc/onesix/OneSixLauncher.java new file mode 100644 index 00000000..2232eeba --- /dev/null +++ b/depends/launcher/org/multimc/onesix/OneSixLauncher.java @@ -0,0 +1,196 @@ +/* Copyright 2012-2014 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.multimc.onesix; + +import org.multimc.*; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +public class OneSixLauncher implements Launcher +{ + @Override + public int launch(ParamBucket params) + { + // get and process the launch script params + List libraries; + List mcparams; + List mods; + String mainClass; + String natives; + final String windowTitle; + String windowParams; + try + { + libraries = params.all("cp"); + mcparams = params.all("param"); + mainClass = params.first("mainClass"); + mods = params.allSafe("mods", new ArrayList()); + natives = params.first("natives"); + windowTitle = params.first("windowTitle"); + // windowParams = params.first("windowParams"); + } catch (NotFoundException e) + { + System.err.println("Not enough arguments."); + e.printStackTrace(System.err); + return -1; + } + + List allJars = new ArrayList(); + allJars.addAll(mods); + allJars.addAll(libraries); + + if(!Utils.addToClassPath(allJars)) + { + System.err.println("Halting launch due to previous errors."); + return -1; + } + + final ClassLoader cl = ClassLoader.getSystemClassLoader(); + + // print the pretty things + { + System.out.println("Main Class:"); + System.out.println(mainClass); + System.out.println(); + + System.out.println("Libraries:"); + for (String s : libraries) + { + System.out.println(s); + } + System.out.println(); + + if(mods.size() > 0) + { + System.out.println("Class Path Mods:"); + for (String s : mods) + { + System.out.println(s); + } + System.out.println(); + } + + System.out.println("Params:"); + System.out.println(mcparams.toString()); + System.out.println(); + } + + // set up the natives path(s). + System.setProperty("java.library.path", natives ); + Field fieldSysPath; + try + { + fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); + fieldSysPath.setAccessible( true ); + fieldSysPath.set( null, null ); + } catch (Exception e) + { + System.err.println("Failed to set the native library path:"); + e.printStackTrace(System.err); + return -1; + } + + // Get the Minecraft Class. + Class mc; + try + { + mc = cl.loadClass(mainClass); + } catch (ClassNotFoundException e) + { + System.err.println("Failed to find Minecraft main class:"); + e.printStackTrace(System.err); + return -1; + } + + // get the main method. + Method meth; + try + { + meth = mc.getMethod("main", String[].class); + } catch (NoSuchMethodException e) + { + System.err.println("Failed to acquire the main method:"); + e.printStackTrace(System.err); + return -1; + } + + // FIXME: works only on linux, we need a better solution +/* + final java.nio.ByteBuffer[] icons = IconLoader.load("icon.png"); + new Thread() { + public void run() { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + try + { + Class Display; + Method isCreated; + Method setTitle; + Method setIcon; + + Display = cl.loadClass("org.lwjgl.opengl.Display"); + isCreated = Display.getMethod("isCreated"); + setTitle = Display.getMethod("setTitle", String.class); + setIcon = Display.getMethod("setIcon", java.nio.ByteBuffer[].class); + + // set the window title? Maybe? + while(!(Boolean) isCreated.invoke(null)) + { + try + { + Thread.sleep(150); + } catch (InterruptedException ignored) {} + } + // Give it a bit more time ;) + Thread.sleep(150); + // set the title + setTitle.invoke(null,windowTitle); + // only set icon when there's actually something to set... + if(icons.length > 0) + { + setIcon.invoke(null,(Object)icons); + } + } + catch (Exception e) + { + System.err.println("Couldn't set window icon or title."); + e.printStackTrace(System.err); + } + } + } + .start(); +*/ + // start Minecraft + String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); // init params accordingly + try + { + meth.invoke(null, (Object) paramsArray); // static method doesn't have an instance + } catch (Exception e) + { + System.err.println("Failed to start Minecraft:"); + e.printStackTrace(System.err); + return -1; + } + return 0; + } +} -- cgit From 555cbe00ced6097202f148f200811acc6b1eeb4d Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 14 Jan 2014 01:13:35 +0100 Subject: Do not use the java checker during instance update --- depends/launcher/org/multimc/Utils.java | 31 +++++ .../org/multimc/onesix/OneSixLauncher.java | 17 ++- logic/OneSixUpdate.cpp | 148 +++++++++------------ logic/OneSixUpdate.h | 8 -- 4 files changed, 111 insertions(+), 93 deletions(-) (limited to 'depends/launcher/org/multimc/onesix') diff --git a/depends/launcher/org/multimc/Utils.java b/depends/launcher/org/multimc/Utils.java index ba90c07f..c4d55b6e 100644 --- a/depends/launcher/org/multimc/Utils.java +++ b/depends/launcher/org/multimc/Utils.java @@ -27,6 +27,37 @@ import java.util.List; public class Utils { + /** + * Combine two parts of a path. + * @param path1 + * @param path2 + * @return the paths, combined + */ + public static String combine (String path1, String path2) + { + File file1 = new File(path1); + File file2 = new File(file1, path2); + return file2.getPath(); + } + + /** + * Join a list of strings into a string using a separator! + * @param strings the string list to join + * @param separator the glue + * @return the result. + */ + public static String join (List strings, String separator) + { + StringBuilder sb = new StringBuilder(); + String sep = ""; + for(String s: strings) + { + sb.append(sep).append(s); + sep = separator; + } + return sb.toString(); + } + /** * Adds the specified library to the classpath * diff --git a/depends/launcher/org/multimc/onesix/OneSixLauncher.java b/depends/launcher/org/multimc/onesix/OneSixLauncher.java index 2232eeba..f25a3245 100644 --- a/depends/launcher/org/multimc/onesix/OneSixLauncher.java +++ b/depends/launcher/org/multimc/onesix/OneSixLauncher.java @@ -66,7 +66,11 @@ public class OneSixLauncher implements Launcher return -1; } - final ClassLoader cl = ClassLoader.getSystemClassLoader(); + String property = System.getProperty("os.arch"); + List allNativePaths = new ArrayList(); + boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64"); + allNativePaths.add(natives); + allNativePaths.add(Utils.combine(natives, is_64 ? "64" : "32")); // print the pretty things { @@ -74,6 +78,13 @@ public class OneSixLauncher implements Launcher System.out.println(mainClass); System.out.println(); + System.out.println("Native paths:"); + for (String s : allNativePaths) + { + System.out.println(s); + } + System.out.println(); + System.out.println("Libraries:"); for (String s : libraries) { @@ -96,8 +107,10 @@ public class OneSixLauncher implements Launcher System.out.println(); } + final ClassLoader cl = ClassLoader.getSystemClassLoader(); + // set up the natives path(s). - System.setProperty("java.library.path", natives ); + System.setProperty("java.library.path", Utils.join(allNativePaths, String.valueOf(File.pathSeparatorChar)) ); Field fieldSysPath; try { diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 4d93477a..5309a7e0 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -54,17 +54,7 @@ void OneSixUpdate::executeTask() if (m_only_prepare) { - /* - * FIXME: in offline mode, do not proceed! - */ - setStatus(tr("Testing the Java installation...")); - QString java_path = m_inst->settings().get("JavaPath").toString(); - - checker.reset(new JavaChecker()); - connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, - SLOT(checkFinishedOffline(JavaCheckResult))); - checker->path = java_path; - checker->performCheck(); + prepareForLaunch(); return; } @@ -83,46 +73,8 @@ void OneSixUpdate::executeTask() } else { - checkJavaOnline(); - } -} - -void OneSixUpdate::checkJavaOnline() -{ - setStatus(tr("Testing the Java installation...")); - QString java_path = m_inst->settings().get("JavaPath").toString(); - - checker.reset(new JavaChecker()); - connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, - SLOT(checkFinishedOnline(JavaCheckResult))); - checker->path = java_path; - checker->performCheck(); -} - -void OneSixUpdate::checkFinishedOnline(JavaCheckResult result) -{ - if (result.valid) - { - java_is_64bit = result.is_64bit; jarlibStart(); } - else - { - emitFailed("The java binary doesn't work. Check the settings and correct the problem"); - } -} - -void OneSixUpdate::checkFinishedOffline(JavaCheckResult result) -{ - if (result.valid) - { - java_is_64bit = result.is_64bit; - prepareForLaunch(); - } - else - { - emitFailed("The java binary doesn't work. Check the settings and correct the problem"); - } } void OneSixUpdate::versionFileStart() @@ -130,7 +82,8 @@ void OneSixUpdate::versionFileStart() QLOG_INFO() << m_inst->name() << ": getting version file."; setStatus(tr("Getting the version files from Mojang...")); - QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; + QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + + targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; auto job = new NetJob("Version index"); job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); specificVersionDownloadJob.reset(job); @@ -186,7 +139,7 @@ void OneSixUpdate::versionFileFinished() } inst->reloadFullVersion(); - checkJavaOnline(); + jarlibStart(); } void OneSixUpdate::versionFileFailed() @@ -230,7 +183,7 @@ void OneSixUpdate::assetIndexFinished() { emitFailed("Failed to read the assets index!"); } - + QList dls; for (auto object : index.objects.values()) { @@ -245,17 +198,17 @@ void OneSixUpdate::assetIndexFinished() dls.append(objectDL); } } - if(dls.size()) + if (dls.size()) { setStatus(tr("Getting the assets files from Mojang...")); auto job = new NetJob("Assets for " + inst->name()); - for(auto dl: dls) + for (auto dl : dls) job->addNetAction(dl); jarlibDownloadJob.reset(job); connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished())); connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetsFailed())); connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); + SIGNAL(progress(qint64, qint64))); jarlibDownloadJob->start(); return; } @@ -277,8 +230,6 @@ void OneSixUpdate::assetsFailed() emitFailed("Failed to download assets!"); } - - void OneSixUpdate::jarlibStart() { setStatus(tr("Getting the library files from Mojang...")); @@ -318,24 +269,37 @@ void OneSixUpdate::jarlibStart() { if (lib->hint() == "local") continue; - QString subst = java_is_64bit ? "64" : "32"; - QString storage = lib->storagePath(); - QString dl = lib->downloadUrl(); - storage.replace("${arch}", subst); - dl.replace("${arch}", subst); + QString raw_storage = lib->storagePath(); + QString raw_dl = lib->downloadUrl(); - auto entry = metacache->resolveEntry("libraries", storage); - if (entry->stale) + auto f = [&](QString storage, QString dl) { - if (lib->hint() == "forge-pack-xz") + auto entry = metacache->resolveEntry("libraries", storage); + if (entry->stale) { - ForgeLibs.append(ForgeXzDownload::make(storage, entry)); - } - else - { - jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); + if (lib->hint() == "forge-pack-xz") + { + ForgeLibs.append(ForgeXzDownload::make(storage, entry)); + } + else + { + jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); + } } + }; + if (raw_storage.contains("${arch}")) + { + QString cooked_storage = raw_storage; + QString cooked_dl = raw_dl; + f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); + cooked_storage = raw_storage; + cooked_dl = raw_dl; + f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); + } + else + { + f(raw_storage, raw_dl); } } // TODO: think about how to propagate this from the original json file... or IF AT ALL @@ -388,7 +352,9 @@ void OneSixUpdate::prepareForLaunch() auto libs_to_extract = version->getActiveNativeLibs(); // Acquire bag - bool success = ensureFolderPathExists(natives_dir_raw); + bool success = true; + success &= ensureFolderPathExists(natives_dir_raw + "/32"); + success &= ensureFolderPathExists(natives_dir_raw + "/64"); if (!success) { emitFailed("Could not create the native library folder:\n" + natives_dir_raw + @@ -398,22 +364,38 @@ void OneSixUpdate::prepareForLaunch() } // Put swag in the bag - QString subst = java_is_64bit ? "64" : "32"; for (auto lib : libs_to_extract) { + auto f = [&](QString storage, QString arch = "") + { + QString path = "libraries/" + storage; + QLOG_INFO() << "Will extract " << path.toLocal8Bit(); + if (JlCompress::extractWithExceptions(path, natives_dir_raw + "/" + arch, + lib->extract_excludes).isEmpty()) + { + emitFailed( + "Could not extract the native library:\n" + path + + "\nMake sure MultiMC has appropriate permissions and there is enough space " + "on the storage device."); + return false; + } + }; QString storage = lib->storagePath(); - storage.replace("${arch}", subst); - - QString path = "libraries/" + storage; - QLOG_INFO() << "Will extract " << path.toLocal8Bit(); - if (JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes) - .isEmpty()) + if (storage.contains("${arch}")) { - emitFailed( - "Could not extract the native library:\n" + path + - "\nMake sure MultiMC has appropriate permissions and there is enough space " - "on the storage device."); - return; + QString cooked_storage = storage; + cooked_storage.replace("${arch}", "32"); + if (!f(cooked_storage, "32")) + return; + cooked_storage = storage; + cooked_storage.replace("${arch}", "64"); + if (!f(cooked_storage, "64")) + return; + } + else + { + if (!f(storage)) + return; } } diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index 00b769c7..bc717a94 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -21,7 +21,6 @@ #include "logic/net/NetJob.h" #include "logic/tasks/Task.h" -#include "logic/JavaChecker.h" class MinecraftVersion; class BaseInstance; @@ -50,10 +49,6 @@ slots: void assetsFinished(); void assetsFailed(); - void checkJavaOnline(); - void checkFinishedOnline(JavaCheckResult result); - void checkFinishedOffline(JavaCheckResult result); - // extract the appropriate libraries void prepareForLaunch(); @@ -65,7 +60,4 @@ private: std::shared_ptr targetVersion; BaseInstance *m_inst = nullptr; bool m_only_prepare = false; - std::shared_ptr checker; - - bool java_is_64bit = false; }; -- cgit From 188d0d58865f5e134b5803bda2cd631a61cf2915 Mon Sep 17 00:00:00 2001 From: Orochimarufan Date: Fri, 17 Jan 2014 22:55:10 +0100 Subject: Improve Console window output. -> Log Pre- and Post-Launch command happenings -> Enable the java part to specify the level TODO: fix logging with mc 1.7's log4j logging infrastructure Signed-off-by: Orochimarufan --- .gitignore | 14 +- MultiMC.cpp | 1 + depends/launcher/org/multimc/EntryPoint.java | 8 +- depends/launcher/org/multimc/Utils.java | 23 +++ .../org/multimc/legacy/LegacyLauncher.java | 28 +-- .../org/multimc/onesix/OneSixLauncher.java | 30 +-- gui/ConsoleWindow.cpp | 3 + logic/BaseInstance.cpp | 1 + logic/MinecraftProcess.cpp | 203 +++++++++++++++++---- logic/MinecraftProcess.h | 17 +- 10 files changed, 247 insertions(+), 81 deletions(-) (limited to 'depends/launcher/org/multimc/onesix') diff --git a/.gitignore b/.gitignore index 54bd5039..2ef0d673 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,23 @@ Thumbs.db .kdev4 -MultiMC5.kdev4 -MultiMC.pro.user -CMakeLists.txt.user .user .directory -build resources/CMakeFiles resources/MultiMCLauncher.jar *~ *.swp html/ +# Project Files +MultiMC5.kdev4 +MultiMC.pro.user +CMakeLists.txt.user +CMakeLists.txt.user.* + +# Build dirs +build +/build-* + # Ctags File tags diff --git a/MultiMC.cpp b/MultiMC.cpp index 7a82c642..e4a30f22 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -379,6 +379,7 @@ void MultiMC::initGlobalSettings() // Console m_settings->registerSetting("ShowConsole", true); m_settings->registerSetting("AutoCloseConsole", true); + m_settings->registerSetting("LogPrePostOutput", true); // Console Colors // m_settings->registerSetting("SysMessageColor", QColor(Qt::blue)); diff --git a/depends/launcher/org/multimc/EntryPoint.java b/depends/launcher/org/multimc/EntryPoint.java index c42e34e7..83f232f1 100644 --- a/depends/launcher/org/multimc/EntryPoint.java +++ b/depends/launcher/org/multimc/EntryPoint.java @@ -71,15 +71,15 @@ public class EntryPoint if(param.equals("legacy")) { m_launcher = new LegacyLauncher(); - System.out.println("Using legacy launcher."); - System.out.println(); + Utils.log("Using legacy launcher."); + Utils.log(); return Action.Launch; } if(param.equals("onesix")) { m_launcher = new OneSixLauncher(); - System.out.println("Using onesix launcher."); - System.out.println(); + Utils.log("Using onesix launcher."); + Utils.log(); return Action.Launch; } else diff --git a/depends/launcher/org/multimc/Utils.java b/depends/launcher/org/multimc/Utils.java index c4d55b6e..df0ef861 100644 --- a/depends/launcher/org/multimc/Utils.java +++ b/depends/launcher/org/multimc/Utils.java @@ -153,4 +153,27 @@ public class Utils } return null; } + + /** + * Log to the MultiMC console + * + * @param message A String containing the message + * @param level A String containing the level name. See MinecraftProcess::getLevel() + */ + public static void log(String message, String level) + { + // Kinda dirty + String tag = "!![" + level + "]!"; + System.out.println(tag + message.replace("\n", "\n" + tag)); + } + + public static void log(String message) + { + log(message, "MultiMC"); + } + + public static void log() + { + System.out.println(); + } } diff --git a/depends/launcher/org/multimc/legacy/LegacyLauncher.java b/depends/launcher/org/multimc/legacy/LegacyLauncher.java index 6a0a3014..1ca37c4a 100644 --- a/depends/launcher/org/multimc/legacy/LegacyLauncher.java +++ b/depends/launcher/org/multimc/legacy/LegacyLauncher.java @@ -102,20 +102,20 @@ public class LegacyLauncher implements Launcher // print the pretty things { - System.out.println("Main Class:"); - System.out.println(mainClass); - System.out.println(); + Utils.log("Main Class:"); + Utils.log(" " + mainClass); + Utils.log(); - System.out.println("Class Path:"); + Utils.log("Class Path:"); for (URL s : classpath) { - System.out.println(s); + Utils.log(" " + s); } - System.out.println(); + Utils.log(); - System.out.println("Native Path:"); - System.out.println(nativesDir); - System.out.println(); + Utils.log("Native Path:"); + Utils.log(" " + nativesDir); + Utils.log(); } URLClassLoader cl = new URLClassLoader(classpath, LegacyLauncher.class.getClassLoader()); @@ -149,7 +149,7 @@ public class LegacyLauncher implements Launcher mcArgs[0] = userName; mcArgs[1] = sessionId; - System.out.println("Launching with applet wrapper..."); + Utils.log("Launching with applet wrapper..."); try { Class MCAppletClass = cl.loadClass("net.minecraft.client.MinecraftApplet"); @@ -158,16 +158,16 @@ public class LegacyLauncher implements Launcher mcWindow.start(mcappl, userName, sessionId, winSize, maximize); } catch (Exception e) { - System.err.println("Applet wrapper failed:"); + Utils.log("Applet wrapper failed:", "Error"); e.printStackTrace(System.err); - System.err.println(); - System.out.println("Falling back to compatibility mode."); + Utils.log(); + Utils.log("Falling back to compatibility mode."); try { mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs); } catch (Exception e1) { - System.err.println("Failed to invoke the Minecraft main class:"); + Utils.log("Failed to invoke the Minecraft main class:", "Fatal"); e1.printStackTrace(System.err); return -1; } diff --git a/depends/launcher/org/multimc/onesix/OneSixLauncher.java b/depends/launcher/org/multimc/onesix/OneSixLauncher.java index f25a3245..d6c80b67 100644 --- a/depends/launcher/org/multimc/onesix/OneSixLauncher.java +++ b/depends/launcher/org/multimc/onesix/OneSixLauncher.java @@ -74,37 +74,37 @@ public class OneSixLauncher implements Launcher // print the pretty things { - System.out.println("Main Class:"); - System.out.println(mainClass); - System.out.println(); + Utils.log("Main Class:"); + Utils.log(" " + mainClass); + Utils.log(); - System.out.println("Native paths:"); + Utils.log("Native paths:"); for (String s : allNativePaths) { - System.out.println(s); + Utils.log(" " + s); } - System.out.println(); + Utils.log(); - System.out.println("Libraries:"); + Utils.log("Libraries:"); for (String s : libraries) { - System.out.println(s); + Utils.log(" " + s); } - System.out.println(); + Utils.log(); if(mods.size() > 0) { - System.out.println("Class Path Mods:"); + Utils.log("Class Path Mods:"); for (String s : mods) { - System.out.println(s); + Utils.log(" " + s); } - System.out.println(); + Utils.log(); } - System.out.println("Params:"); - System.out.println(mcparams.toString()); - System.out.println(); + Utils.log("Params:"); + Utils.log(" " + mcparams.toString()); + Utils.log(); } final ClassLoader cl = ClassLoader.getSystemClassLoader(); diff --git a/gui/ConsoleWindow.cpp b/gui/ConsoleWindow.cpp index 54a74bde..dc36a8ff 100644 --- a/gui/ConsoleWindow.cpp +++ b/gui/ConsoleWindow.cpp @@ -140,6 +140,9 @@ void ConsoleWindow::write(QString data, MessageLevel::Enum mode) else if (mode == MessageLevel::Debug) while (iter.hasNext()) writeColor(iter.next(), "green"); + else if (mode == MessageLevel::PrePost) + while (iter.hasNext()) + writeColor(iter.next(), "grey"); // TODO: implement other MessageLevels else while (iter.hasNext()) diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index afe3dd03..222004a3 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -82,6 +82,7 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, settings().registerSetting("OverrideConsole", false); settings().registerOverride(globalSettings->getSetting("ShowConsole")); settings().registerOverride(globalSettings->getSetting("AutoCloseConsole")); + settings().registerOverride(globalSettings->getSetting("LogPrePostOutput")); } void BaseInstance::iconUpdated(QString key) diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index 18d63674..84610021 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "BaseInstance.h" @@ -42,6 +43,7 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst) #ifdef LINUX // Strip IBus + // IBus is a Linux IME framework. For some reason, it breaks MC? if (env.value("XMODIFIERS").contains(IBUS)) env.insert("XMODIFIERS", env.value("XMODIFIERS").replace(IBUS, "")); #endif @@ -57,6 +59,15 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst) // std channels connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr())); connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut())); + + // Log prepost launch command output (can be disabled.) + if (m_instance->settings().get("LogPrePostOutput").toBool()) + { + connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, + this, &MinecraftProcess::on_prepost_stdErr); + connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, + this, &MinecraftProcess::on_prepost_stdOut); + } } void MinecraftProcess::setWorkdir(QString path) @@ -97,43 +108,133 @@ QString MinecraftProcess::censorPrivateInfo(QString in) } // console window +MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLevel::Enum level) +{ + if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || + line.contains("[FINER]") || line.contains("[FINEST]")) + level = MessageLevel::Message; + if (line.contains("[SEVERE]") || line.contains("[STDERR]")) + level = MessageLevel::Error; + if (line.contains("[WARNING]")) + level = MessageLevel::Warning; + if (line.contains("Exception in thread") || line.contains(" at ")) + level = MessageLevel::Fatal; + if (line.contains("[DEBUG]")) + level = MessageLevel::Debug; + return level; +} + +MessageLevel::Enum MinecraftProcess::getLevel(const QString &levelName) +{ + if (levelName == "MultiMC") + return MessageLevel::MultiMC; + else if (levelName == "Debug") + return MessageLevel::Debug; + else if (levelName == "Info") + return MessageLevel::Info; + else if (levelName == "Message") + return MessageLevel::Message; + else if (levelName == "Warning") + return MessageLevel::Warning; + else if (levelName == "Error") + return MessageLevel::Error; + else if (levelName == "Fatal") + return MessageLevel::Fatal; + // Skip PrePost, it's not exposed to !![]! + else + return MessageLevel::Message; +} + +void MinecraftProcess::logOutput(const QStringList &lines, + MessageLevel::Enum defaultLevel, + bool guessLevel, bool censor) +{ + for (int i = 0; i < lines.size(); ++i) + logOutput(lines[i], defaultLevel, guessLevel, censor); +} + +void MinecraftProcess::logOutput(QString line, + MessageLevel::Enum defaultLevel, + bool guessLevel, bool censor) +{ + MessageLevel::Enum level = defaultLevel; + + // Level prefix + int endmark = line.indexOf("]!"); + if (line.startsWith("!![") && endmark != -1) + { + level = getLevel(line.left(endmark).mid(3)); + line = line.mid(endmark + 2); + } + // Guess level + else if (guessLevel) + level = this->guessLevel(line, defaultLevel); + + if (censor) + line = censorPrivateInfo(line); + + emit log(line, level); +} + void MinecraftProcess::on_stdErr() { QByteArray data = readAllStandardError(); QString str = m_err_leftover + QString::fromLocal8Bit(data); - m_err_leftover.clear(); + QStringList lines = str.split("\n"); - bool complete = str.endsWith("\n"); + m_err_leftover = lines.takeLast(); - for (int i = 0; i < lines.size() - 1; i++) - { - QString &line = lines[i]; - emit log(censorPrivateInfo(line), getLevel(line, MessageLevel::Error)); - } - if (!complete) - m_err_leftover = lines.last(); + logOutput(lines, MessageLevel::Error); } void MinecraftProcess::on_stdOut() { QByteArray data = readAllStandardOutput(); QString str = m_out_leftover + QString::fromLocal8Bit(data); - m_out_leftover.clear(); + QStringList lines = str.split("\n"); - bool complete = str.endsWith("\n"); + m_out_leftover = lines.takeLast(); - for (int i = 0; i < lines.size() - 1; i++) - { - QString &line = lines[i]; - emit log(censorPrivateInfo(line), getLevel(line, MessageLevel::Message)); - } - if (!complete) - m_out_leftover = lines.last(); + logOutput(lines); +} + +void MinecraftProcess::on_prepost_stdErr() +{ + QByteArray data = m_prepostlaunchprocess.readAllStandardError(); + QString str = m_err_leftover + QString::fromLocal8Bit(data); + + QStringList lines = str.split("\n"); + m_err_leftover = lines.takeLast(); + + logOutput(lines, MessageLevel::PrePost, false, false); +} + +void MinecraftProcess::on_prepost_stdOut() +{ + QByteArray data = m_prepostlaunchprocess.readAllStandardOutput(); + QString str = m_out_leftover + QString::fromLocal8Bit(data); + + QStringList lines = str.split("\n"); + m_out_leftover = lines.takeLast(); + + logOutput(lines, MessageLevel::PrePost, false, false); } // exit handler void MinecraftProcess::finish(int code, ExitStatus status) { + // Flush console window + if (!m_err_leftover.isEmpty()) + { + logOutput(m_err_leftover, MessageLevel::Error); + m_err_leftover.clear(); + } + if (!m_out_leftover.isEmpty()) + { + logOutput(m_out_leftover); + m_out_leftover.clear(); + } + if (!killed) { if (status == NormalExit) @@ -156,15 +257,32 @@ void MinecraftProcess::finish(int code, ExitStatus status) m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); // run post-exit - if (!m_instance->settings().get("PostExitCommand").toString().isEmpty()) + QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString(); + if (!postlaunch_cmd.isEmpty()) { - m_prepostlaunchprocess.start(m_instance->settings().get("PostExitCommand").toString()); + emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd)); + m_prepostlaunchprocess.start(postlaunch_cmd); m_prepostlaunchprocess.waitForFinished(); + // Flush console window + if (!m_err_leftover.isEmpty()) + { + logOutput(m_err_leftover, MessageLevel::PrePost); + m_err_leftover.clear(); + } + if (!m_out_leftover.isEmpty()) + { + logOutput(m_out_leftover, MessageLevel::PrePost); + m_out_leftover.clear(); + } if (m_prepostlaunchprocess.exitStatus() != NormalExit) { + emit log(tr("Post-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()), + MessageLevel::Error); emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(), m_prepostlaunchprocess.exitStatus()); } + else + emit log(tr("Post-Launch command ran successfully.\n\n")); } m_instance->cleanupAfterRun(); emit ended(m_instance, code, status); @@ -178,17 +296,40 @@ void MinecraftProcess::killMinecraft() void MinecraftProcess::launch() { - if (!m_instance->settings().get("PreLaunchCommand").toString().isEmpty()) + emit log("MultiMC version: " + MMC->version().toString() + "\n\n"); + emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n"); + + QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString(); + if (!prelaunch_cmd.isEmpty()) { - m_prepostlaunchprocess.start(m_instance->settings().get("PreLaunchCommand").toString()); + // Launch + emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd)); + m_prepostlaunchprocess.start(prelaunch_cmd); + // Wait m_prepostlaunchprocess.waitForFinished(); + // Flush console window + if (!m_err_leftover.isEmpty()) + { + logOutput(m_err_leftover, MessageLevel::PrePost); + m_err_leftover.clear(); + } + if (!m_out_leftover.isEmpty()) + { + logOutput(m_out_leftover, MessageLevel::PrePost); + m_out_leftover.clear(); + } + // Process return values if (m_prepostlaunchprocess.exitStatus() != NormalExit) { + emit log(tr("Pre-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()), + MessageLevel::Fatal); m_instance->cleanupAfterRun(); emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(), m_prepostlaunchprocess.exitStatus()); return; } + else + emit log(tr("Pre-Launch command ran successfully.\n\n")); } m_instance->setLastLaunch(); @@ -221,8 +362,6 @@ void MinecraftProcess::launch() } QString JavaPath = m_instance->settings().get("JavaPath").toString(); - emit log("MultiMC version: " + MMC->version().toString() + "\n\n"); - emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n"); emit log("Java path is:\n" + JavaPath + "\n\n"); QString allArgs = args.join(", "); emit log("Java Arguments:\n[" + censorPrivateInfo(allArgs) + "]\n\n"); @@ -241,19 +380,3 @@ void MinecraftProcess::launch() QByteArray bytes = launchScript.toUtf8(); writeData(bytes.constData(), bytes.length()); } - -MessageLevel::Enum MinecraftProcess::getLevel(const QString &line, MessageLevel::Enum level) -{ - if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || - line.contains("[FINER]") || line.contains("[FINEST]")) - level = MessageLevel::Message; - if (line.contains("[SEVERE]") || line.contains("[STDERR]")) - level = MessageLevel::Error; - if (line.contains("[WARNING]")) - level = MessageLevel::Warning; - if (line.contains("Exception in thread") || line.contains(" at ")) - level = MessageLevel::Fatal; - if (line.contains("[DEBUG]")) - level = MessageLevel::Debug; - return level; -} diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h index 5d1a2b71..70e5df52 100644 --- a/logic/MinecraftProcess.h +++ b/logic/MinecraftProcess.h @@ -31,11 +31,12 @@ enum Enum { MultiMC, /**< MultiMC Messages */ Debug, /**< Debug Messages */ - Info, /**< Info Messages */ + Info, /**< Info Messages */ Message, /**< Standard Messages */ Warning, /**< Warnings */ Error, /**< Errors */ - Fatal /**< Fatal Errors */ + Fatal, /**< Fatal Errors */ + PrePost, /**< Pre/Post Launch command output */ }; } @@ -125,9 +126,17 @@ slots: void finish(int, QProcess::ExitStatus status); void on_stdErr(); void on_stdOut(); + void on_prepost_stdOut(); + void on_prepost_stdErr(); + void logOutput(const QStringList &lines, + MessageLevel::Enum defaultLevel = MessageLevel::Message, + bool guessLevel = true, bool censor = true); + void logOutput(QString line, + MessageLevel::Enum defaultLevel = MessageLevel::Message, + bool guessLevel = true, bool censor = true); private: QString censorPrivateInfo(QString in); - MessageLevel::Enum getLevel(const QString &message, MessageLevel::Enum defaultLevel); - + MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel); + MessageLevel::Enum getLevel(const QString &levelName); }; -- cgit From ecc80bd763111b0e368aa80366bd8382cd814ee6 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sat, 18 Jan 2014 03:32:31 +0100 Subject: Change the native extraction/loading logic. --- .../org/multimc/onesix/OneSixLauncher.java | 5 +- logic/OneSixLibrary.cpp | 56 ++++++++++++++++++++++ logic/OneSixLibrary.h | 2 + logic/OneSixUpdate.cpp | 55 ++++----------------- 4 files changed, 71 insertions(+), 47 deletions(-) (limited to 'depends/launcher/org/multimc/onesix') diff --git a/depends/launcher/org/multimc/onesix/OneSixLauncher.java b/depends/launcher/org/multimc/onesix/OneSixLauncher.java index d6c80b67..c1676c94 100644 --- a/depends/launcher/org/multimc/onesix/OneSixLauncher.java +++ b/depends/launcher/org/multimc/onesix/OneSixLauncher.java @@ -70,7 +70,7 @@ public class OneSixLauncher implements Launcher List allNativePaths = new ArrayList(); boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64"); allNativePaths.add(natives); - allNativePaths.add(Utils.combine(natives, is_64 ? "64" : "32")); + allNativePaths.add(natives + "/" + (is_64 ? "64" : "32")); // print the pretty things { @@ -110,7 +110,8 @@ public class OneSixLauncher implements Launcher final ClassLoader cl = ClassLoader.getSystemClassLoader(); // set up the natives path(s). - System.setProperty("java.library.path", Utils.join(allNativePaths, String.valueOf(File.pathSeparatorChar)) ); + String libpath = Utils.join(allNativePaths, String.valueOf(File.pathSeparatorChar)); + System.setProperty("java.library.path", libpath); Field fieldSysPath; try { diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 4b6ed9dc..1d69b660 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -19,6 +19,9 @@ #include "OneSixRule.h" #include "OpSys.h" #include "logic/net/URLConstants.h" +#include +#include +#include "logger/QsLog.h" void OneSixLibrary::finalize() { @@ -133,6 +136,59 @@ QString OneSixLibrary::hint() return m_hint; } +bool OneSixLibrary::extractTo(QString target_dir) +{ + QString storage = storagePath(); + if (storage.contains("${arch}")) + { + QString cooked_storage = storage; + cooked_storage.replace("${arch}", "32"); + QString origin = PathCombine("libraries", cooked_storage); + QString target_dir_cooked = PathCombine(target_dir, "32"); + if(!ensureFolderPathExists(target_dir_cooked)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; + return false; + } + if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) + .isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + origin; + return false; + } + cooked_storage = storage; + cooked_storage.replace("${arch}", "64"); + origin = PathCombine("libraries", cooked_storage); + target_dir_cooked = PathCombine(target_dir, "32"); + if(!ensureFolderPathExists(target_dir_cooked)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; + return false; + } + if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) + .isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + origin; + return false; + } + } + else + { + if(!ensureFolderPathExists(target_dir)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir; + return false; + } + QString path = PathCombine("libraries", storage); + if (JlCompress::extractWithExceptions(path, target_dir, extract_excludes).isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + path; + return false; + } + } + return true; +} + QJsonObject OneSixLibrary::toJson() { QJsonObject libRoot; diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 3f0bc83d..bc097348 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -126,4 +126,6 @@ public: /// set a hint about how to treat the library. This is an MMC extension. void setHint(QString hint); QString hint(); + + bool extractTo(QString target_dir); }; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 5309a7e0..ee355308 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -340,7 +340,6 @@ void OneSixUpdate::prepareForLaunch() // delete any leftovers, if they are present. onesix_inst->cleanupAfterRun(); - // Acquire swag QString natives_dir_raw = PathCombine(onesix_inst->instanceRoot(), "natives/"); auto version = onesix_inst->getFullVersion(); if (!version) @@ -349,56 +348,22 @@ void OneSixUpdate::prepareForLaunch() "it or changing the version."); return; } - auto libs_to_extract = version->getActiveNativeLibs(); - - // Acquire bag - bool success = true; - success &= ensureFolderPathExists(natives_dir_raw + "/32"); - success &= ensureFolderPathExists(natives_dir_raw + "/64"); - if (!success) - { - emitFailed("Could not create the native library folder:\n" + natives_dir_raw + +/* + * emitFailed("Could not create the native library folder:\n" + natives_dir_raw + "\nMake sure MultiMC has appropriate permissions and there is enough space " "on the storage device."); - return; - } - - // Put swag in the bag - for (auto lib : libs_to_extract) +*/ + for (auto lib : version->getActiveNativeLibs()) { - auto f = [&](QString storage, QString arch = "") + if (!lib->extractTo(natives_dir_raw)) { - QString path = "libraries/" + storage; - QLOG_INFO() << "Will extract " << path.toLocal8Bit(); - if (JlCompress::extractWithExceptions(path, natives_dir_raw + "/" + arch, - lib->extract_excludes).isEmpty()) - { - emitFailed( - "Could not extract the native library:\n" + path + - "\nMake sure MultiMC has appropriate permissions and there is enough space " - "on the storage device."); - return false; - } - }; - QString storage = lib->storagePath(); - if (storage.contains("${arch}")) - { - QString cooked_storage = storage; - cooked_storage.replace("${arch}", "32"); - if (!f(cooked_storage, "32")) - return; - cooked_storage = storage; - cooked_storage.replace("${arch}", "64"); - if (!f(cooked_storage, "64")) - return; - } - else - { - if (!f(storage)) - return; + emitFailed("Could not extract the native library:\n" + lib->storagePath() + " to " + + natives_dir_raw + + "\nMake sure MultiMC has appropriate permissions and there is enough " + "space on the storage device."); + return; } } - // Show them your war face! emitSucceeded(); } -- cgit