diff options
author | TakSuyu <taksuyu@gmail.com> | 2013-07-27 12:55:51 -0700 |
---|---|---|
committer | TakSuyu <taksuyu@gmail.com> | 2013-07-27 12:55:51 -0700 |
commit | dc39d093396c9a0ed4f03d81f95fcc8fa5705b65 (patch) | |
tree | b88bf5fa692de82ce3418bdf5c55e9cd9eff37bc | |
parent | 44498f98945b7501486da35c5fdc32f94a2be080 (diff) | |
parent | a7a84d4dbb58565f108cb0886da6cb786e34d10d (diff) | |
download | MultiMC-dc39d093396c9a0ed4f03d81f95fcc8fa5705b65.tar MultiMC-dc39d093396c9a0ed4f03d81f95fcc8fa5705b65.tar.gz MultiMC-dc39d093396c9a0ed4f03d81f95fcc8fa5705b65.tar.lz MultiMC-dc39d093396c9a0ed4f03d81f95fcc8fa5705b65.tar.xz MultiMC-dc39d093396c9a0ed4f03d81f95fcc8fa5705b65.zip |
Merge remote-tracking branch 'upstream/master'
Conflicts:
gui/mainwindow.cpp
29 files changed, 1589 insertions, 232 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index afcc9587..f8df3260 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ gui/instancedelegate.h gui/versionselectdialog.h gui/lwjglselectdialog.h gui/iconcache.h +gui/instancesettings.h multimc_pragma.h @@ -208,6 +209,7 @@ gui/instancedelegate.cpp gui/versionselectdialog.cpp gui/lwjglselectdialog.cpp gui/iconcache.cpp +gui/instancesettings.cpp java/javautils.cpp java/annotations.cpp @@ -228,6 +230,7 @@ gui/aboutdialog.ui gui/consolewindow.ui gui/versionselectdialog.ui gui/lwjglselectdialog.ui +gui/instancesettings.ui ) diff --git a/MultiMC.pro b/MultiMC.pro index 3f480529..6af1ec0d 100644 --- a/MultiMC.pro +++ b/MultiMC.pro @@ -21,7 +21,8 @@ SOURCES += main.cpp\ data/inifile.cpp \ gui/settingsdialog.cpp \ gui/modeditwindow.cpp \ - util/appsettings.cpp + util/appsettings.cpp \ + gui/instancesettings.cpp HEADERS += gui/mainwindow.h \ data/instancebase.h \ @@ -32,11 +33,13 @@ HEADERS += gui/mainwindow.h \ gui/settingsdialog.h \ gui/modeditwindow.h \ util/apputils.h \ - util/appsettings.h + util/appsettings.h \ + gui/instancesettings.h FORMS += gui/mainwindow.ui \ gui/settingsdialog.ui \ - gui/modeditwindow.ui + gui/modeditwindow.ui \ + gui/instancesettings.ui RESOURCES += \ multimc.qrc diff --git a/asset_test.cpp b/asset_test.cpp index fb797b97..90da314f 100644 --- a/asset_test.cpp +++ b/asset_test.cpp @@ -13,19 +13,11 @@ inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname) return QDomElement(); } -// a job that removes all files from the base folder that don't match the whitelist -// runs in whatever thread owns the queue. it is fast though. -class NukeAndPaveJob: public Job +class ThreadedDeleter : public QThread { + Q_OBJECT public: - explicit NukeAndPaveJob(QString base, QStringList whitelist) - :Job() - { - QDir dir(base); - m_base = dir.absolutePath(); - m_whitelist = whitelist; - }; - virtual void start() + void run() { QDirIterator iter(m_base, QDirIterator::Subdirectories); QStringList nuke_list; @@ -51,13 +43,37 @@ public: f.remove(); } } - emit finish(); }; -private: QString m_base; QStringList m_whitelist; }; +class NukeAndPaveJob: public Job +{ + Q_OBJECT +public: + + explicit NukeAndPaveJob(QString base, QStringList whitelist) + :Job() + { + QDir dir(base); + deleterThread.m_base = dir.absolutePath(); + deleterThread.m_whitelist = whitelist; + }; +public slots: + virtual void start() + { + connect(&deleterThread, SIGNAL(finished()), SLOT(threadFinished())); + deleterThread.start(); + }; + void threadFinished() + { + emit finish(); + } +private: + ThreadedDeleter deleterThread; +}; + class DlMachine : public QObject { Q_OBJECT @@ -84,7 +100,7 @@ public slots: qDebug() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << xmlErrorMsg << ba; } - QRegExp etag_match(".*([a-f0-9]{32}).*"); + //QRegExp etag_match(".*([a-f0-9]{32}).*"); QDomNodeList contents = doc.elementsByTagName("Contents"); JobList *job = new JobList(); diff --git a/gui/instancesettings.cpp b/gui/instancesettings.cpp new file mode 100644 index 00000000..eea61ce8 --- /dev/null +++ b/gui/instancesettings.cpp @@ -0,0 +1,180 @@ +/* Copyright 2013 MultiMC Contributors + * + * Authors: Andrew Okin + * Peterix + * Orochimarufan <orochimarufan.x3@gmail.com> + * + * 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 "instancesettings.h" +#include "ui_instancesettings.h" + +InstanceSettings::InstanceSettings( SettingsObject * obj, QWidget *parent) : + m_obj(obj), + QDialog(parent), + ui(new Ui::InstanceSettings) +{ + ui->setupUi(this); + loadSettings(); +} + +InstanceSettings::~InstanceSettings() +{ + delete ui; +} + +void InstanceSettings::on_customCommandsGroupBox_toggled(bool state) +{ + ui->labelCustomCmdsDescription->setEnabled(state); +} + +void InstanceSettings::on_buttonBox_accepted() +{ + applySettings(); + accept(); +} + +void InstanceSettings::on_buttonBox_rejected() +{ + reject(); +} + + +void InstanceSettings::applySettings() +{ + // Console + bool console = ui->consoleSettingsBox->isChecked(); + m_obj->set("OverrideConsole", console); + if(console) + { + m_obj->set("ShowConsole", ui->showConsoleCheck->isChecked()); + m_obj->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked()); + } + else + { + m_obj->reset("ShowConsole"); + m_obj->reset("AutoCloseConsole"); + } + + // Window Size + bool window = ui->windowSizeGroupBox->isChecked(); + m_obj->set("OverrideWindow", window); + if(window) + { + m_obj->set("LaunchCompatMode", ui->compatModeCheckBox->isChecked()); + m_obj->set("LaunchMaximized", ui->maximizedCheckBox->isChecked()); + m_obj->set("MinecraftWinWidth", ui->windowWidthSpinBox->value()); + m_obj->set("MinecraftWinHeight", ui->windowHeightSpinBox->value()); + } + else + { + m_obj->reset("LaunchCompatMode"); + m_obj->reset("LaunchMaximized"); + m_obj->reset("MinecraftWinWidth"); + m_obj->reset("MinecraftWinHeight"); + } + + + // Auto Login + bool login = ui->accountSettingsGroupBox->isChecked(); + m_obj->set("OverrideLogin", login); + if(login) + { + m_obj->set("AutoLogin", ui->autoLoginChecBox->isChecked()); + } + else + { + m_obj->reset("AutoLogin"); + } + + + // Memory + bool memory = ui->memoryGroupBox->isChecked(); + m_obj->set("OverrideMemory", memory); + if(memory) + { + m_obj->set("MinMemAlloc", ui->minMemSpinBox->value()); + m_obj->set("MaxMemAlloc", ui->maxMemSpinBox->value()); + } + else + { + m_obj->reset("MinMemAlloc"); + m_obj->reset("MaxMemAlloc"); + } + + + // Java Settings + bool java = ui->javaSettingsGroupBox->isChecked(); + m_obj->set("OverrideJava", java); + if(java) + { + m_obj->set("JavaPath", ui->javaPathTextBox->text()); + m_obj->set("JvmArgs", ui->jvmArgsTextBox->text()); + } + else + { + m_obj->reset("JavaPath"); + m_obj->reset("JvmArgs"); + } + + + // Custom Commands + bool custcmd = ui->customCommandsGroupBox->isChecked(); + m_obj->set("OverrideCommands", custcmd); + if(custcmd) + { + m_obj->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text()); + m_obj->set("PostExitCommand", ui->postExitCmdTextBox->text()); + } + else + { + m_obj->reset("PreLaunchCommand"); + m_obj->reset("PostExitCommand"); + } + +} + +void InstanceSettings::loadSettings() +{ + // Console + ui->showConsoleCheck->setChecked(m_obj->get("ShowConsole").toBool()); + ui->autoCloseConsoleCheck->setChecked(m_obj->get("AutoCloseConsole").toBool()); + ui->consoleSettingsBox->setChecked(m_obj->get("OverrideConsole").toBool()); + + // Window Size + ui->compatModeCheckBox->setChecked(m_obj->get("LaunchCompatMode").toBool()); + ui->maximizedCheckBox->setChecked(m_obj->get("LaunchMaximized").toBool()); + ui->windowWidthSpinBox->setValue(m_obj->get("MinecraftWinWidth").toInt()); + ui->windowHeightSpinBox->setValue(m_obj->get("MinecraftWinHeight").toInt()); + ui->windowSizeGroupBox->setChecked(m_obj->get("OverrideWindow").toBool()); + + // Auto Login + ui->autoLoginChecBox->setChecked(m_obj->get("AutoLogin").toBool()); + ui->accountSettingsGroupBox->setChecked(m_obj->get("OverrideLogin").toBool()); + + // Memory + ui->minMemSpinBox->setValue(m_obj->get("MinMemAlloc").toInt()); + ui->maxMemSpinBox->setValue(m_obj->get("MaxMemAlloc").toInt()); + ui->memoryGroupBox->setChecked(m_obj->get("OverrideMemory").toBool()); + + // Java Settings + ui->javaPathTextBox->setText(m_obj->get("JavaPath").toString()); + ui->jvmArgsTextBox->setText(m_obj->get("JvmArgs").toString()); + ui->javaSettingsGroupBox->setChecked(m_obj->get("OverrideJava").toBool()); + + // Custom Commands + ui->preLaunchCmdTextBox->setText(m_obj->get("PreLaunchCommand").toString()); + ui->postExitCmdTextBox->setText(m_obj->get("PostExitCommand").toString()); + ui->customCommandsGroupBox->setChecked(m_obj->get("OverrideCommands").toBool()); +} diff --git a/gui/instancesettings.h b/gui/instancesettings.h new file mode 100644 index 00000000..afbd0c16 --- /dev/null +++ b/gui/instancesettings.h @@ -0,0 +1,34 @@ +#ifndef INSTANCESETTINGS_H +#define INSTANCESETTINGS_H + +#include <QDialog> +#include "settingsobject.h" + +namespace Ui { +class InstanceSettings; +} + +class InstanceSettings : public QDialog +{ + Q_OBJECT + +public: + explicit InstanceSettings(SettingsObject *s, QWidget *parent = 0); + ~InstanceSettings(); + + void updateCheckboxStuff(); + + void applySettings(); + void loadSettings(); + +private slots: + void on_customCommandsGroupBox_toggled(bool arg1); + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + +private: + Ui::InstanceSettings *ui; + SettingsObject * m_obj; +}; + +#endif // INSTANCESETTINGS_H diff --git a/gui/instancesettings.ui b/gui/instancesettings.ui new file mode 100644 index 00000000..16e64100 --- /dev/null +++ b/gui/instancesettings.ui @@ -0,0 +1,416 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>InstanceSettings</class> + <widget class="QDialog" name="InstanceSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>453</width> + <height>563</height> + </rect> + </property> + <property name="windowTitle"> + <string/> + </property> + <widget class="QTabWidget" name="settingsTabs"> + <property name="geometry"> + <rect> + <x>9</x> + <y>9</y> + <width>435</width> + <height>516</height> + </rect> + </property> + <property name="tabShape"> + <enum>QTabWidget::Rounded</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="minecraftTab"> + <attribute name="title"> + <string>Minecraft</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="windowSizeGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Window Size</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QCheckBox" name="compatModeCheckBox"> + <property name="text"> + <string>Compatibility mode?</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="maximizedCheckBox"> + <property name="text"> + <string>Start Minecraft maximized?</string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayoutWindowSize"> + <item row="1" column="0"> + <widget class="QLabel" name="labelWindowHeight"> + <property name="text"> + <string>Window height:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelWindowWidth"> + <property name="text"> + <string>Window width:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="windowWidthSpinBox"> + <property name="minimum"> + <number>854</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="singleStep"> + <number>1</number> + </property> + <property name="value"> + <number>854</number> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="windowHeightSpinBox"> + <property name="minimum"> + <number>480</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="value"> + <number>480</number> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="consoleSettingsBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Console Settings</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="showConsoleCheck"> + <property name="text"> + <string>Show console while the game is running?</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="autoCloseConsoleCheck"> + <property name="text"> + <string>Automatically close console when the game quits?</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="accountSettingsGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Account Settings</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QCheckBox" name="autoLoginChecBox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Login automatically when an instance icon is double clicked?</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacerMinecraft"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="javaTab"> + <attribute name="title"> + <string>Java</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QGroupBox" name="memoryGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Memory</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="1" column="1"> + <widget class="QSpinBox" name="maxMemSpinBox"> + <property name="minimum"> + <number>512</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="singleStep"> + <number>128</number> + </property> + <property name="value"> + <number>1024</number> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelMinMem"> + <property name="text"> + <string>Minimum memory allocation:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelMaxMem"> + <property name="text"> + <string>Maximum memory allocation:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="minMemSpinBox"> + <property name="minimum"> + <number>256</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + <property name="singleStep"> + <number>128</number> + </property> + <property name="value"> + <number>256</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="javaSettingsGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Java Settings</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QLabel" name="labelJavaPath"> + <property name="text"> + <string>Java path:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="javaPathTextBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelJVMArgs"> + <property name="text"> + <string>JVM arguments:</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>Auto-detect</string> + </property> + </widget> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QLineEdit" name="jvmArgsTextBox"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="customCommandsGroupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Custom Commands</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="1"> + <widget class="QLineEdit" name="preLaunchCmdTextBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelPostExitCmd"> + <property name="text"> + <string>Post-exit command:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelPreLaunchCmd"> + <property name="text"> + <string>Pre-launch command:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="postExitCmdTextBox"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="labelCustomCmdsDescription"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="geometry"> + <rect> + <x>9</x> + <y>530</y> + <width>435</width> + <height>23</height> + </rect> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <tabstops> + <tabstop>settingsTabs</tabstop> + <tabstop>windowSizeGroupBox</tabstop> + <tabstop>compatModeCheckBox</tabstop> + <tabstop>maximizedCheckBox</tabstop> + <tabstop>windowWidthSpinBox</tabstop> + <tabstop>windowHeightSpinBox</tabstop> + <tabstop>consoleSettingsBox</tabstop> + <tabstop>showConsoleCheck</tabstop> + <tabstop>autoCloseConsoleCheck</tabstop> + <tabstop>accountSettingsGroupBox</tabstop> + <tabstop>autoLoginChecBox</tabstop> + <tabstop>memoryGroupBox</tabstop> + <tabstop>minMemSpinBox</tabstop> + <tabstop>maxMemSpinBox</tabstop> + <tabstop>javaSettingsGroupBox</tabstop> + <tabstop>javaPathTextBox</tabstop> + <tabstop>pushButton</tabstop> + <tabstop>jvmArgsTextBox</tabstop> + <tabstop>customCommandsGroupBox</tabstop> + <tabstop>preLaunchCmdTextBox</tabstop> + <tabstop>postExitCmdTextBox</tabstop> + <tabstop>buttonBox</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 80f737e3..f9398c8b 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -43,6 +43,7 @@ #include "gui/lwjglselectdialog.h" #include "gui/consolewindow.h" #include "gui/legacymodeditdialog.h" +#include "gui/instancesettings.h" #include "kcategorizedview.h" #include "kcategorydrawer.h" @@ -130,6 +131,9 @@ MainWindow::MainWindow ( QWidget *parent ) : view->setModel ( proxymodel ); connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(instanceActivated(const QModelIndex &))); + + connect(view, SIGNAL(clicked(const QModelIndex &)), + this, SLOT(instanceChanged(const QModelIndex &))); // Load the instances. instList.loadList(); @@ -417,7 +421,7 @@ void MainWindow::onLoginComplete(LoginResponse response) { Q_ASSERT_X(m_activeInst != NULL, "onLoginComplete", "no active instance is set"); - if (!m_activeInst->shouldUpdateGame()) + if (!m_activeInst->shouldUpdate()) { launchInstance(m_activeInst, response); } @@ -554,3 +558,20 @@ void MainWindow::on_actionChangeInstLWJGLVersion_triggered() } } + +void MainWindow::on_actionInstanceSettings_triggered() +{ + if (view->selectionModel()->selectedIndexes().count() < 1) + return; + + Instance *inst = selectedInstance(); + SettingsObject *s; + s = &inst->settings(); + InstanceSettings settings(s, this); + settings.setWindowTitle(QString("Instance settings")); + settings.exec(); +} + +void MainWindow::instanceChanged(QModelIndex idx) { + ui->instanceToolBar->setEnabled(idx.isValid()); +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h index f13d9395..a10d570c 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -108,8 +108,12 @@ private slots: void on_actionChangeInstLWJGLVersion_triggered(); + void on_actionInstanceSettings_triggered(); + public slots: void instanceActivated ( QModelIndex ); + + void instanceChanged ( QModelIndex ); void startTask(Task *task); diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 771e7096..e6a82635 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -65,6 +65,9 @@ </widget> <widget class="QStatusBar" name="statusBar"/> <widget class="QToolBar" name="instanceToolBar"> + <property name="enabled"> + <bool>true</bool> + </property> <property name="windowTitle"> <string>Instance Toolbar</string> </property> @@ -300,7 +303,7 @@ </action> <action name="actionInstanceSettings"> <property name="enabled"> - <bool>false</bool> + <bool>true</bool> </property> <property name="text"> <string>Settings</string> diff --git a/libmultimc/CMakeLists.txt b/libmultimc/CMakeLists.txt index 0209f8a4..8adee1ec 100644 --- a/libmultimc/CMakeLists.txt +++ b/libmultimc/CMakeLists.txt @@ -32,6 +32,9 @@ include/instversionlist.h include/minecraftversion.h include/minecraftversionlist.h +include/library.h +include/fullversion.h +include/fullversionfactory.h # Tasks include/task.h @@ -63,6 +66,9 @@ src/instversionlist.cpp src/minecraftversion.cpp src/minecraftversionlist.cpp +src/library.cpp +src/fullversion.cpp +src/fullversionfactory.cpp # Tasks src/task.cpp diff --git a/libmultimc/include/fullversion.h b/libmultimc/include/fullversion.h new file mode 100644 index 00000000..523ac87c --- /dev/null +++ b/libmultimc/include/fullversion.h @@ -0,0 +1,69 @@ +#pragma once +#include <QString> + +class Library; + +class FullVersion +{ +public: + FullVersion() + { + minimumLauncherVersion = 0xDEADBEEF; + isLegacy = false; + } + // the ID - determines which jar to use! ACTUALLY IMPORTANT! + QString id; + // do we actually care about parsing this? + QString time; + // I don't think we do. + QString releaseTime; + // eh, not caring - "release" or "snapshot" + QString type; + /* + * DEPRECATED: Old versions of the new vanilla launcher used this + * ex: "username_session_version" + */ + QString processArguments; + /* + * arguments that should be used for launching minecraft + * + * ex: "--username ${auth_player_name} --session ${auth_session} + * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" + */ + QString minecraftArguments; + /* + * the minimum launcher version required by this version ... current is 4 (at point of writing) + */ + int minimumLauncherVersion; + /* + * The main class to load first + */ + QString mainClass; + + // the list of libs. just the names for now. expand to full-blown strutures! + QList<QSharedPointer<Library> > libraries; + + // is this actually a legacy version? if so, none of the other stuff here will be ever used. + // added by FullVersionFactory + bool isLegacy; + +/* +FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. + + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "osx", + "version": "^10\\.5\\.\\d$" + } + } + ], + "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" +} +*/ + // QList<Rule> rules; +};
\ No newline at end of file diff --git a/libmultimc/include/fullversionfactory.h b/libmultimc/include/fullversionfactory.h new file mode 100644 index 00000000..60e5c983 --- /dev/null +++ b/libmultimc/include/fullversionfactory.h @@ -0,0 +1,25 @@ +#pragma once +#include <QtCore> + +struct FullVersion; +class Rule; + +class FullVersionFactory +{ +public: + enum Error + { + AllOK, // all parsed OK + ParseError, // the file was corrupted somehow + UnsupportedVersion // the file was meant for a launcher version we don't support (yet) + } m_error; + QString error_string; + +public: + FullVersionFactory(); + QSharedPointer<FullVersion> parse(QByteArray data); +private: + QSharedPointer<FullVersion> parse4(QJsonObject root, QSharedPointer<FullVersion> product); + QList<QSharedPointer<Rule> > parse4rules(QJsonObject & baseObj); + QStringList legacyWhitelist; +};
\ No newline at end of file diff --git a/libmultimc/include/gameupdatetask.h b/libmultimc/include/gameupdatetask.h index c3f84356..b56c448b 100644 --- a/libmultimc/include/gameupdatetask.h +++ b/libmultimc/include/gameupdatetask.h @@ -22,47 +22,15 @@ #include <QNetworkAccessManager> #include <QUrl> +#include "dlqueue.h" #include "task.h" #include "loginresponse.h" #include "instance.h" #include "libmmc_config.h" -class FileToDownload; -typedef QSharedPointer<FileToDownload> FileToDownloadPtr; - -class FileToDownload : public QObject -{ - Q_OBJECT - - /*! - * The URL to download the file from. - */ - Q_PROPERTY(QUrl url READ url WRITE setURL) - - /*! - * The path to download to. - * This path is relative to the instance's root directory. - */ - Q_PROPERTY(QString path READ path WRITE setPath) - -private: - FileToDownload(const QUrl &url, const QString &path, QObject *parent = 0); -public: - static FileToDownloadPtr Create(const QUrl &url, const QString &path, QObject *parent = 0); - - virtual QUrl url() const { return m_dlURL; } - virtual void setURL(const QUrl &url) { m_dlURL = url; } - - virtual QString path() const { return m_dlPath; } - virtual void setPath(const QString &path) { m_dlPath = path; } - -private: - QUrl m_dlURL; - QString m_dlPath; -}; - +class MinecraftVersion; /*! * The game update task is the task that handles downloading instances' files. @@ -92,9 +60,6 @@ public: virtual void executeTask(); - virtual bool downloadFile(const FileToDownloadPtr file); - - ////////////////////// // STATE AND STATUS // ////////////////////// @@ -110,6 +75,10 @@ public: */ virtual QString getStateMessage(int state); +private: + void getLegacyJar(); + void determineNewVersion(); + public slots: /*! @@ -122,7 +91,15 @@ public slots: private slots: - virtual void updateDownloadProgress(qint64 current, qint64 total); + void updateDownloadProgress(qint64 current, qint64 total); + void legacyJarFinished(); + void legacyJarFailed(); + + void versionFileFinished(); + void versionFileFailed(); + + void jarlibFinished(); + void jarlibFailed(); signals: /*! @@ -143,23 +120,8 @@ private: /////////// Instance *m_inst; - LoginResponse m_response; - QNetworkAccessManager *netMgr; - - - - //////////////////////// - // FILE DOWNLOAD LIST // - //////////////////////// - - // List of URLs that the game updater will need to download. - QList<FileToDownloadPtr> m_downloadList; - int m_currentDownload; - - - //////////////////////////// // STATE AND STATUS STUFF // //////////////////////////// @@ -184,6 +146,13 @@ private: // Finished StateFinished }; + JobListPtr legacyDownloadJob; + JobListPtr specificVersionDownloadJob; + JobListPtr jarlibDownloadJob; + JobListQueue download_queue; + + // target version, determined during this task + MinecraftVersion *targetVersion; }; diff --git a/libmultimc/include/instance.h b/libmultimc/include/instance.h index 717f8816..f6857cd8 100644 --- a/libmultimc/include/instance.h +++ b/libmultimc/include/instance.h @@ -80,16 +80,7 @@ class LIBMULTIMC_EXPORT Instance : public QObject * This returns true if shouldForceUpdate game is true or if the intended and * current versions don't match. */ - Q_PROPERTY(bool shouldUpdateGame READ shouldUpdateGame STORED false) - - /*! - * Whether or not the game will be forced to update on the next launch. - * If this is set to true, shouldUpdateGame will be true, regardless of whether or not - * the current and intended versions match. - * It should be noted that this is set to false automatically when game updates are run. - */ - Q_PROPERTY(bool shouldForceUpdateGame READ shouldForceUpdateGame WRITE setShouldForceUpdateGame) - + Q_PROPERTY(bool shouldUpdate READ shouldUpdate WRITE setShouldUpdate) /*! * The instance's current version. @@ -125,8 +116,11 @@ class LIBMULTIMC_EXPORT Instance : public QObject */ Q_PROPERTY(qint64 lastCurrentVersionUpdate READ lastCurrentVersionUpdate WRITE setLastCurrentVersionUpdate) - - + /*! + * Is the instance a new launcher instance? Get/Set + */ + Q_PROPERTY(bool isForNewLauncher READ isForNewLauncher WRITE setIsForNewLauncher) + // Dirs //! Path to the instance's .minecraft folder. Q_PROPERTY(QString minecraftDir READ minecraftDir STORED false) @@ -236,14 +230,17 @@ public: virtual QString intendedVersion() const { return settings().get("IntendedJarVersion").toString(); } virtual void setIntendedVersion(QString val) { settings().set("IntendedJarVersion", val); } - virtual bool shouldUpdateGame() const - { return shouldForceUpdateGame() || intendedVersion() != currentVersion(); } - - virtual bool shouldForceUpdateGame() const { return settings().get("ShouldForceUpdate").toBool(); } - virtual void setShouldForceUpdateGame(bool val) { settings().set("ShouldForceUpdate", val); } - - - + virtual bool shouldUpdate() const + { + QVariant var = settings().get("ShouldUpdate"); + if(!var.isValid() || var.toBool() == false) + { + return intendedVersion() != currentVersion(); + } + return true; + } + virtual void setShouldUpdate(bool val) { settings().set("ShouldUpdate", val); } + //// Timestamps //// virtual qint64 lastLaunch() const { return settings().get("lastLaunchTime").value<qint64>(); } @@ -256,6 +253,15 @@ public: virtual qint64 lastCurrentVersionUpdate() const { return settings().get("lastVersionUpdate").value<qint64>(); } virtual void setLastCurrentVersionUpdate(qint64 val) { settings().set("lastVersionUpdate", val); } + virtual bool isForNewLauncher() + { + return settings().get("IsForNewLauncher").value<bool>(); + } + + virtual void setIsForNewLauncher(bool value = true) + { + settings().set("IsForNewLauncher", value); + } ////// Directories ////// QString minecraftDir() const; diff --git a/libmultimc/include/library.h b/libmultimc/include/library.h new file mode 100644 index 00000000..8d97b4a6 --- /dev/null +++ b/libmultimc/include/library.h @@ -0,0 +1,219 @@ +/* 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 <QtCore> + +class Library; + +enum OpSys +{ + Os_Windows, + Os_Linux, + Os_OSX, + Os_Other +}; + +OpSys OpSys_fromString(QString); + +#ifdef Q_OS_MAC + #define currentSystem Os_OSX +#endif + +#ifdef Q_OS_LINUX + #define currentSystem Os_Linux +#endif + +#ifdef Q_OS_WIN32 + #define currentSystem Os_Windows +#endif + +#ifndef currentSystem + #define currentSystem Os_Other +#endif + + +enum RuleAction +{ + Allow, + Disallow, + Defer +}; + +RuleAction RuleAction_fromString(QString); + +class Rule +{ +protected: + RuleAction m_result; + virtual bool applies(Library * parent) = 0; +public: + Rule(RuleAction result) + :m_result(result) {} + virtual ~Rule(){}; + RuleAction apply(Library * parent) + { + if(applies(parent)) + return m_result; + else + return Defer; + }; +}; + +class OsRule : public Rule +{ +private: + // the OS + OpSys m_system; + // the OS version regexp + QString m_version_regexp; +protected: + virtual bool applies ( Library* ) + { + return (m_system == currentSystem); + } + OsRule(RuleAction result, OpSys system, QString version_regexp) + : Rule(result), m_system(system), m_version_regexp(version_regexp) {} +public: + static QSharedPointer<OsRule> create(RuleAction result, OpSys system, QString version_regexp) + { + return QSharedPointer<OsRule> (new OsRule(result, system, version_regexp)); + } +}; + +class ImplicitRule : public Rule +{ +protected: + virtual bool applies ( Library* ) + { + return true; + } + ImplicitRule(RuleAction result) + : Rule(result) {} +public: + static QSharedPointer<ImplicitRule> create(RuleAction result) + { + return QSharedPointer<ImplicitRule> (new ImplicitRule(result)); + } +}; + +class Library +{ +private: + // basic values used internally (so far) + QString m_name; + QString m_base_url; + QList<QSharedPointer<Rule> > m_rules; + + // derived values used for real things + /// where to store the lib locally + QString m_storage_path; + /// where to download the lib from + QString m_download_path; + /// is this lib actuall active on the current OS? + bool m_is_active; + + // native lib? + bool m_is_native; + QMap<OpSys, QString> m_native_suffixes; +public: + QStringList extract_excludes; + +public: + /// finalize the library, processing the input values into derived values and state + void finalize() + { + QStringList parts = m_name.split(':'); + QString relative = parts[0]; + relative.replace('.','/'); + relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; + if(!m_is_native) + relative += ".jar"; + else + { + if(m_native_suffixes.contains(currentSystem)) + { + relative += "-" + m_native_suffixes[currentSystem] + ".jar"; + } + else + { + // really, bad. + relative += ".jar"; + } + } + m_storage_path = relative; + m_download_path = m_base_url + relative; + + if(m_rules.empty()) + { + m_is_active = true; + } + else + { + RuleAction result = Disallow; + for(auto rule: m_rules) + { + RuleAction temp = rule->apply( this ); + if(temp != Defer) + result = temp; + } + m_is_active = (result == Allow); + } + if(m_is_native) + { + m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); + } + }; + + Library(QString name) + { + m_is_native = false; + m_is_native = false; + m_name = name; + m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; + } + + void setName(QString name) + { + m_name = name; + } + + void setBaseUrl(QString base_url) + { + m_base_url = base_url; + } + + void setIsNative() + { + m_is_native = true; + } + + void addNative(OpSys os, QString suffix) + { + m_is_native = true; + m_native_suffixes[os] = suffix; + } + + void setRules(QList<QSharedPointer<Rule> > rules) + { + m_rules = rules; + } + + bool applies() + { + return m_is_active; + } +}; diff --git a/libmultimc/src/fullversion.cpp b/libmultimc/src/fullversion.cpp new file mode 100644 index 00000000..53664c2a --- /dev/null +++ b/libmultimc/src/fullversion.cpp @@ -0,0 +1,5 @@ +#include <QtCore> +#include "fullversion.h" +#include <library.h> + +// ECHO, echo, echo, ....
\ No newline at end of file diff --git a/libmultimc/src/fullversionfactory.cpp b/libmultimc/src/fullversionfactory.cpp new file mode 100644 index 00000000..bb55b4a9 --- /dev/null +++ b/libmultimc/src/fullversionfactory.cpp @@ -0,0 +1,206 @@ +#include "fullversionfactory.h" +#include "fullversion.h" +#include <library.h> + +class LibraryFinalizer +{ +public: + LibraryFinalizer(QSharedPointer<Library> library) + { + m_library = library; + } + + QSharedPointer<Library> m_library; +}; + +// Library rules (if any) +QList<QSharedPointer<Rule> > FullVersionFactory::parse4rules(QJsonObject & baseObj) +{ + QList<QSharedPointer<Rule> > rules; + auto rulesVal = baseObj.value("rules"); + if(rulesVal.isArray()) + { + QJsonArray ruleList = rulesVal.toArray(); + for(auto ruleVal : ruleList) + { + QSharedPointer<Rule> rule; + if(!ruleVal.isObject()) + continue; + auto ruleObj = ruleVal.toObject(); + auto actionVal = ruleObj.value("action"); + if(!actionVal.isString()) + continue; + auto action = RuleAction_fromString(actionVal.toString()); + if(action == Defer) + continue; + + auto osVal = ruleObj.value("os"); + if(!osVal.isObject()) + { + // add a new implicit action rule + rules.append(ImplicitRule::create(action)); + } + else + { + auto osObj = osVal.toObject(); + auto osNameVal = osObj.value("name"); + if(!osNameVal.isString()) + continue; + OpSys requiredOs = OpSys_fromString(osNameVal.toString()); + QString versionRegex = osObj.value("version").toString(); + // add a new OS rule + rules.append(OsRule::create(action, requiredOs, versionRegex)); + } + } + } + return rules; +} + + +QSharedPointer<FullVersion> FullVersionFactory::parse4(QJsonObject root, QSharedPointer<FullVersion> fullVersion) +{ + fullVersion->id = root.value("id").toString(); + + // if it's on our legacy list, it's legacy + if(legacyWhitelist.contains(fullVersion->id)) + fullVersion->isLegacy = true; + + fullVersion->mainClass = root.value("mainClass").toString(); + auto procArgsValue = root.value("processArguments"); + if(procArgsValue.isString()) + { + fullVersion->processArguments = procArgsValue.toString(); + QString toCompare = fullVersion->processArguments.toLower(); + if(toCompare == "legacy") + { + fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}"; + fullVersion->isLegacy = true; + } + else if(toCompare == "username_session") + { + fullVersion->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; + } + else if(toCompare == "username_session_version") + { + fullVersion->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; + } + } + + auto minecraftArgsValue = root.value("minecraftArguments"); + if(minecraftArgsValue.isString()) + { + fullVersion->minecraftArguments = minecraftArgsValue.toString(); + } + + fullVersion->releaseTime = root.value("releaseTime").toString(); + fullVersion->time = root.value("time").toString(); + + // Iterate through the list, if it's a list. + auto librariesValue = root.value("libraries"); + if(!librariesValue.isArray()) + return fullVersion; + + QJsonArray libList = root.value("libraries").toArray(); + for (auto libVal : libList) + { + if (!libVal.isObject()) + { + continue; + } + + QJsonObject libObj = libVal.toObject(); + + // Library name + auto nameVal = libObj.value("name"); + if(!nameVal.isString()) + continue; + QSharedPointer<Library> library(new Library(nameVal.toString())); + + // Extract excludes (if any) + auto extractVal = libObj.value("extract"); + if(extractVal.isObject()) + { + QStringList excludes; + auto extractObj = extractVal.toObject(); + auto excludesVal = extractObj.value("exclude"); + if(!excludesVal.isArray()) + goto SKIP_EXTRACTS; + auto excludesList = excludesVal.toArray(); + for(auto excludeVal : excludesList) + { + if(excludeVal.isString()) + excludes.append(excludeVal.toString()); + } + library->extract_excludes = excludes; + } + SKIP_EXTRACTS: + + auto nativesVal = libObj.value("natives"); + if(nativesVal.isObject()) + { + library->setIsNative(); + auto nativesObj = nativesVal.toObject(); + auto iter = nativesObj.begin(); + while(iter != nativesObj.end()) + { + auto osType = OpSys_fromString(iter.key()); + if(osType == Os_Other) + continue; + if(!iter.value().isString()) + continue; + library->addNative(osType, iter.value().toString()); + iter++; + } + } + library->setRules(parse4rules(libObj)); + library->finalize(); + fullVersion->libraries.append(library); + } + return fullVersion; +} + +QSharedPointer<FullVersion> FullVersionFactory::parse(QByteArray data) +{ + QSharedPointer<FullVersion> readVersion(new FullVersion()); + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + + if (jsonError.error != QJsonParseError::NoError) + { + error_string = QString( "Error reading version file :") + " " + jsonError.errorString(); + m_error = FullVersionFactory::ParseError; + return QSharedPointer<FullVersion>(); + } + + if(!jsonDoc.isObject()) + { + error_string = "Error reading version file."; + m_error = FullVersionFactory::ParseError; + return QSharedPointer<FullVersion>(); + } + QJsonObject root = jsonDoc.object(); + + readVersion->minimumLauncherVersion = root.value("minimumLauncherVersion").toDouble(); + switch(readVersion->minimumLauncherVersion) + { + case 1: + case 2: + case 3: + case 4: + return parse4(root, readVersion); + // ADD MORE HERE :D + default: + error_string = "Version file was for an unrecognized launcher version. RIP"; + m_error = FullVersionFactory::UnsupportedVersion; + return QSharedPointer<FullVersion>(); + } +} + + +FullVersionFactory::FullVersionFactory() +{ + m_error = FullVersionFactory::AllOK; + legacyWhitelist.append("1.5.1"); + legacyWhitelist.append("1.5.2"); +} diff --git a/libmultimc/src/gameupdatetask.cpp b/libmultimc/src/gameupdatetask.cpp index c718ce71..bae85c17 100644 --- a/libmultimc/src/gameupdatetask.cpp +++ b/libmultimc/src/gameupdatetask.cpp @@ -25,40 +25,34 @@ #include <QDebug> #include "minecraftversionlist.h" +#include "fullversionfactory.h" +#include <fullversion.h> #include "pathutils.h" -#include "netutils.h" + GameUpdateTask::GameUpdateTask(const LoginResponse &response, Instance *inst, QObject *parent) : Task(parent), m_response(response) { m_inst = inst; m_updateState = StateInit; - m_currentDownload = 0; } void GameUpdateTask::executeTask() { updateStatus(); - QNetworkAccessManager networkMgr; - netMgr = &networkMgr; - // Get a pointer to the version object that corresponds to the instance's version. - MinecraftVersion *targetVersion = (MinecraftVersion *)MinecraftVersionList::getMainList(). + targetVersion = (MinecraftVersion *)MinecraftVersionList::getMainList(). findVersion(m_inst->intendedVersion()); - Q_ASSERT_X(targetVersion != NULL, "game update", "instance's intended version is not an actual version"); - - // Make directories - QDir binDir(m_inst->binDir()); - if (!binDir.exists() && !binDir.mkpath(".")) + if(targetVersion == NULL) { - error("Failed to create bin folder."); + //Q_ASSERT_X(targetVersion != NULL, "game update", "instance's intended version is not an actual version"); + setState(StateFinished); + emit gameUpdateComplete(m_response); return; } - - ///////////////////////// // BUILD DOWNLOAD LIST // ///////////////////////// @@ -66,90 +60,150 @@ void GameUpdateTask::executeTask() setState(StateDetermineURLs); + if (targetVersion->launcherVersion() == MinecraftVersion::Launcher16) + { + determineNewVersion(); + } + else + { + getLegacyJar(); + } + QEventLoop loop; + loop.exec(); +} + +void GameUpdateTask::determineNewVersion() +{ + QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); + urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; + auto dljob = DownloadJob::create(QUrl(urlstr)); + specificVersionDownloadJob.reset(new JobList()); + specificVersionDownloadJob->add(dljob); + connect(specificVersionDownloadJob.data(), SIGNAL(finished()), SLOT(versionFileFinished())); + connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); + connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + download_queue.enqueue(specificVersionDownloadJob); +} + +void GameUpdateTask::versionFileFinished() +{ + JobPtr firstJob = specificVersionDownloadJob->getFirstJob(); + auto DlJob = firstJob.dynamicCast<DownloadJob>(); + FullVersionFactory parser; + auto version = parser.parse(DlJob->m_data); - // Add the URL for minecraft.jar + if(!version) + { + error(parser.error_string); + exit(0); + } + + if(version->isLegacy) + { + getLegacyJar(); + return; + } + // save the version file in $instanceId/version.json and versions/$version/$version.json + QString version_id = targetVersion->descriptor(); + QString mc_dir = m_inst->minecraftDir(); + QString inst_dir = m_inst->rootDir(); + QString version1 = PathCombine(inst_dir, "/version.json"); + QString version2 = QString("versions/") + version_id + "/" + version_id + ".json"; + DownloadJob::ensurePathExists(version1); + DownloadJob::ensurePathExists(version2); + QFile vfile1 (version1); + QFile vfile2 (version2); + vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly ); + vfile2.open(QIODevice::Truncate | QIODevice::WriteOnly ); + vfile1.write(DlJob->m_data); + vfile2.write(DlJob->m_data); + vfile1.close(); + vfile2.close(); + + // download the right jar, save it in versions/$version/$version.jar + QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); + urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".jar"; + QString targetstr ("versions/"); + targetstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".jar"; + auto dljob = DownloadJob::create(QUrl(urlstr), targetstr); + + jarlibDownloadJob.reset(new JobList()); + jarlibDownloadJob->add(dljob); + connect(jarlibDownloadJob.data(), SIGNAL(finished()), SLOT(jarlibFinished())); + connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); + connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + // determine and download all the libraries, save them in libraries/whatever... + download_queue.enqueue(jarlibDownloadJob); +} + +void GameUpdateTask::jarlibFinished() +{ + m_inst->setCurrentVersion(targetVersion->descriptor()); + m_inst->setShouldUpdate(false); + m_inst->setIsForNewLauncher(true); + exit(1); +} + +void GameUpdateTask::jarlibFailed() +{ + error("Failed to download the binary garbage. Try again. Maybe. IF YOU DARE"); + exit(0); +} + +void GameUpdateTask::versionFileFailed() +{ + error("Failed to download the version description. Try again."); + exit(0); +} + + +// this is legacy minecraft... +void GameUpdateTask::getLegacyJar() +{ + // Make directories + QDir binDir(m_inst->binDir()); + if (!binDir.exists() && !binDir.mkpath(".")) + { + error("Failed to create bin folder."); + return; + } + + // Add the URL for minecraft.jar // This will be either 'minecraft' or the version number, depending on where // we're downloading from. QString jarFilename = "minecraft"; - - // FIXME: this is NOT enough if (targetVersion->launcherVersion() == MinecraftVersion::Launcher16) + { jarFilename = targetVersion->descriptor(); + } QUrl mcJarURL = targetVersion->downloadURL() + jarFilename + ".jar"; qDebug() << mcJarURL.toString(); - m_downloadList.append(FileToDownload::Create(mcJarURL, PathCombine(m_inst->minecraftDir(), "bin/minecraft.jar"))); - - + auto dljob = DownloadJob::create(mcJarURL, PathCombine(m_inst->minecraftDir(), "bin/minecraft.jar")); - //////////////////// - // DOWNLOAD FILES // - //////////////////// - setState(StateDownloadFiles); - for (int i = 0; i < m_downloadList.length(); i++) - { - m_currentDownload = i; - if (!downloadFile(m_downloadList[i])) - return; - } - - - - /////////////////// - // INSTALL FILES // - /////////////////// - setState(StateInstall); - - // Nothing to do here yet + legacyDownloadJob.reset(new JobList()); + legacyDownloadJob->add(dljob); + connect(legacyDownloadJob.data(), SIGNAL(finished()), SLOT(legacyJarFinished())); + connect(legacyDownloadJob.data(), SIGNAL(failed()), SLOT(legacyJarFailed())); + connect(legacyDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); - - - ////////////// - // FINISHED // - ////////////// + download_queue.enqueue(legacyDownloadJob); +} + + +void GameUpdateTask::legacyJarFinished() +{ setState(StateFinished); emit gameUpdateComplete(m_response); + m_inst->setIsForNewLauncher(true); + exit(1); } -bool GameUpdateTask::downloadFile( const FileToDownloadPtr file ) +void GameUpdateTask::legacyJarFailed() { - setSubStatus("Downloading " + file->url().toString()); - QNetworkReply *reply = netMgr->get(QNetworkRequest(file->url())); - - this->connect(reply, SIGNAL(downloadProgress(qint64,qint64)), - SLOT(updateDownloadProgress(qint64,qint64))); - - NetUtils::waitForNetRequest(reply); - - if (reply->error() == QNetworkReply::NoError) - { - QString filePath = file->path(); - QFile outFile(filePath); - if (outFile.exists() && !outFile.remove()) - { - error("Can't delete old file " + file->path() + ": " + outFile.errorString()); - return false; - } - - if (!outFile.open(QIODevice::WriteOnly)) - { - error("Can't write to " + file->path() + ": " + outFile.errorString()); - return false; - } - - outFile.write(reply->readAll()); - outFile.close(); - } - else - { - error("Can't download " + file->url().toString() + ": " + reply->errorString()); - return false; - } - - // TODO: Check file integrity after downloading. - - return true; + emit gameUpdateError("failed to download the minecraft.jar"); + exit(0); } int GameUpdateTask::state() const @@ -223,22 +277,7 @@ void GameUpdateTask::error(const QString &msg) void GameUpdateTask::updateDownloadProgress(qint64 current, qint64 total) { // The progress on the current file is current / total - float currentDLProgress = (float) current / (float) total; // Cast ALL the values! - - // The overall progress is (current progress + files downloaded) / total files to download - float overallDLProgress = ((currentDLProgress + m_currentDownload) / (float) m_downloadList.length()); - - // Multiply by 100 to make it a percentage. - setProgress((int)(overallDLProgress * 100)); -} - -FileToDownloadPtr FileToDownload::Create(const QUrl &url, const QString &path, QObject *parent) -{ - return FileToDownloadPtr(new FileToDownload (url, path, parent)); + float currentDLProgress = (float) current / (float) total; + setProgress((int)(currentDLProgress * 100)); // convert to percentage } -FileToDownload::FileToDownload(const QUrl &url, const QString &path, QObject *parent) : - QObject(parent), m_dlURL(url), m_dlPath(path) -{ - -} diff --git a/libmultimc/src/instance.cpp b/libmultimc/src/instance.cpp index 08cd6605..5fdb5064 100644 --- a/libmultimc/src/instance.cpp +++ b/libmultimc/src/instance.cpp @@ -34,7 +34,8 @@ Instance::Instance(const QString &rootDir, QObject *parent) : settings().registerSetting(new Setting("iconKey", "default")); settings().registerSetting(new Setting("notes", "")); settings().registerSetting(new Setting("NeedsRebuild", true)); - settings().registerSetting(new Setting("ShouldForceUpdate", false)); + settings().registerSetting(new Setting("IsForNewLauncher", false)); + settings().registerSetting(new Setting("ShouldUpdate", false)); settings().registerSetting(new Setting("JarVersion", "Unknown")); settings().registerSetting(new Setting("LwjglVersion", "2.9.0")); settings().registerSetting(new Setting("IntendedJarVersion", "")); @@ -62,6 +63,18 @@ Instance::Instance(const QString &rootDir, QObject *parent) : // Auto login settings().registerSetting(new OverrideSetting("AutoLogin", globalSettings->getSetting("AutoLogin"))); + + // Console + settings().registerSetting(new OverrideSetting("ShowConsole", globalSettings->getSetting("ShowConsole"))); + settings().registerSetting(new OverrideSetting("AutoCloseConsole", globalSettings->getSetting("AutoCloseConsole"))); + + // Overrides + settings().registerSetting(new Setting("OverrideConsole", false)); + settings().registerSetting(new Setting("OverrideWindow", false)); + settings().registerSetting(new Setting("OverrideLogin", false)); + settings().registerSetting(new Setting("OverrideMemory", false)); + settings().registerSetting(new Setting("OverrideJava", false)); + settings().registerSetting(new Setting("OverrideCommands", false)); } QString Instance::id() const diff --git a/libmultimc/src/library.cpp b/libmultimc/src/library.cpp new file mode 100644 index 00000000..9d4cc6e3 --- /dev/null +++ b/libmultimc/src/library.cpp @@ -0,0 +1,37 @@ +/* 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 "include/library.h" + +RuleAction RuleAction_fromString(QString name) +{ + if(name == "allow") + return Allow; + if(name == "disallow") + return Disallow; + return Defer; +} + +OpSys OpSys_fromString(QString name) +{ + if(name == "linux") + return Os_Linux; + if(name == "windows") + return Os_Windows; + if(name == "osx") + return Os_OSX; + return Os_Other; +} +// default url for lib: diff --git a/libsettings/include/inisettingsobject.h b/libsettings/include/inisettingsobject.h index 36c8e4bd..03d6fe05 100644 --- a/libsettings/include/inisettingsobject.h +++ b/libsettings/include/inisettingsobject.h @@ -47,6 +47,7 @@ public: protected slots: virtual void changeSetting(const Setting &setting, QVariant value); + virtual void resetSetting ( const Setting& setting ); protected: virtual QVariant retrieveValue(const Setting &setting); diff --git a/libsettings/include/setting.h b/libsettings/include/setting.h index 36709729..a161ab50 100644 --- a/libsettings/include/setting.h +++ b/libsettings/include/setting.h @@ -84,6 +84,12 @@ signals: */ void settingChanged(const Setting &setting, QVariant value); + /*! + * \brief Signal emitted when this Setting object's value resets to default. + * \param setting A reference to the Setting that changed. + */ + void settingReset(const Setting &setting); + public slots: /*! * \brief Changes the setting's value. @@ -93,6 +99,13 @@ public slots: */ virtual void set(QVariant value); + /*! + * \brief Reset the setting to default + * This is done by emitting the settingReset() signal which will then be + * handled by the SettingsObject object and cause the setting to change. + * \param value The new value. + */ + virtual void reset(); protected: QString m_id; QVariant m_defVal; diff --git a/libsettings/include/settingsobject.h b/libsettings/include/settingsobject.h index 23f0d644..a2f03699 100644 --- a/libsettings/include/settingsobject.h +++ b/libsettings/include/settingsobject.h @@ -100,6 +100,11 @@ public: */ virtual bool set(const QString &id, QVariant value); + /*! + * \brief Reverts the setting with the given ID to default. + * \param id The ID of the setting to reset. + */ + virtual void reset(const QString &id) const; /*! * \brief Gets a QList with pointers to all of the registered settings. @@ -125,6 +130,14 @@ signals: */ void settingChanged(const Setting &setting, QVariant value); + /*! + * \brief Signal emitted when one of this SettingsObject object's settings resets. + * This is usually just connected directly to each Setting object's + * settingReset() signals. + * \param setting A reference to the Setting object that changed. + */ + void settingReset(const Setting &setting); + protected slots: /*! * \brief Changes a setting. @@ -136,6 +149,15 @@ protected slots: */ virtual void changeSetting(const Setting &setting, QVariant value) = 0; + /*! + * \brief Resets a setting. + * This slot is usually connected to each Setting object's + * settingReset() signal. The signal is emitted, causing this slot + * to update the setting's value in the config file. + * \param setting A reference to the Setting object that changed. + */ + virtual void resetSetting(const Setting &setting) = 0; + protected: /*! * \brief Connects the necessary signals to the given Setting. diff --git a/libsettings/src/inisettingsobject.cpp b/libsettings/src/inisettingsobject.cpp index 17b132a3..854421b6 100644 --- a/libsettings/src/inisettingsobject.cpp +++ b/libsettings/src/inisettingsobject.cpp @@ -40,6 +40,15 @@ void INISettingsObject::changeSetting(const Setting &setting, QVariant value) } } +void INISettingsObject::resetSetting ( const Setting& setting ) +{ + if (contains(setting.id())) + { + m_ini.remove(setting.configKey()); + m_ini.saveFile(m_filePath); + } +} + QVariant INISettingsObject::retrieveValue(const Setting &setting) { if (contains(setting.id())) diff --git a/libsettings/src/setting.cpp b/libsettings/src/setting.cpp index 1a4f9e13..8e60af06 100644 --- a/libsettings/src/setting.cpp +++ b/libsettings/src/setting.cpp @@ -47,3 +47,8 @@ void Setting::set(QVariant value) { emit settingChanged(*this, value); } + +void Setting::reset() +{ + emit settingReset(*this); +} diff --git a/libsettings/src/settingsobject.cpp b/libsettings/src/settingsobject.cpp index 98c7b479..bf7b8825 100644 --- a/libsettings/src/settingsobject.cpp +++ b/libsettings/src/settingsobject.cpp @@ -49,7 +49,7 @@ bool SettingsObject::registerSetting(Setting *setting) // Connect signals. connectSignals(*setting); - qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8()); + // qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8()); return true; } @@ -98,6 +98,14 @@ bool SettingsObject::set(const QString &id, QVariant value) } } +void SettingsObject::reset(const QString &id) const +{ + Setting *setting = getSetting(id); + if(setting) + setting->reset(); +} + + QList<Setting *> SettingsObject::getSettings() { return m_settings.values(); @@ -115,6 +123,11 @@ void SettingsObject::connectSignals(const Setting &setting) SLOT(changeSetting(const Setting &, QVariant))); connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)), SIGNAL(settingChanged(const Setting &, QVariant))); + + connect(&setting, SIGNAL(settingReset(Setting)), + SLOT(resetSetting(const Setting &))); + connect(&setting, SIGNAL(settingReset(Setting)), + SIGNAL(settingReset(const Setting &))); } void SettingsObject::disconnectSignals(const Setting &setting) @@ -123,4 +136,9 @@ void SettingsObject::disconnectSignals(const Setting &setting) this, SLOT(changeSetting(const Setting &, QVariant))); setting.disconnect(SIGNAL(settingChanged(const Setting &, QVariant)), this, SIGNAL(settingChanged(const Setting &, QVariant))); + + setting.disconnect(SIGNAL(settingReset(const Setting &, QVariant)), + this, SLOT(resetSetting(const Setting &, QVariant))); + setting.disconnect(SIGNAL(settingReset(const Setting &, QVariant)), + this, SIGNAL(settingReset(const Setting &, QVariant))); } diff --git a/libutil/include/dlqueue.h b/libutil/include/dlqueue.h index 9041e762..69fc22a6 100644 --- a/libutil/include/dlqueue.h +++ b/libutil/include/dlqueue.h @@ -5,13 +5,16 @@ /** * A single file for the downloader/cache to process. */ -class DownloadJob : public Job +class LIBUTIL_EXPORT DownloadJob : public Job { Q_OBJECT public: DownloadJob(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); static JobPtr create(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); + +public: + static bool ensurePathExists(QString filenamepath); public slots: virtual void start(); @@ -37,8 +40,10 @@ public: /// save to file? bool m_save_to_file; + /// is the saving file already open? + bool m_opened_for_saving; /// if saving to file, use the one specified in this string - QString m_rel_target_path; + QString m_target_path; /// this is the output file, if any QFile m_output_file; /// if not saving to file, downloaded data is placed here diff --git a/libutil/include/jobqueue.h b/libutil/include/jobqueue.h index 061686f6..26f49307 100644 --- a/libutil/include/jobqueue.h +++ b/libutil/include/jobqueue.h @@ -1,5 +1,6 @@ #pragma once #include <QtCore> +#include "libutil_config.h" enum JobStatus { @@ -11,7 +12,7 @@ enum JobStatus class JobList; -class Job : public QObject +class LIBUTIL_EXPORT Job : public QObject { Q_OBJECT protected: @@ -30,7 +31,7 @@ typedef QSharedPointer<Job> JobPtr; /** * A list of jobs, to be processed one by one. */ -class JobList : public QObject +class LIBUTIL_EXPORT JobList : public QObject { friend class JobListQueue; Q_OBJECT @@ -127,7 +128,7 @@ typedef QSharedPointer<JobList> JobListPtr; /** * A queue of job lists! The job lists fail or finish as units. */ -class JobListQueue : public QObject +class LIBUTIL_EXPORT JobListQueue : public QObject { Q_OBJECT public: diff --git a/libutil/src/dlqueue.cpp b/libutil/src/dlqueue.cpp index dfc51f36..1ef8e212 100644 --- a/libutil/src/dlqueue.cpp +++ b/libutil/src/dlqueue.cpp @@ -1,20 +1,28 @@ #include "include/dlqueue.h" -DownloadJob::DownloadJob ( QUrl url, QString rel_target_path, QString expected_md5 ) +DownloadJob::DownloadJob ( QUrl url, QString target_path, QString expected_md5 ) :Job() { m_url = url; - m_rel_target_path = rel_target_path; + m_target_path = target_path; m_expected_md5 = expected_md5; m_check_md5 = m_expected_md5.size(); - m_save_to_file = m_rel_target_path.size(); + m_save_to_file = m_target_path.size(); m_status = Job_NotStarted; + m_opened_for_saving = false; } -JobPtr DownloadJob::create ( QUrl url, QString rel_target_path, QString expected_md5 ) +JobPtr DownloadJob::create ( QUrl url, QString target_path, QString expected_md5 ) { - return JobPtr ( new DownloadJob ( url, rel_target_path, expected_md5 ) ); + return JobPtr ( new DownloadJob ( url, target_path, expected_md5 ) ); +} + +bool DownloadJob::ensurePathExists(QString filenamepath) +{ + QFileInfo a ( filenamepath ); + QDir dir; + return (dir.mkpath ( a.path() )); } void DownloadJob::start() @@ -22,47 +30,35 @@ void DownloadJob::start() m_manager.reset ( new QNetworkAccessManager() ); if ( m_save_to_file ) { - QString filename = m_rel_target_path; + QString filename = m_target_path; m_output_file.setFileName ( filename ); - // if there already is a file and md5 checking is in effect - if ( m_output_file.exists() && m_check_md5 ) + // if there already is a file and md5 checking is in effect and it can be opened + if ( m_output_file.exists() && m_output_file.open ( QIODevice::ReadOnly ) ) { - // and it can be opened - if ( m_output_file.open ( QIODevice::ReadOnly ) ) + // check the md5 against the expected one + QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); + m_output_file.close(); + // skip this file if they match + if ( m_check_md5 && hash == m_expected_md5 ) { - // check the md5 against the expected one - QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); - m_output_file.close(); - // skip this file if they match - if ( hash == m_expected_md5 ) - { - qDebug() << "Skipping " << m_url.toString() << ": md5 match."; - emit finish(); - return; - } + qDebug() << "Skipping " << m_url.toString() << ": md5 match."; + emit finish(); + return; + } + else + { + m_expected_md5 = hash; } } - QFileInfo a ( filename ); - QDir dir; - if ( !dir.mkpath ( a.path() ) ) - { - /* - * error when making the folder structure - */ - emit fail(); - return; - } - if ( !m_output_file.open ( QIODevice::WriteOnly ) ) + if(!ensurePathExists(filename)) { - /* - * Can't open the file... the job failed - */ emit fail(); return; } } qDebug() << "Downloading " << m_url.toString(); QNetworkRequest request ( m_url ); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); QNetworkReply * rep = m_manager->get ( request ); m_reply = QSharedPointer<QNetworkReply> ( rep, &QObject::deleteLater ); connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); @@ -121,8 +117,21 @@ void DownloadJob::downloadFinished() void DownloadJob::downloadReadyRead() { - if ( m_save_to_file ) + if( m_save_to_file ) { + if(!m_opened_for_saving) + { + if ( !m_output_file.open ( QIODevice::WriteOnly ) ) + { + /* + * Can't open the file... the job failed + */ + m_reply->abort(); + emit fail(); + return; + } + m_opened_for_saving = true; + } m_output_file.write ( m_reply->readAll() ); } } |