Merge remote-tracking branch 'upstream/master'

Conflicts:
	gui/mainwindow.cpp
This commit is contained in:
TakSuyu 2013-07-27 12:55:51 -07:00
commit dc39d09339
29 changed files with 1598 additions and 241 deletions

View File

@ -176,6 +176,7 @@ gui/instancedelegate.h
gui/versionselectdialog.h gui/versionselectdialog.h
gui/lwjglselectdialog.h gui/lwjglselectdialog.h
gui/iconcache.h gui/iconcache.h
gui/instancesettings.h
multimc_pragma.h multimc_pragma.h
@ -208,6 +209,7 @@ gui/instancedelegate.cpp
gui/versionselectdialog.cpp gui/versionselectdialog.cpp
gui/lwjglselectdialog.cpp gui/lwjglselectdialog.cpp
gui/iconcache.cpp gui/iconcache.cpp
gui/instancesettings.cpp
java/javautils.cpp java/javautils.cpp
java/annotations.cpp java/annotations.cpp
@ -228,6 +230,7 @@ gui/aboutdialog.ui
gui/consolewindow.ui gui/consolewindow.ui
gui/versionselectdialog.ui gui/versionselectdialog.ui
gui/lwjglselectdialog.ui gui/lwjglselectdialog.ui
gui/instancesettings.ui
) )

View File

@ -21,7 +21,8 @@ SOURCES += main.cpp\
data/inifile.cpp \ data/inifile.cpp \
gui/settingsdialog.cpp \ gui/settingsdialog.cpp \
gui/modeditwindow.cpp \ gui/modeditwindow.cpp \
util/appsettings.cpp util/appsettings.cpp \
gui/instancesettings.cpp
HEADERS += gui/mainwindow.h \ HEADERS += gui/mainwindow.h \
data/instancebase.h \ data/instancebase.h \
@ -32,11 +33,13 @@ HEADERS += gui/mainwindow.h \
gui/settingsdialog.h \ gui/settingsdialog.h \
gui/modeditwindow.h \ gui/modeditwindow.h \
util/apputils.h \ util/apputils.h \
util/appsettings.h util/appsettings.h \
gui/instancesettings.h
FORMS += gui/mainwindow.ui \ FORMS += gui/mainwindow.ui \
gui/settingsdialog.ui \ gui/settingsdialog.ui \
gui/modeditwindow.ui gui/modeditwindow.ui \
gui/instancesettings.ui
RESOURCES += \ RESOURCES += \
multimc.qrc multimc.qrc

View File

@ -13,19 +13,11 @@ inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
return QDomElement(); return QDomElement();
} }
// a job that removes all files from the base folder that don't match the whitelist class ThreadedDeleter : public QThread
// runs in whatever thread owns the queue. it is fast though.
class NukeAndPaveJob: public Job
{ {
Q_OBJECT
public: public:
explicit NukeAndPaveJob(QString base, QStringList whitelist) void run()
:Job()
{
QDir dir(base);
m_base = dir.absolutePath();
m_whitelist = whitelist;
};
virtual void start()
{ {
QDirIterator iter(m_base, QDirIterator::Subdirectories); QDirIterator iter(m_base, QDirIterator::Subdirectories);
QStringList nuke_list; QStringList nuke_list;
@ -51,13 +43,37 @@ public:
f.remove(); f.remove();
} }
} }
emit finish();
}; };
private:
QString m_base; QString m_base;
QStringList m_whitelist; 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 class DlMachine : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -84,7 +100,7 @@ public slots:
qDebug() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << qDebug() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" <<
xmlErrorMsg << ba; xmlErrorMsg << ba;
} }
QRegExp etag_match(".*([a-f0-9]{32}).*"); //QRegExp etag_match(".*([a-f0-9]{32}).*");
QDomNodeList contents = doc.elementsByTagName("Contents"); QDomNodeList contents = doc.elementsByTagName("Contents");
JobList *job = new JobList(); JobList *job = new JobList();

180
gui/instancesettings.cpp Normal file
View File

@ -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());
}

34
gui/instancesettings.h Normal file
View File

@ -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

416
gui/instancesettings.ui Normal file
View File

@ -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>

View File

@ -43,6 +43,7 @@
#include "gui/lwjglselectdialog.h" #include "gui/lwjglselectdialog.h"
#include "gui/consolewindow.h" #include "gui/consolewindow.h"
#include "gui/legacymodeditdialog.h" #include "gui/legacymodeditdialog.h"
#include "gui/instancesettings.h"
#include "kcategorizedview.h" #include "kcategorizedview.h"
#include "kcategorydrawer.h" #include "kcategorydrawer.h"
@ -131,6 +132,9 @@ MainWindow::MainWindow ( QWidget *parent ) :
connect(view, SIGNAL(doubleClicked(const QModelIndex &)), connect(view, SIGNAL(doubleClicked(const QModelIndex &)),
this, SLOT(instanceActivated(const QModelIndex &))); this, SLOT(instanceActivated(const QModelIndex &)));
connect(view, SIGNAL(clicked(const QModelIndex &)),
this, SLOT(instanceChanged(const QModelIndex &)));
// Load the instances. // Load the instances.
instList.loadList(); instList.loadList();
// just a test // just a test
@ -417,7 +421,7 @@ void MainWindow::onLoginComplete(LoginResponse response)
{ {
Q_ASSERT_X(m_activeInst != NULL, "onLoginComplete", "no active instance is set"); Q_ASSERT_X(m_activeInst != NULL, "onLoginComplete", "no active instance is set");
if (!m_activeInst->shouldUpdateGame()) if (!m_activeInst->shouldUpdate())
{ {
launchInstance(m_activeInst, response); 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());
}

View File

@ -108,9 +108,13 @@ private slots:
void on_actionChangeInstLWJGLVersion_triggered(); void on_actionChangeInstLWJGLVersion_triggered();
void on_actionInstanceSettings_triggered();
public slots: public slots:
void instanceActivated ( QModelIndex ); void instanceActivated ( QModelIndex );
void instanceChanged ( QModelIndex );
void startTask(Task *task); void startTask(Task *task);
void launchInstance(LoginResponse response); void launchInstance(LoginResponse response);

View File

@ -65,6 +65,9 @@
</widget> </widget>
<widget class="QStatusBar" name="statusBar"/> <widget class="QStatusBar" name="statusBar"/>
<widget class="QToolBar" name="instanceToolBar"> <widget class="QToolBar" name="instanceToolBar">
<property name="enabled">
<bool>true</bool>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Instance Toolbar</string> <string>Instance Toolbar</string>
</property> </property>
@ -300,7 +303,7 @@
</action> </action>
<action name="actionInstanceSettings"> <action name="actionInstanceSettings">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Settings</string> <string>Settings</string>

View File

@ -32,6 +32,9 @@ include/instversionlist.h
include/minecraftversion.h include/minecraftversion.h
include/minecraftversionlist.h include/minecraftversionlist.h
include/library.h
include/fullversion.h
include/fullversionfactory.h
# Tasks # Tasks
include/task.h include/task.h
@ -63,6 +66,9 @@ src/instversionlist.cpp
src/minecraftversion.cpp src/minecraftversion.cpp
src/minecraftversionlist.cpp src/minecraftversionlist.cpp
src/library.cpp
src/fullversion.cpp
src/fullversionfactory.cpp
# Tasks # Tasks
src/task.cpp src/task.cpp

View File

@ -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;
};

View File

@ -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;
};

View File

@ -22,47 +22,15 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QUrl> #include <QUrl>
#include "dlqueue.h"
#include "task.h" #include "task.h"
#include "loginresponse.h" #include "loginresponse.h"
#include "instance.h" #include "instance.h"
#include "libmmc_config.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. * The game update task is the task that handles downloading instances' files.
@ -92,9 +60,6 @@ public:
virtual void executeTask(); virtual void executeTask();
virtual bool downloadFile(const FileToDownloadPtr file);
////////////////////// //////////////////////
// STATE AND STATUS // // STATE AND STATUS //
////////////////////// //////////////////////
@ -110,6 +75,10 @@ public:
*/ */
virtual QString getStateMessage(int state); virtual QString getStateMessage(int state);
private:
void getLegacyJar();
void determineNewVersion();
public slots: public slots:
/*! /*!
@ -122,7 +91,15 @@ public slots:
private 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: signals:
/*! /*!
@ -143,23 +120,8 @@ private:
/////////// ///////////
Instance *m_inst; Instance *m_inst;
LoginResponse m_response; 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 // // STATE AND STATUS STUFF //
//////////////////////////// ////////////////////////////
@ -184,6 +146,13 @@ private:
// Finished // Finished
StateFinished StateFinished
}; };
JobListPtr legacyDownloadJob;
JobListPtr specificVersionDownloadJob;
JobListPtr jarlibDownloadJob;
JobListQueue download_queue;
// target version, determined during this task
MinecraftVersion *targetVersion;
}; };

View File

@ -80,16 +80,7 @@ class LIBMULTIMC_EXPORT Instance : public QObject
* This returns true if shouldForceUpdate game is true or if the intended and * This returns true if shouldForceUpdate game is true or if the intended and
* current versions don't match. * current versions don't match.
*/ */
Q_PROPERTY(bool shouldUpdateGame READ shouldUpdateGame STORED false) Q_PROPERTY(bool shouldUpdate READ shouldUpdate WRITE setShouldUpdate)
/*!
* 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)
/*! /*!
* The instance's current version. * The instance's current version.
@ -125,7 +116,10 @@ class LIBMULTIMC_EXPORT Instance : public QObject
*/ */
Q_PROPERTY(qint64 lastCurrentVersionUpdate READ lastCurrentVersionUpdate WRITE setLastCurrentVersionUpdate) 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 // Dirs
//! Path to the instance's .minecraft folder. //! Path to the instance's .minecraft folder.
@ -236,13 +230,16 @@ public:
virtual QString intendedVersion() const { return settings().get("IntendedJarVersion").toString(); } virtual QString intendedVersion() const { return settings().get("IntendedJarVersion").toString(); }
virtual void setIntendedVersion(QString val) { settings().set("IntendedJarVersion", val); } virtual void setIntendedVersion(QString val) { settings().set("IntendedJarVersion", val); }
virtual bool shouldUpdateGame() const virtual bool shouldUpdate() const
{ return shouldForceUpdateGame() || intendedVersion() != currentVersion(); } {
QVariant var = settings().get("ShouldUpdate");
virtual bool shouldForceUpdateGame() const { return settings().get("ShouldForceUpdate").toBool(); } if(!var.isValid() || var.toBool() == false)
virtual void setShouldForceUpdateGame(bool val) { settings().set("ShouldForceUpdate", val); } {
return intendedVersion() != currentVersion();
}
return true;
}
virtual void setShouldUpdate(bool val) { settings().set("ShouldUpdate", val); }
//// Timestamps //// //// Timestamps ////
@ -256,6 +253,15 @@ public:
virtual qint64 lastCurrentVersionUpdate() const { return settings().get("lastVersionUpdate").value<qint64>(); } virtual qint64 lastCurrentVersionUpdate() const { return settings().get("lastVersionUpdate").value<qint64>(); }
virtual void setLastCurrentVersionUpdate(qint64 val) { settings().set("lastVersionUpdate", val); } 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 ////// ////// Directories //////
QString minecraftDir() const; QString minecraftDir() const;

View File

@ -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;
}
};

View File

@ -0,0 +1,5 @@
#include <QtCore>
#include "fullversion.h"
#include <library.h>
// ECHO, echo, echo, ....

View File

@ -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");
}

View File

@ -25,40 +25,34 @@
#include <QDebug> #include <QDebug>
#include "minecraftversionlist.h" #include "minecraftversionlist.h"
#include "fullversionfactory.h"
#include <fullversion.h>
#include "pathutils.h" #include "pathutils.h"
#include "netutils.h"
GameUpdateTask::GameUpdateTask(const LoginResponse &response, Instance *inst, QObject *parent) : GameUpdateTask::GameUpdateTask(const LoginResponse &response, Instance *inst, QObject *parent) :
Task(parent), m_response(response) Task(parent), m_response(response)
{ {
m_inst = inst; m_inst = inst;
m_updateState = StateInit; m_updateState = StateInit;
m_currentDownload = 0;
} }
void GameUpdateTask::executeTask() void GameUpdateTask::executeTask()
{ {
updateStatus(); updateStatus();
QNetworkAccessManager networkMgr;
netMgr = &networkMgr;
// Get a pointer to the version object that corresponds to the instance's version. // 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()); findVersion(m_inst->intendedVersion());
Q_ASSERT_X(targetVersion != NULL, "game update", "instance's intended version is not an actual version"); if(targetVersion == NULL)
// Make directories
QDir binDir(m_inst->binDir());
if (!binDir.exists() && !binDir.mkpath("."))
{ {
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; return;
} }
///////////////////////// /////////////////////////
// BUILD DOWNLOAD LIST // // BUILD DOWNLOAD LIST //
///////////////////////// /////////////////////////
@ -66,90 +60,150 @@ void GameUpdateTask::executeTask()
setState(StateDetermineURLs); setState(StateDetermineURLs);
// 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) 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")));
////////////////////
// DOWNLOAD FILES //
////////////////////
setState(StateDownloadFiles);
for (int i = 0; i < m_downloadList.length(); i++)
{ {
m_currentDownload = i; determineNewVersion();
if (!downloadFile(m_downloadList[i]))
return;
}
///////////////////
// INSTALL FILES //
///////////////////
setState(StateInstall);
// Nothing to do here yet
//////////////
// FINISHED //
//////////////
setState(StateFinished);
emit gameUpdateComplete(m_response);
}
bool GameUpdateTask::downloadFile( const FileToDownloadPtr file )
{
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 else
{ {
error("Can't download " + file->url().toString() + ": " + reply->errorString()); getLegacyJar();
return false; }
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);
if(!version)
{
error(parser.error_string);
exit(0);
} }
// TODO: Check file integrity after downloading. if(version->isLegacy)
{
getLegacyJar();
return;
}
return true; // 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";
if (targetVersion->launcherVersion() == MinecraftVersion::Launcher16)
{
jarFilename = targetVersion->descriptor();
}
QUrl mcJarURL = targetVersion->downloadURL() + jarFilename + ".jar";
qDebug() << mcJarURL.toString();
auto dljob = DownloadJob::create(mcJarURL, PathCombine(m_inst->minecraftDir(), "bin/minecraft.jar"));
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)));
download_queue.enqueue(legacyDownloadJob);
}
void GameUpdateTask::legacyJarFinished()
{
setState(StateFinished);
emit gameUpdateComplete(m_response);
m_inst->setIsForNewLauncher(true);
exit(1);
}
void GameUpdateTask::legacyJarFailed()
{
emit gameUpdateError("failed to download the minecraft.jar");
exit(0);
} }
int GameUpdateTask::state() const int GameUpdateTask::state() const
@ -223,22 +277,7 @@ void GameUpdateTask::error(const QString &msg)
void GameUpdateTask::updateDownloadProgress(qint64 current, qint64 total) void GameUpdateTask::updateDownloadProgress(qint64 current, qint64 total)
{ {
// The progress on the current file is current / total // The progress on the current file is current / total
float currentDLProgress = (float) current / (float) total; // Cast ALL the values! float currentDLProgress = (float) current / (float) total;
setProgress((int)(currentDLProgress * 100)); // convert to percentage
// 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));
}
FileToDownload::FileToDownload(const QUrl &url, const QString &path, QObject *parent) :
QObject(parent), m_dlURL(url), m_dlPath(path)
{
}

View File

@ -34,7 +34,8 @@ Instance::Instance(const QString &rootDir, QObject *parent) :
settings().registerSetting(new Setting("iconKey", "default")); settings().registerSetting(new Setting("iconKey", "default"));
settings().registerSetting(new Setting("notes", "")); settings().registerSetting(new Setting("notes", ""));
settings().registerSetting(new Setting("NeedsRebuild", true)); 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("JarVersion", "Unknown"));
settings().registerSetting(new Setting("LwjglVersion", "2.9.0")); settings().registerSetting(new Setting("LwjglVersion", "2.9.0"));
settings().registerSetting(new Setting("IntendedJarVersion", "")); settings().registerSetting(new Setting("IntendedJarVersion", ""));
@ -62,6 +63,18 @@ Instance::Instance(const QString &rootDir, QObject *parent) :
// Auto login // Auto login
settings().registerSetting(new OverrideSetting("AutoLogin", globalSettings->getSetting("AutoLogin"))); 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 QString Instance::id() const

View File

@ -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:

View File

@ -47,6 +47,7 @@ public:
protected slots: protected slots:
virtual void changeSetting(const Setting &setting, QVariant value); virtual void changeSetting(const Setting &setting, QVariant value);
virtual void resetSetting ( const Setting& setting );
protected: protected:
virtual QVariant retrieveValue(const Setting &setting); virtual QVariant retrieveValue(const Setting &setting);

View File

@ -84,6 +84,12 @@ signals:
*/ */
void settingChanged(const Setting &setting, QVariant value); 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: public slots:
/*! /*!
* \brief Changes the setting's value. * \brief Changes the setting's value.
@ -93,6 +99,13 @@ public slots:
*/ */
virtual void set(QVariant value); 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: protected:
QString m_id; QString m_id;
QVariant m_defVal; QVariant m_defVal;

View File

@ -100,6 +100,11 @@ public:
*/ */
virtual bool set(const QString &id, QVariant value); 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. * \brief Gets a QList with pointers to all of the registered settings.
@ -125,6 +130,14 @@ signals:
*/ */
void settingChanged(const Setting &setting, QVariant value); 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: protected slots:
/*! /*!
* \brief Changes a setting. * \brief Changes a setting.
@ -136,6 +149,15 @@ protected slots:
*/ */
virtual void changeSetting(const Setting &setting, QVariant value) = 0; 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: protected:
/*! /*!
* \brief Connects the necessary signals to the given Setting. * \brief Connects the necessary signals to the given Setting.

View File

@ -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) QVariant INISettingsObject::retrieveValue(const Setting &setting)
{ {
if (contains(setting.id())) if (contains(setting.id()))

View File

@ -47,3 +47,8 @@ void Setting::set(QVariant value)
{ {
emit settingChanged(*this, value); emit settingChanged(*this, value);
} }
void Setting::reset()
{
emit settingReset(*this);
}

View File

@ -49,7 +49,7 @@ bool SettingsObject::registerSetting(Setting *setting)
// Connect signals. // Connect signals.
connectSignals(*setting); connectSignals(*setting);
qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8()); // qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8());
return true; 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() QList<Setting *> SettingsObject::getSettings()
{ {
return m_settings.values(); return m_settings.values();
@ -115,6 +123,11 @@ void SettingsObject::connectSignals(const Setting &setting)
SLOT(changeSetting(const Setting &, QVariant))); SLOT(changeSetting(const Setting &, QVariant)));
connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)), connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)),
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) void SettingsObject::disconnectSignals(const Setting &setting)
@ -123,4 +136,9 @@ void SettingsObject::disconnectSignals(const Setting &setting)
this, SLOT(changeSetting(const Setting &, QVariant))); this, SLOT(changeSetting(const Setting &, QVariant)));
setting.disconnect(SIGNAL(settingChanged(const Setting &, QVariant)), setting.disconnect(SIGNAL(settingChanged(const Setting &, QVariant)),
this, 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)));
} }

View File

@ -5,13 +5,16 @@
/** /**
* A single file for the downloader/cache to process. * A single file for the downloader/cache to process.
*/ */
class DownloadJob : public Job class LIBUTIL_EXPORT DownloadJob : public Job
{ {
Q_OBJECT Q_OBJECT
public: public:
DownloadJob(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); 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()); static JobPtr create(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString());
public:
static bool ensurePathExists(QString filenamepath);
public slots: public slots:
virtual void start(); virtual void start();
@ -37,8 +40,10 @@ public:
/// save to file? /// save to file?
bool m_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 /// 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 /// this is the output file, if any
QFile m_output_file; QFile m_output_file;
/// if not saving to file, downloaded data is placed here /// if not saving to file, downloaded data is placed here

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <QtCore> #include <QtCore>
#include "libutil_config.h"
enum JobStatus enum JobStatus
{ {
@ -11,7 +12,7 @@ enum JobStatus
class JobList; class JobList;
class Job : public QObject class LIBUTIL_EXPORT Job : public QObject
{ {
Q_OBJECT Q_OBJECT
protected: protected:
@ -30,7 +31,7 @@ typedef QSharedPointer<Job> JobPtr;
/** /**
* A list of jobs, to be processed one by one. * A list of jobs, to be processed one by one.
*/ */
class JobList : public QObject class LIBUTIL_EXPORT JobList : public QObject
{ {
friend class JobListQueue; friend class JobListQueue;
Q_OBJECT Q_OBJECT
@ -127,7 +128,7 @@ typedef QSharedPointer<JobList> JobListPtr;
/** /**
* A queue of job lists! The job lists fail or finish as units. * 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 Q_OBJECT
public: public:

View File

@ -1,20 +1,28 @@
#include "include/dlqueue.h" #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() :Job()
{ {
m_url = url; m_url = url;
m_rel_target_path = rel_target_path; m_target_path = target_path;
m_expected_md5 = expected_md5; m_expected_md5 = expected_md5;
m_check_md5 = m_expected_md5.size(); 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_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() void DownloadJob::start()
@ -22,47 +30,35 @@ void DownloadJob::start()
m_manager.reset ( new QNetworkAccessManager() ); m_manager.reset ( new QNetworkAccessManager() );
if ( m_save_to_file ) if ( m_save_to_file )
{ {
QString filename = m_rel_target_path; QString filename = m_target_path;
m_output_file.setFileName ( filename ); m_output_file.setFileName ( filename );
// if there already is a file and md5 checking is in effect // if there already is a file and md5 checking is in effect and it can be opened
if ( m_output_file.exists() && m_check_md5 ) 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 // check the md5 against the expected one
QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData();
m_output_file.close(); m_output_file.close();
// skip this file if they match // skip this file if they match
if ( hash == m_expected_md5 ) if ( m_check_md5 && hash == m_expected_md5 )
{ {
qDebug() << "Skipping " << m_url.toString() << ": md5 match."; qDebug() << "Skipping " << m_url.toString() << ": md5 match.";
emit finish(); emit finish();
return; return;
} }
} else
}
QFileInfo a ( filename );
QDir dir;
if ( !dir.mkpath ( a.path() ) )
{ {
/* m_expected_md5 = hash;
* 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(); emit fail();
return; return;
} }
} }
qDebug() << "Downloading " << m_url.toString(); qDebug() << "Downloading " << m_url.toString();
QNetworkRequest request ( m_url ); QNetworkRequest request ( m_url );
request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1());
QNetworkReply * rep = m_manager->get ( request ); QNetworkReply * rep = m_manager->get ( request );
m_reply = QSharedPointer<QNetworkReply> ( rep, &QObject::deleteLater ); m_reply = QSharedPointer<QNetworkReply> ( rep, &QObject::deleteLater );
connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) );
@ -121,8 +117,21 @@ void DownloadJob::downloadFinished()
void DownloadJob::downloadReadyRead() 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() ); m_output_file.write ( m_reply->readAll() );
} }
} }