diff --git a/CMakeLists.txt b/CMakeLists.txt index a413c700..c92b3f07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -388,6 +388,8 @@ logic/NagUtils.h logic/NagUtils.cpp logic/SkinUtils.h logic/SkinUtils.cpp +logic/JavaCheckerJob.h +logic/JavaCheckerJob.cpp # Assets logic/assets/AssetsUtils.h diff --git a/depends/javacheck/JavaCheck.java b/depends/javacheck/JavaCheck.java index 73688082..11420b86 100644 --- a/depends/javacheck/JavaCheck.java +++ b/depends/javacheck/JavaCheck.java @@ -2,13 +2,23 @@ import java.lang.Integer; public class JavaCheck { - private static final String key = "os.arch"; + private static final String[] keys = {"os.arch", "java.version"}; public static void main (String [] args) { - String property = System.getProperty(key); - System.out.println(key + "=" + property); - if (property != null) - System.exit(0); - System.exit(1); + int ret = 0; + for(String key : keys) + { + String property = System.getProperty(key); + if(property != null) + { + System.out.println(key + "=" + property); + } + else + { + ret = 1; + } + } + + System.exit(ret); } } diff --git a/gui/dialogs/InstanceSettings.cpp b/gui/dialogs/InstanceSettings.cpp index bc6b266a..641c7fab 100644 --- a/gui/dialogs/InstanceSettings.cpp +++ b/gui/dialogs/InstanceSettings.cpp @@ -220,7 +220,8 @@ void InstanceSettings::on_javaTestBtn_clicked() checker.reset(new JavaChecker()); connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, SLOT(checkFinished(JavaCheckResult))); - checker->performCheck(ui->javaPathTextBox->text()); + checker->path = ui->javaPathTextBox->text(); + checker->performCheck(); } void InstanceSettings::checkFinished(JavaCheckResult result) diff --git a/gui/dialogs/SettingsDialog.cpp b/gui/dialogs/SettingsDialog.cpp index 5dcf60f8..e7f537e3 100644 --- a/gui/dialogs/SettingsDialog.cpp +++ b/gui/dialogs/SettingsDialog.cpp @@ -259,7 +259,8 @@ void SettingsDialog::on_javaTestBtn_clicked() checker.reset(new JavaChecker()); connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, SLOT(checkFinished(JavaCheckResult))); - checker->performCheck(ui->javaPathTextBox->text()); + checker->path = ui->javaPathTextBox->text(); + checker->performCheck(); } void SettingsDialog::checkFinished(JavaCheckResult result) @@ -271,7 +272,8 @@ void SettingsDialog::checkFinished(JavaCheckResult result) if (result.is_64bit) text += "Using 64bit java.\n"; text += "\n"; - text += "Platform reported: " + result.realPlatform; + text += "Platform reported: " + result.realPlatform + "\n"; + text += "Java version reported: " + result.javaVersion; QMessageBox::information(this, tr("Java test success"), text); } else diff --git a/logic/JavaChecker.cpp b/logic/JavaChecker.cpp index daad7281..38bbf700 100644 --- a/logic/JavaChecker.cpp +++ b/logic/JavaChecker.cpp @@ -1,6 +1,7 @@ #include "JavaChecker.h" #include #include +#include #define CHECKER_FILE "JavaChecker.jar" @@ -8,7 +9,7 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent) { } -void JavaChecker::performCheck(QString path) +void JavaChecker::performCheck() { if(QFile::exists(CHECKER_FILE)) { @@ -40,30 +41,53 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) QProcessPtr _process; _process.swap(process); - if (status == QProcess::CrashExit || exitcode == 1) - { - emit checkFinished({}); - return; - } - - QString p_stdout = _process->readAllStandardOutput(); - auto parts = p_stdout.split('=', QString::SkipEmptyParts); - if (parts.size() != 2 || parts[0] != "os.arch") - { - emit checkFinished({}); - return; - } - - auto os_arch = parts[1].remove('\n').remove('\r'); - bool is_64 = os_arch == "x86_64" || os_arch == "amd64"; - JavaCheckResult result; { - result.valid = true; - result.is_64bit = is_64; - result.mojangPlatform = is_64 ? "64" : "32"; - result.realPlatform = os_arch; + result.path = path; } + + if (status == QProcess::CrashExit || exitcode == 1) + { + emit checkFinished(result); + return; + } + + bool success = true; + QString p_stdout = _process->readAllStandardOutput(); + QMap results; + QStringList lines = p_stdout.split("\n", QString::SkipEmptyParts); + for(QString line : lines) + { + line = line.trimmed(); + + auto parts = line.split('=', QString::SkipEmptyParts); + if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) + { + success = false; + } + else + { + results.insert(parts[0], parts[1]); + } + } + + if(!results.contains("os.arch") || !results.contains("java.version") || !success) + { + emit checkFinished(result); + return; + } + + auto os_arch = results["os.arch"]; + auto java_version = results["java.version"]; + bool is_64 = os_arch == "x86_64" || os_arch == "amd64"; + + + result.valid = true; + result.is_64bit = is_64; + result.mojangPlatform = is_64 ? "64" : "32"; + result.realPlatform = os_arch; + result.javaVersion = java_version; + emit checkFinished(result); } @@ -72,7 +96,13 @@ void JavaChecker::error(QProcess::ProcessError err) if(err == QProcess::FailedToStart) { killTimer.stop(); - emit checkFinished({}); + + JavaCheckResult result; + { + result.path = path; + } + + emit checkFinished(result); return; } } diff --git a/logic/JavaChecker.h b/logic/JavaChecker.h index 34782383..4488da66 100644 --- a/logic/JavaChecker.h +++ b/logic/JavaChecker.h @@ -3,21 +3,28 @@ #include #include +class JavaChecker; + struct JavaCheckResult { + QString path; QString mojangPlatform; QString realPlatform; + QString javaVersion; bool valid = false; bool is_64bit = false; }; -typedef std::shared_ptr QProcessPtr; +typedef std::shared_ptr QProcessPtr; +typedef std::shared_ptr JavaCheckerPtr; class JavaChecker : public QObject { Q_OBJECT public: explicit JavaChecker(QObject *parent = 0); - void performCheck(QString path); + void performCheck(); + + QString path; signals: void checkFinished(JavaCheckResult result); diff --git a/logic/JavaCheckerJob.cpp b/logic/JavaCheckerJob.cpp new file mode 100644 index 00000000..36a8a050 --- /dev/null +++ b/logic/JavaCheckerJob.cpp @@ -0,0 +1,49 @@ +/* Copyright 2013 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. + */ + +#include "JavaCheckerJob.h" +#include "pathutils.h" +#include "MultiMC.h" + +#include "logger/QsLog.h" + +void JavaCheckerJob::partFinished(JavaCheckResult result) +{ + num_finished++; + QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" + << javacheckers.size(); + emit progress(num_finished, javacheckers.size()); + + javaresults.append(result); + int result_size = javacheckers.size(); + + emit progress(num_finished, result_size); + + if (num_finished == javacheckers.size()) + { + emit finished(javaresults); + } +} + +void JavaCheckerJob::start() +{ + QLOG_INFO() << m_job_name.toLocal8Bit() << " started."; + m_running = true; + for (auto iter : javacheckers) + { + connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); + iter->performCheck(); + } +} diff --git a/logic/JavaCheckerJob.h b/logic/JavaCheckerJob.h new file mode 100644 index 00000000..132a92d4 --- /dev/null +++ b/logic/JavaCheckerJob.h @@ -0,0 +1,100 @@ +/* Copyright 2013 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. + */ + +#pragma once + +#include +#include +#include "JavaChecker.h" +#include "logic/tasks/ProgressProvider.h" + +class JavaCheckerJob; +typedef std::shared_ptr JavaCheckerJobPtr; + +class JavaCheckerJob : public ProgressProvider +{ + Q_OBJECT +public: + explicit JavaCheckerJob(QString job_name) : ProgressProvider(), m_job_name(job_name) {}; + + bool addJavaCheckerAction(JavaCheckerPtr base) + { + javacheckers.append(base); + total_progress++; + // if this is already running, the action needs to be started right away! + if (isRunning()) + { + emit progress(current_progress, total_progress); + connect(base.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); + + base->performCheck(); + } + return true; + } + + JavaCheckerPtr operator[](int index) + { + return javacheckers[index]; + } + ; + JavaCheckerPtr first() + { + if (javacheckers.size()) + return javacheckers[0]; + return JavaCheckerPtr(); + } + int size() const + { + return javacheckers.size(); + } + virtual void getProgress(qint64 ¤t, qint64 &total) + { + current = current_progress; + total = total_progress; + } + ; + virtual QString getStatus() const + { + return m_job_name; + } + ; + virtual bool isRunning() const + { + return m_running; + } + ; + +signals: + void started(); + void progress(int current, int total); + void finished(QList); +public +slots: + virtual void start(); + // FIXME: implement + virtual void abort() {}; +private +slots: + void partFinished(JavaCheckResult result); + +private: + QString m_job_name; + QList javacheckers; + QList javaresults; + qint64 current_progress = 0; + qint64 total_progress = 0; + int num_finished = 0; + bool m_running = false; +}; diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index 61d0231a..77972874 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -26,11 +26,24 @@ #include "JavaUtils.h" #include "logger/QsLog.h" #include "gui/dialogs/VersionSelectDialog.h" +#include "JavaCheckerJob.h" +#include "lists/JavaVersionList.h" JavaUtils::JavaUtils() { } +JavaVersionPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch) +{ + JavaVersionPtr javaVersion(new JavaVersion()); + + javaVersion->id = id; + javaVersion->arch = arch; + javaVersion->path = path; + + return javaVersion; +} + JavaVersionPtr JavaUtils::GetDefaultJava() { JavaVersionPtr javaVersion(new JavaVersion()); @@ -38,7 +51,6 @@ JavaVersionPtr JavaUtils::GetDefaultJava() javaVersion->id = "java"; javaVersion->arch = "unknown"; javaVersion->path = "java"; - javaVersion->recommended = false; return javaVersion; } @@ -112,7 +124,6 @@ QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString javaVersion->arch = archType; javaVersion->path = QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"); - javaVersion->recommended = (recommended == subKeyName); javas.append(javaVersion); } @@ -130,7 +141,7 @@ QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString QList JavaUtils::FindJavaPaths() { - QList javas; + QList candidates; QList JRE64s = this->FindJavaFromRegistryKey( KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); @@ -141,30 +152,21 @@ QList JavaUtils::FindJavaPaths() QList JDK32s = this->FindJavaFromRegistryKey( KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); - javas.append(JRE64s); - javas.append(JDK64s); - javas.append(JRE32s); - javas.append(JDK32s); + candidates.append(JRE64s); + candidates.append(JDK64s); + candidates.append(JRE32s); + candidates.append(JDK32s); - if (javas.size() <= 0) - { - QLOG_WARN() << "Failed to find Java in the Windows registry - defaulting to \"java\""; - javas.append(this->GetDefaultJava()); - return javas; - } + candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/java.exe")); + candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/java.exe")); + candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/java.exe")); + candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/java.exe")); - QLOG_INFO() << "Found the following Java installations (64 -> 32, JRE -> JDK): "; + candidates.append(this->GetDefaultJava()); - for (auto &java : javas) - { - QString sRec; - if (java->recommended) - sRec = "(Recommended)"; - QLOG_INFO() << java->id << java->arch << " at " << java->path << sRec; - } - - return javas; + return candidates; } + #elif OSX QList JavaUtils::FindJavaPaths() { diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h index 44f576b4..d056cbbe 100644 --- a/logic/JavaUtils.h +++ b/logic/JavaUtils.h @@ -19,21 +19,23 @@ #include #include - -#include "logic/lists/JavaVersionList.h" +#include "JavaCheckerJob.h" +#include "JavaChecker.h" +#include "lists/JavaVersionList.h" #if WINDOWS #include #endif -class JavaUtils +class JavaUtils : public QObject { + Q_OBJECT public: JavaUtils(); + JavaVersionPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown"); QList FindJavaPaths(); JavaVersionPtr GetDefaultJava(); -private: #if WINDOWS QList FindJavaFromRegistryKey(DWORD keyType, QString keyName); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 44314e1e..b32e833c 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -64,7 +64,8 @@ void OneSixUpdate::executeTask() checker.reset(new JavaChecker()); connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, SLOT(checkFinishedOffline(JavaCheckResult))); - checker->performCheck(java_path); + checker->path = java_path; + checker->performCheck(); return; } @@ -95,7 +96,8 @@ void OneSixUpdate::checkJavaOnline() checker.reset(new JavaChecker()); connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, SLOT(checkFinishedOnline(JavaCheckResult))); - checker->performCheck(java_path); + checker->path = java_path; + checker->performCheck(); } void OneSixUpdate::checkFinishedOnline(JavaCheckResult result) diff --git a/logic/lists/JavaVersionList.cpp b/logic/lists/JavaVersionList.cpp index 3dc1969b..bc177f09 100644 --- a/logic/lists/JavaVersionList.cpp +++ b/logic/lists/JavaVersionList.cpp @@ -21,7 +21,8 @@ #include #include "logger/QsLog.h" -#include +#include "logic/JavaCheckerJob.h" +#include "logic/JavaUtils.h" JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent) { @@ -49,7 +50,7 @@ int JavaVersionList::count() const int JavaVersionList::columnCount(const QModelIndex &parent) const { - return 4; + return 3; } QVariant JavaVersionList::data(const QModelIndex &index, int role) const @@ -75,9 +76,6 @@ QVariant JavaVersionList::data(const QModelIndex &index, int role) const case 2: return version->path; - case 3: - return version->recommended ? tr("Yes") : tr("No"); - default: return QVariant(); } @@ -109,9 +107,6 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i case 2: return "Path"; - case 3: - return "Recommended"; - default: return QVariant(); } @@ -128,9 +123,6 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i case 2: return "Path to this Java version."; - case 3: - return "Whether the version is recommended or not."; - default: return QVariant(); } @@ -142,15 +134,15 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i BaseVersionPtr JavaVersionList::getTopRecommended() const { - for (int i = 0; i < m_vlist.length(); i++) + auto first = m_vlist.first(); + if(first != nullptr) { - auto ver = std::dynamic_pointer_cast(m_vlist.at(i)); - if (ver->recommended) - { - return m_vlist.at(i); - } + return first; + } + else + { + return BaseVersionPtr(); } - return BaseVersionPtr(); } void JavaVersionList::updateListData(QList versions) @@ -182,17 +174,66 @@ void JavaListLoadTask::executeTask() { setStatus("Detecting Java installations..."); + QSet candidate_paths; JavaUtils ju; - QList javas = ju.FindJavaPaths(); + + QList candidates = ju.FindJavaPaths(); + + for(JavaVersionPtr &candidate : candidates) + { + candidate_paths.insert(candidate->path); + } + + auto job = new JavaCheckerJob("Java detection"); + connect(job, SIGNAL(finished(QList)), this, SLOT(javaCheckerFinished(QList))); + connect(job, SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int))); + + for(const QString candidate : candidate_paths) + { + auto candidate_checker = new JavaChecker(); + candidate_checker->path = candidate; + job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); + } + + QLOG_DEBUG() << "Starting java checker job with" << job->size() << "candidates"; + job->start(); +} + +void JavaListLoadTask::checkerProgress(int current, int total) +{ + float progress = (current * 100.0) / (current + total); + this->setProgress((int) progress); +} + +void JavaListLoadTask::javaCheckerFinished(QList results) +{ + QList candidates; + + QLOG_DEBUG() << "Got Java checker results:"; + for(JavaCheckResult result : results) + { + if(result.valid) + { + JavaVersionPtr javaVersion(new JavaVersion()); + + javaVersion->id = result.javaVersion; + javaVersion->arch = result.mojangPlatform; + javaVersion->path = result.path; + candidates.append(javaVersion); + + QLOG_DEBUG() << javaVersion->id << javaVersion->arch << javaVersion->path; + } + } QList javas_bvp; - for (int i = 0; i < javas.length(); i++) + for (auto &java : candidates) { - BaseVersionPtr java = std::dynamic_pointer_cast(javas.at(i)); + //QLOG_INFO() << java->id << java->arch << " at " << java->path; + BaseVersionPtr bp_java = std::dynamic_pointer_cast(java); - if (java) + if (bp_java) { - javas_bvp.append(java); + javas_bvp.append(bp_java); } } diff --git a/logic/lists/JavaVersionList.h b/logic/lists/JavaVersionList.h index f816c932..879b2480 100644 --- a/logic/lists/JavaVersionList.h +++ b/logic/lists/JavaVersionList.h @@ -20,6 +20,7 @@ #include "BaseVersionList.h" #include "logic/tasks/Task.h" +#include "logic/JavaCheckerJob.h" class JavaListLoadTask; @@ -43,7 +44,6 @@ struct JavaVersion : public BaseVersion QString id; QString arch; QString path; - bool recommended; }; typedef std::shared_ptr JavaVersionPtr; @@ -85,6 +85,9 @@ public: ~JavaListLoadTask(); virtual void executeTask(); +public slots: + void javaCheckerFinished(QList results); + void checkerProgress(int current, int total); protected: JavaVersionList *m_list;