summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
authorSky <git@bunnies.cc>2013-10-21 18:24:29 +0100
committerSky <git@bunnies.cc>2013-10-21 18:24:29 +0100
commitaaf6fe894406ab8aa814de83692504493060e303 (patch)
treea7ac3a60534322f70cbdc34111228885e0606691 /logic
parent6892c11e9f287dcfb1e698f8f46233a01fb7abb6 (diff)
parent11813a0621dd7b500c2d7966a2671c0ab93be692 (diff)
downloadMultiMC-aaf6fe894406ab8aa814de83692504493060e303.tar
MultiMC-aaf6fe894406ab8aa814de83692504493060e303.tar.gz
MultiMC-aaf6fe894406ab8aa814de83692504493060e303.tar.lz
MultiMC-aaf6fe894406ab8aa814de83692504493060e303.tar.xz
MultiMC-aaf6fe894406ab8aa814de83692504493060e303.zip
Merge branch 'develop'
Diffstat (limited to 'logic')
-rw-r--r--logic/BaseInstance.cpp5
-rw-r--r--logic/BaseInstance.h107
-rw-r--r--logic/BaseInstance_p.h2
-rw-r--r--logic/BaseUpdate.cpp4
-rw-r--r--logic/BaseVersion.h45
-rw-r--r--logic/EnabledItemFilter.cpp30
-rw-r--r--logic/EnabledItemFilter.h16
-rw-r--r--logic/ForgeInstaller.cpp140
-rw-r--r--logic/ForgeInstaller.h25
-rw-r--r--logic/InstanceFactory.cpp15
-rw-r--r--logic/InstanceFactory.h6
-rw-r--r--logic/InstanceLauncher.cpp12
-rw-r--r--logic/InstanceVersion.h68
-rw-r--r--logic/JavaUtils.cpp183
-rw-r--r--logic/JavaUtils.h40
-rw-r--r--logic/LegacyInstance.cpp131
-rw-r--r--logic/LegacyInstance.h60
-rw-r--r--logic/LegacyInstance_p.h8
-rw-r--r--logic/LegacyUpdate.cpp233
-rw-r--r--logic/LegacyUpdate.h2
-rw-r--r--logic/MinecraftProcess.cpp103
-rw-r--r--logic/MinecraftProcess.h9
-rw-r--r--logic/MinecraftVersion.h29
-rw-r--r--logic/Mod.cpp121
-rw-r--r--logic/Mod.h87
-rw-r--r--logic/ModList.cpp257
-rw-r--r--logic/ModList.h3
-rw-r--r--logic/NostalgiaInstance.cpp4
-rw-r--r--logic/NostalgiaInstance.h2
-rw-r--r--logic/OneSixAssets.cpp40
-rw-r--r--logic/OneSixInstance.cpp190
-rw-r--r--logic/OneSixInstance.h49
-rw-r--r--logic/OneSixInstance_p.h7
-rw-r--r--logic/OneSixLibrary.cpp162
-rw-r--r--logic/OneSixLibrary.h104
-rw-r--r--logic/OneSixRule.cpp72
-rw-r--r--logic/OneSixRule.h72
-rw-r--r--logic/OneSixUpdate.cpp148
-rw-r--r--logic/OneSixUpdate.h2
-rw-r--r--logic/OneSixVersion.cpp347
-rw-r--r--logic/OneSixVersion.h194
-rw-r--r--logic/OpSys.cpp23
-rw-r--r--logic/OpSys.h22
-rw-r--r--logic/VersionFactory.cpp195
-rw-r--r--logic/VersionFactory.h24
-rw-r--r--logic/lists/BaseVersionList.cpp (renamed from logic/lists/InstVersionList.cpp)36
-rw-r--r--logic/lists/BaseVersionList.h (renamed from logic/lists/InstVersionList.h)14
-rw-r--r--logic/lists/ForgeVersionList.cpp297
-rw-r--r--logic/lists/ForgeVersionList.h109
-rw-r--r--logic/lists/InstanceList.cpp218
-rw-r--r--logic/lists/JavaVersionList.cpp203
-rw-r--r--logic/lists/JavaVersionList.h93
-rw-r--r--logic/lists/LwjglVersionList.cpp70
-rw-r--r--logic/lists/LwjglVersionList.h8
-rw-r--r--logic/lists/MinecraftVersionList.cpp113
-rw-r--r--logic/lists/MinecraftVersionList.h23
-rw-r--r--logic/net/ByteArrayDownload.cpp65
-rw-r--r--logic/net/ByteArrayDownload.h24
-rw-r--r--logic/net/CacheDownload.cpp136
-rw-r--r--logic/net/CacheDownload.h34
-rw-r--r--logic/net/Download.h54
-rw-r--r--logic/net/DownloadJob.cpp222
-rw-r--r--logic/net/DownloadJob.h124
-rw-r--r--logic/net/FileDownload.cpp112
-rw-r--r--logic/net/FileDownload.h35
-rw-r--r--logic/net/ForgeXzDownload.cpp279
-rw-r--r--logic/net/ForgeXzDownload.h35
-rw-r--r--logic/net/HttpMetaCache.cpp165
-rw-r--r--logic/net/HttpMetaCache.h39
-rw-r--r--logic/net/LoginTask.cpp285
-rw-r--r--logic/net/LoginTask.h (renamed from logic/tasks/LoginTask.h)39
-rw-r--r--logic/tasks/LoginTask.cpp111
-rw-r--r--logic/tasks/ProgressProvider.h20
-rw-r--r--logic/tasks/Task.cpp50
-rw-r--r--logic/tasks/Task.h48
75 files changed, 4531 insertions, 1928 deletions
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
index e166449f..6a6b195b 100644
--- a/logic/BaseInstance.cpp
+++ b/logic/BaseInstance.cpp
@@ -13,6 +13,7 @@
* limitations under the License.
*/
+#include "MultiMC.h"
#include "BaseInstance.h"
#include "BaseInstance_p.h"
@@ -131,9 +132,9 @@ InstanceList *BaseInstance::instList() const
return NULL;
}
-InstVersionList *BaseInstance::versionList() const
+std::shared_ptr<BaseVersionList> BaseInstance::versionList() const
{
- return &MinecraftVersionList::getMainList();
+ return MMC->minecraftlist();
}
SettingsObject &BaseInstance::settings() const
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index cc9422be..b083c24a 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -3,7 +3,7 @@
* 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
@@ -21,7 +21,8 @@
#include <settingsobject.h>
#include "inifile.h"
-#include "lists/InstVersionList.h"
+#include "lists/BaseVersionList.h"
+#include "net/LoginTask.h"
class QDialog;
class BaseUpdate;
@@ -32,9 +33,9 @@ class BaseInstancePrivate;
/*!
* \brief Base class for instances.
- * This class implements many functions that are common between instances and
+ * This class implements many functions that are common between instances and
* provides a standard interface for all instances.
- *
+ *
* To create a new instance type, create a new class inheriting from this class
* and implement the pure virtual functions.
*/
@@ -43,66 +44,72 @@ class BaseInstance : public QObject
Q_OBJECT
protected:
/// no-touchy!
- BaseInstance(BaseInstancePrivate * d, const QString &rootDir, SettingsObject * settings, QObject *parent = 0);
+ BaseInstance(BaseInstancePrivate *d, const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+
public:
/// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance() {};
-
- /// nuke thoroughly - deletes the instance contents, notifies the list/model which is responsible of cleaning up the husk
+
+ /// nuke thoroughly - deletes the instance contents, notifies the list/model which is
+ /// responsible of cleaning up the husk
void nuke();
-
- /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to be unique.
+
+ /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
+ /// be unique.
QString id() const;
-
+
/// get the type of this instance
QString instanceType() const;
-
+
/// Path to the instance's root directory.
QString instanceRoot() const;
-
+
/// Path to the instance's minecraft directory.
QString minecraftRoot() const;
-
+
QString name() const;
void setName(QString val);
-
+
QString iconKey() const;
void setIconKey(QString val);
-
+
QString notes() const;
void setNotes(QString val);
-
+
QString group() const;
void setGroupInitial(QString val);
void setGroupPost(QString val);
-
-
+
virtual QString intendedVersionId() const = 0;
virtual bool setIntendedVersionId(QString version) = 0;
-
+
+ virtual bool versionIsCustom() = 0;
+
/*!
* The instance's current version.
- * This value represents the instance's current version. If this value is
+ * This value represents the instance's current version. If this value is
* different from the intendedVersion, the instance should be updated.
* \warning Don't change this value unless you know what you're doing.
*/
virtual QString currentVersionId() const = 0;
- //virtual void setCurrentVersionId(QString val) = 0;
-
+ // virtual void setCurrentVersionId(QString val) = 0;
+
/*!
* Whether or not Minecraft should be downloaded when the instance is launched.
*/
virtual bool shouldUpdate() const = 0;
virtual void setShouldUpdate(bool val) = 0;
- /// Get the curent base jar of this instance. By default, it's the versions/$version/$version.jar
+ /// Get the curent base jar of this instance. By default, it's the
+ /// versions/$version/$version.jar
QString baseJar() const;
/// the default base jar of this instance
virtual QString defaultBaseJar() const = 0;
/// the default custom base jar of this instance
virtual QString defaultCustomBaseJar() const = 0;
-
+
/*!
* Whether or not custom base jar is used
*/
@@ -113,7 +120,7 @@ public:
*/
QString customBaseJar() const;
void setCustomBaseJar(QString val);
-
+
/**
* Gets the time that the instance was last launched.
* Stored in milliseconds since epoch.
@@ -121,53 +128,54 @@ public:
qint64 lastLaunch() const;
/// Sets the last launched time to 'val' milliseconds since epoch
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
-
+
/*!
- * \brief Gets the instance list that this instance is a part of.
- * Returns NULL if this instance is not in a list
+ * \brief Gets the instance list that this instance is a part of.
+ * Returns NULL if this instance is not in a list
* (the parent is not an InstanceList).
- * \return A pointer to the InstanceList containing this instance.
+ * \return A pointer to the InstanceList containing this instance.
*/
InstanceList *instList() const;
-
+
/*!
* \brief Gets a pointer to this instance's version list.
* \return A pointer to the available version list for this instance.
*/
- virtual InstVersionList *versionList() const;
-
+ virtual std::shared_ptr<BaseVersionList> versionList() const;
+
/*!
* \brief Gets this instance's settings object.
* This settings object stores instance-specific settings.
* \return A pointer to this instance's settings object.
*/
virtual SettingsObject &settings() const;
-
+
/// returns a valid update task if update is needed, NULL otherwise
- virtual BaseUpdate* doUpdate() = 0;
-
+ virtual BaseUpdate *doUpdate() = 0;
+
/// returns a valid minecraft process, ready for launch
- virtual MinecraftProcess* prepareForLaunch(QString user, QString session) = 0;
-
- /// do any necessary cleanups after the instance finishes. also runs before 'prepareForLaunch'
+ virtual MinecraftProcess *prepareForLaunch(LoginResponse response) = 0;
+
+ /// do any necessary cleanups after the instance finishes. also runs before
+ /// 'prepareForLaunch'
virtual void cleanupAfterRun() = 0;
-
+
/// create a mod edit dialog for the instance
- virtual QDialog * createModEditDialog ( QWidget* parent ) = 0;
-
+ virtual QDialog *createModEditDialog(QWidget *parent) = 0;
+
/// is a particular action enabled with this instance selected?
virtual bool menuActionEnabled(QString action_name) const = 0;
-
+
virtual QString getStatusbarDescription() = 0;
-
+
/// FIXME: this really should be elsewhere...
virtual QString instanceConfigFolder() const = 0;
-
+
signals:
/*!
* \brief Signal emitted when properties relevant to the instance view change
*/
- void propertiesChanged(BaseInstance * inst);
+ void propertiesChanged(BaseInstance *inst);
/*!
* \brief Signal emitted when groups are affected in any way
*/
@@ -175,12 +183,11 @@ signals:
/*!
* \brief The instance just got nuked. Hurray!
*/
- void nuked(BaseInstance * inst);
-
+ void nuked(BaseInstance *inst);
+
protected:
- QSharedPointer<BaseInstancePrivate> inst_d;
+ std::shared_ptr<BaseInstancePrivate> inst_d;
};
// pointer for lazy people
-typedef QSharedPointer<BaseInstance> InstancePtr;
-
+typedef std::shared_ptr<BaseInstance> InstancePtr;
diff --git a/logic/BaseInstance_p.h b/logic/BaseInstance_p.h
index a30916a4..06c0c0ba 100644
--- a/logic/BaseInstance_p.h
+++ b/logic/BaseInstance_p.h
@@ -4,7 +4,7 @@
class BaseInstance;
-#define I_D(Class) Class##Private * const d = (Class##Private * const) inst_d.data()
+#define I_D(Class) Class##Private * const d = (Class##Private * const) inst_d.get()
struct BaseInstancePrivate
{
diff --git a/logic/BaseUpdate.cpp b/logic/BaseUpdate.cpp
index b086ab14..02b29d32 100644
--- a/logic/BaseUpdate.cpp
+++ b/logic/BaseUpdate.cpp
@@ -7,7 +7,5 @@ BaseUpdate::BaseUpdate ( BaseInstance* inst, QObject* parent ) : Task ( parent )
void BaseUpdate::updateDownloadProgress(qint64 current, qint64 total)
{
- // The progress on the current file is current / total
- float currentDLProgress = (float) current / (float) total;
- setProgress((int)(currentDLProgress * 100)); // convert to percentage
+ emit progress(current, total);
} \ No newline at end of file
diff --git a/logic/BaseVersion.h b/logic/BaseVersion.h
new file mode 100644
index 00000000..01745c46
--- /dev/null
+++ b/logic/BaseVersion.h
@@ -0,0 +1,45 @@
+/* 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 <memory>
+
+/*!
+ * An abstract base class for versions.
+ */
+struct BaseVersion
+{
+ /*!
+ * A string used to identify this version in config files.
+ * This should be unique within the version list or shenanigans will occur.
+ */
+ virtual QString descriptor() = 0;
+
+ /*!
+ * The name of this version as it is displayed to the user.
+ * For example: "1.5.1"
+ */
+ virtual QString name() = 0;
+
+ /*!
+ * This should return a string that describes
+ * the kind of version this is (Stable, Beta, Snapshot, whatever)
+ */
+ virtual QString typeString() const = 0;
+};
+
+typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
+
+Q_DECLARE_METATYPE( BaseVersionPtr ) \ No newline at end of file
diff --git a/logic/EnabledItemFilter.cpp b/logic/EnabledItemFilter.cpp
new file mode 100644
index 00000000..6ecd0271
--- /dev/null
+++ b/logic/EnabledItemFilter.cpp
@@ -0,0 +1,30 @@
+#include "EnabledItemFilter.h"
+
+EnabledItemFilter::EnabledItemFilter(QObject* parent)
+ :QSortFilterProxyModel(parent)
+{
+
+}
+
+void EnabledItemFilter::setActive(bool active)
+{
+ m_active = active;
+ invalidateFilter();
+}
+
+bool EnabledItemFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ if(!m_active)
+ return true;
+ QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
+ if(sourceModel()->flags(index) & Qt::ItemIsEnabled)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool EnabledItemFilter::lessThan(const QModelIndex& left, const QModelIndex& right) const
+{
+ return QSortFilterProxyModel::lessThan(left, right);
+}
diff --git a/logic/EnabledItemFilter.h b/logic/EnabledItemFilter.h
new file mode 100644
index 00000000..cb6d4041
--- /dev/null
+++ b/logic/EnabledItemFilter.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <QSortFilterProxyModel>
+
+class EnabledItemFilter : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ EnabledItemFilter(QObject *parent = 0);
+ void setActive(bool active);
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+ bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
+private:
+ bool m_active = false;
+}; \ No newline at end of file
diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp
new file mode 100644
index 00000000..a946dd44
--- /dev/null
+++ b/logic/ForgeInstaller.cpp
@@ -0,0 +1,140 @@
+#include "ForgeInstaller.h"
+#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
+#include "net/HttpMetaCache.h"
+#include <quazip.h>
+#include <quazipfile.h>
+#include <pathutils.h>
+#include <QStringList>
+#include "MultiMC.h"
+
+ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
+{
+ std::shared_ptr<OneSixVersion> newVersion;
+ m_universal_url = universal_url;
+
+ QuaZip zip(filename);
+ if (!zip.open(QuaZip::mdUnzip))
+ return;
+
+ QuaZipFile file(&zip);
+
+ // read the install profile
+ if (!zip.setCurrentFile("install_profile.json"))
+ return;
+
+ QJsonParseError jsonError;
+ if (!file.open(QIODevice::ReadOnly))
+ return;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll(), &jsonError);
+ file.close();
+ if (jsonError.error != QJsonParseError::NoError)
+ return;
+
+ if (!jsonDoc.isObject())
+ return;
+
+ QJsonObject root = jsonDoc.object();
+
+ auto installVal = root.value("install");
+ auto versionInfoVal = root.value("versionInfo");
+ if (!installVal.isObject() || !versionInfoVal.isObject())
+ return;
+
+ // read the forge version info
+ {
+ newVersion = OneSixVersion::fromJson(versionInfoVal.toObject());
+ if (!newVersion)
+ return;
+ }
+
+ QJsonObject installObj = installVal.toObject();
+ QString libraryName = installObj.value("path").toString();
+ internalPath = installObj.value("filePath").toString();
+
+ // where do we put the library? decode the mojang path
+ OneSixLibrary lib(libraryName);
+ lib.finalize();
+
+ auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath());
+ finalPath = "libraries/" + lib.storagePath();
+ if (!ensureFilePathExists(finalPath))
+ return;
+
+ if (!zip.setCurrentFile(internalPath))
+ return;
+ if (!file.open(QIODevice::ReadOnly))
+ return;
+ {
+ QByteArray data = file.readAll();
+ // extract file
+ QSaveFile extraction(finalPath);
+ if (!extraction.open(QIODevice::WriteOnly))
+ return;
+ if (extraction.write(data) != data.size())
+ return;
+ if (!extraction.commit())
+ return;
+ QCryptographicHash md5sum(QCryptographicHash::Md5);
+ md5sum.addData(data);
+
+ cacheentry->stale = false;
+ cacheentry->md5sum = md5sum.result().toHex().constData();
+ MMC->metacache()->updateEntry(cacheentry);
+ }
+ file.close();
+
+ m_forge_version = newVersion;
+ realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
+}
+
+bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to)
+{
+ if (!m_forge_version)
+ return false;
+ to->externalUpdateStart();
+ int sliding_insert_window = 0;
+ {
+ // for each library in the version we are adding (except for the blacklisted)
+ QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"};
+ for (auto lib : m_forge_version->libraries)
+ {
+ QString libName = lib->name();
+ // WARNING: This could actually break.
+ // if this is the actual forge lib, set an absolute url for the download
+ if (libName.contains("minecraftforge"))
+ {
+ lib->setAbsoluteUrl(m_universal_url);
+ }
+ else if (libName.contains("scala"))
+ {
+ lib->setHint("forge-pack-xz");
+ }
+ if (blacklist.contains(libName))
+ continue;
+
+ // find an entry that matches this one
+ bool found = false;
+ for (auto tolib : to->libraries)
+ {
+ if (tolib->name() != libName)
+ continue;
+ found = true;
+ // replace lib
+ tolib = lib;
+ break;
+ }
+ if (!found)
+ {
+ // add lib
+ to->libraries.insert(sliding_insert_window, lib);
+ sliding_insert_window++;
+ }
+ }
+ to->mainClass = m_forge_version->mainClass;
+ to->minecraftArguments = m_forge_version->minecraftArguments;
+ to->processArguments = m_forge_version->processArguments;
+ }
+ to->externalUpdateFinish();
+ return to->toOriginalFile();
+}
diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h
new file mode 100644
index 00000000..f6f22a2a
--- /dev/null
+++ b/logic/ForgeInstaller.h
@@ -0,0 +1,25 @@
+#pragma once
+#include <QString>
+#include <memory>
+
+class OneSixVersion;
+
+class ForgeInstaller
+{
+public:
+ ForgeInstaller(QString filename, QString universal_url);
+
+ bool apply(std::shared_ptr<OneSixVersion> to);
+
+private:
+ // the version, read from the installer
+ std::shared_ptr<OneSixVersion> m_forge_version;
+ QString internalPath;
+ QString finalPath;
+ QString realVersionId;
+ QString m_universal_url;
+};
+
+
+
+
diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp
index f0630568..0da62803 100644
--- a/logic/InstanceFactory.cpp
+++ b/logic/InstanceFactory.cpp
@@ -22,7 +22,7 @@
#include "LegacyInstance.h"
#include "OneSixInstance.h"
#include "NostalgiaInstance.h"
-#include "InstanceVersion.h"
+#include "BaseVersion.h"
#include "MinecraftVersion.h"
#include "inifile.h"
@@ -30,6 +30,7 @@
#include <setting.h>
#include "pathutils.h"
+#include <logger/QsLog.h>
InstanceFactory InstanceFactory::loader;
@@ -68,16 +69,16 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
}
-InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*& inst, InstVersionPtr version, const QString& instDir )
+InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*& inst, BaseVersionPtr version, const QString& instDir )
{
QDir rootDir(instDir);
- qDebug(instDir.toUtf8());
+ QLOG_DEBUG() << instDir.toUtf8();
if (!rootDir.exists() && !rootDir.mkpath("."))
{
return InstanceFactory::CantCreateDir;
}
- auto mcVer = version.dynamicCast<MinecraftVersion>();
+ auto mcVer = std::dynamic_pointer_cast<MinecraftVersion>(version);
if(!mcVer)
return InstanceFactory::NoSuchVersion;
@@ -89,19 +90,19 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*&
case MinecraftVersion::Legacy:
m_settings->set("InstanceType", "Legacy");
inst = new LegacyInstance(instDir, m_settings, this);
- inst->setIntendedVersionId(version->descriptor);
+ inst->setIntendedVersionId(version->descriptor());
inst->setShouldUseCustomBaseJar(false);
break;
case MinecraftVersion::OneSix:
m_settings->set("InstanceType", "OneSix");
inst = new OneSixInstance(instDir, m_settings, this);
- inst->setIntendedVersionId(version->descriptor);
+ inst->setIntendedVersionId(version->descriptor());
inst->setShouldUseCustomBaseJar(false);
break;
case MinecraftVersion::Nostalgia:
m_settings->set("InstanceType", "Nostalgia");
inst = new NostalgiaInstance(instDir, m_settings, this);
- inst->setIntendedVersionId(version->descriptor);
+ inst->setIntendedVersionId(version->descriptor());
inst->setShouldUseCustomBaseJar(false);
break;
default:
diff --git a/logic/InstanceFactory.h b/logic/InstanceFactory.h
index ed54f520..1c527749 100644
--- a/logic/InstanceFactory.h
+++ b/logic/InstanceFactory.h
@@ -19,9 +19,9 @@
#include <QMap>
#include <QList>
-#include "InstanceVersion.h"
+#include "BaseVersion.h"
-class InstVersion;
+class BaseVersion;
class BaseInstance;
/*!
@@ -61,7 +61,7 @@ public:
* - InstExists if the given instance directory is already an instance.
* - CantCreateDir if the given instance directory cannot be created.
*/
- InstCreateError createInstance(BaseInstance *&inst, InstVersionPtr version, const QString &instDir);
+ InstCreateError createInstance(BaseInstance *&inst, BaseVersionPtr version, const QString &instDir);
/*!
* \brief Loads an instance from the given directory.
diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp
index 312f4c69..720052a3 100644
--- a/logic/InstanceLauncher.cpp
+++ b/logic/InstanceLauncher.cpp
@@ -3,9 +3,9 @@
#include <iostream>
#include "gui/logindialog.h"
-#include "gui/taskdialog.h"
+#include "gui/ProgressDialog.h"
#include "gui/consolewindow.h"
-#include "logic/tasks/LoginTask.h"
+#include "logic/net/LoginTask.h"
#include "logic/MinecraftProcess.h"
#include "lists/InstanceList.h"
@@ -25,13 +25,13 @@ void InstanceLauncher::onLoginComplete()
LoginTask * task = ( LoginTask * ) QObject::sender();
auto result = task->getResult();
auto instance = MMC->instances()->getInstanceById(instId);
- proc = instance->prepareForLaunch ( result.username, result.sessionID );
+ proc = instance->prepareForLaunch ( result );
if ( !proc )
{
//FIXME: report error
return;
}
- console = new ConsoleWindow();
+ console = new ConsoleWindow(proc);
console->show();
connect ( proc, SIGNAL ( ended() ), SLOT ( onTerminated() ) );
@@ -48,7 +48,7 @@ void InstanceLauncher::doLogin ( const QString& errorMsg )
{
UserInfo uInfo {loginDlg->getUsername(), loginDlg->getPassword() };
- TaskDialog* tDialog = new TaskDialog ( nullptr );
+ ProgressDialog* tDialog = new ProgressDialog ( nullptr );
LoginTask* loginTask = new LoginTask ( uInfo, tDialog );
connect ( loginTask, SIGNAL ( succeeded() ),SLOT ( onLoginComplete() ), Qt::QueuedConnection );
connect ( loginTask, SIGNAL ( failed ( QString ) ),SLOT ( doLogin ( QString ) ), Qt::QueuedConnection );
@@ -61,7 +61,7 @@ int InstanceLauncher::launch()
{
std::cout << "Launching Instance '" << qPrintable ( instId ) << "'" << std::endl;
auto instance = MMC->instances()->getInstanceById(instId);
- if ( instance.isNull() )
+ if ( !instance )
{
std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl;
return 1;
diff --git a/logic/InstanceVersion.h b/logic/InstanceVersion.h
deleted file mode 100644
index eecd9c4e..00000000
--- a/logic/InstanceVersion.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* 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 <QSharedPointer>
-
-/*!
- * An abstract base class for versions.
- */
-struct InstVersion
-{
- /*!
- * Checks if this version is less (older) than the given version.
- * \param other The version to compare this one to.
- * \return True if this version is older than the given version.
- */
- virtual bool operator<(const InstVersion &rhs) const
- {
- return timestamp < rhs.timestamp;
- }
-
- /*!
- * Checks if this version is greater (newer) than the given version.
- * \param other The version to compare this one to.
- * \return True if this version is newer than the given version.
- */
- virtual bool operator>( const InstVersion& rhs ) const
- {
- return timestamp > rhs.timestamp;
- }
-
- /*!
- * A string used to identify this version in config files.
- * This should be unique within the version list or shenanigans will occur.
- */
- QString descriptor;
- /*!
- * The name of this version as it is displayed to the user.
- * For example: "1.5.1"
- */
- QString name;
- /*!
- * Gets the version's timestamp.
- * This is primarily used for sorting versions in a list.
- */
- qint64 timestamp;
-
- virtual QString typeString() const
- {
- return "InstVersion";
- }
-};
-
-typedef QSharedPointer<InstVersion> InstVersionPtr;
-
-Q_DECLARE_METATYPE( InstVersionPtr ) \ No newline at end of file
diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp
new file mode 100644
index 00000000..8e9c984f
--- /dev/null
+++ b/logic/JavaUtils.cpp
@@ -0,0 +1,183 @@
+/* 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 "JavaUtils.h"
+#include "pathutils.h"
+#include "MultiMC.h"
+
+#include <QStringList>
+#include <QString>
+#include <QDir>
+#include <QMessageBox>
+#include <logger/QsLog.h>
+#include <gui/versionselectdialog.h>
+#include <setting.h>
+
+JavaUtils::JavaUtils()
+{
+
+}
+
+JavaVersionPtr JavaUtils::GetDefaultJava()
+{
+ JavaVersionPtr javaVersion(new JavaVersion());
+
+ javaVersion->id = "java";
+ javaVersion->arch = "unknown";
+ javaVersion->path = "java";
+ javaVersion->recommended = false;
+
+ return javaVersion;
+}
+
+#if WINDOWS
+QList<JavaVersionPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName)
+{
+ QList<JavaVersionPtr> javas;
+
+ QString archType = "unknown";
+ if(keyType == KEY_WOW64_64KEY) archType = "64";
+ else if(keyType == KEY_WOW64_32KEY) archType = "32";
+
+ HKEY jreKey;
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
+ {
+ // Read the current type version from the registry.
+ // This will be used to find any key that contains the JavaHome value.
+ char *value = new char[0];
+ DWORD valueSz = 0;
+ if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE*)value, &valueSz) == ERROR_MORE_DATA)
+ {
+ value = new char[valueSz];
+ RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE*)value, &valueSz);
+ }
+
+ QString recommended = value;
+
+ TCHAR subKeyName[255];
+ DWORD subKeyNameSize, numSubKeys, retCode;
+
+ // Get the number of subkeys
+ RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ // Iterate until RegEnumKeyEx fails
+ if(numSubKeys > 0)
+ {
+ for(int i = 0; i < numSubKeys; i++)
+ {
+ subKeyNameSize = 255;
+ retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL);
+ if(retCode == ERROR_SUCCESS)
+ {
+ // Now open the registry key for the version that we just got.
+ QString newKeyName = keyName + "\\" + subKeyName;
+
+ HKEY newKey;
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
+ {
+ // Read the JavaHome value to find where Java is installed.
+ value = new char[0];
+ valueSz = 0;
+ if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz) == ERROR_MORE_DATA)
+ {
+ value = new char[valueSz];
+ RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz);
+
+ // Now, we construct the version object and add it to the list.
+ JavaVersionPtr javaVersion(new JavaVersion());
+
+ javaVersion->id = subKeyName;
+ javaVersion->arch = archType;
+ javaVersion->path = QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe");
+ javaVersion->recommended = (recommended == subKeyName);
+ javas.append(javaVersion);
+ }
+
+ RegCloseKey(newKey);
+ }
+ }
+ }
+ }
+
+ RegCloseKey(jreKey);
+ }
+
+ return javas;
+}
+
+QList<JavaVersionPtr> JavaUtils::FindJavaPaths()
+{
+ QList<JavaVersionPtr> javas;
+
+ QList<JavaVersionPtr> JRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
+ QList<JavaVersionPtr> JDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
+ QList<JavaVersionPtr> JRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
+ QList<JavaVersionPtr> JDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
+
+ javas.append(JRE64s);
+ javas.append(JDK64s);
+ javas.append(JRE32s);
+ javas.append(JDK32s);
+
+ if(javas.size() <= 0)
+ {
+ QLOG_WARN() << "Failed to find Java in the Windows registry - defaulting to \"java\"";
+ javas.append(this->GetDefaultJava());
+ return javas;
+ }
+
+ QLOG_INFO() << "Found the following Java installations (64 -> 32, JRE -> JDK): ";
+
+ for(auto &java : javas)
+ {
+ QString sRec;
+ if(java->recommended) sRec = "(Recommended)";
+ QLOG_INFO() << java->id << java->arch << " at " << java->path << sRec;
+ }
+
+ return javas;
+}
+#elif OSX
+QList<JavaVersionPtr> JavaUtils::FindJavaPaths()
+{
+ QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\"";
+
+ QList<JavaVersionPtr> javas;
+ javas.append(this->GetDefaultJava());
+
+ return javas;
+}
+
+#elif LINUX
+QList<JavaVersionPtr> JavaUtils::FindJavaPaths()
+{
+ QLOG_INFO() << "Linux Java detection incomplete - defaulting to \"java\"";
+
+ QList<JavaVersionPtr> javas;
+ javas.append(this->GetDefaultJava());
+
+ return javas;
+}
+#else
+QList<JavaVersionPtr> JavaUtils::FindJavaPaths()
+{
+ QLOG_INFO() << "Unknown operating system build - defaulting to \"java\"";
+
+ QList<JavaVersionPtr> javas;
+ javas.append(this->GetDefaultJava());
+
+ return javas;
+}
+#endif
diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h
new file mode 100644
index 00000000..e4f777d0
--- /dev/null
+++ b/logic/JavaUtils.h
@@ -0,0 +1,40 @@
+/* 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 <QStringList>
+#include <QWidget>
+#include <logic/lists/JavaVersionList.h>
+#include "osutils.h"
+
+#if WINDOWS
+ #include <windows.h>
+#endif
+
+class JavaUtils
+{
+public:
+ JavaUtils();
+
+ QList<JavaVersionPtr> FindJavaPaths();
+ JavaVersionPtr GetDefaultJava();
+
+private:
+
+#if WINDOWS
+ QList<JavaVersionPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
+#endif
+};
diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp
index 0672d2c8..f741caad 100644
--- a/logic/LegacyInstance.cpp
+++ b/logic/LegacyInstance.cpp
@@ -14,8 +14,9 @@
#define LAUNCHER_FILE "MultiMCLauncher.jar"
-LegacyInstance::LegacyInstance(const QString& rootDir, SettingsObject* settings, QObject* parent)
- :BaseInstance( new LegacyInstancePrivate(),rootDir, settings, parent)
+LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent)
+ : BaseInstance(new LegacyInstancePrivate(), rootDir, settings, parent)
{
settings->registerSetting(new Setting("NeedsRebuild", true));
settings->registerSetting(new Setting("ShouldUpdate", false));
@@ -24,97 +25,96 @@ LegacyInstance::LegacyInstance(const QString& rootDir, SettingsObject* settings,
settings->registerSetting(new Setting("IntendedJarVersion", ""));
}
-BaseUpdate* LegacyInstance::doUpdate()
+BaseUpdate *LegacyInstance::doUpdate()
{
+ auto list = jarModList();
return new LegacyUpdate(this, this);
}
-MinecraftProcess* LegacyInstance::prepareForLaunch(QString user, QString session)
+MinecraftProcess *LegacyInstance::prepareForLaunch(LoginResponse response)
{
- MinecraftProcess * proc = new MinecraftProcess(this);
-
+ MinecraftProcess *proc = new MinecraftProcess(this);
+
QIcon icon = MMC->icons()->getIcon(iconKey());
- auto pixmap = icon.pixmap(128,128);
- pixmap.save(PathCombine(minecraftRoot(), "icon.png"),"PNG");
-
+ auto pixmap = icon.pixmap(128, 128);
+ pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG");
+
// extract the legacy launcher
QFile(":/launcher/launcher.jar").copy(PathCombine(minecraftRoot(), LAUNCHER_FILE));
-
+
// set the process arguments
{
QStringList args;
-
+
// window size
QString windowSize;
if (settings().get("LaunchMaximized").toBool())
windowSize = "max";
else
- windowSize = QString("%1x%2").
- arg(settings().get("MinecraftWinWidth").toInt()).
- arg(settings().get("MinecraftWinHeight").toInt());
-
+ windowSize = QString("%1x%2").arg(settings().get("MinecraftWinWidth").toInt()).arg(
+ settings().get("MinecraftWinHeight").toInt());
+
// window title
QString windowTitle;
windowTitle.append("MultiMC: ").append(name());
-
+
// Java arguments
args.append(Util::Commandline::splitArgs(settings().get("JvmArgs").toString()));
-
+
#ifdef OSX
// OSX dock icon and name
args << "-Xdock:icon=icon.png";
args << QString("-Xdock:name=\"%1\"").arg(windowTitle);
#endif
-
- QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion()).absolutePath();
-
+
+ QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion())
+ .absolutePath();
+
// launcher arguments
args << QString("-Xms%1m").arg(settings().get("MinMemAlloc").toInt());
args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt());
args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt());
args << "-jar" << LAUNCHER_FILE;
- args << user;
- args << session;
+ args << response.player_name;
+ args << response.session_id;
args << windowTitle;
args << windowSize;
args << lwjgl;
proc->setMinecraftArguments(args);
}
-
+
// set the process work path
proc->setMinecraftWorkdir(minecraftRoot());
-
+
return proc;
}
void LegacyInstance::cleanupAfterRun()
{
- //FIXME: delete the launcher and icons and whatnot.
+ // FIXME: delete the launcher and icons and whatnot.
}
-QSharedPointer< ModList > LegacyInstance::coreModList()
+std::shared_ptr<ModList> LegacyInstance::coreModList()
{
I_D(LegacyInstance);
- if(!d->core_mod_list)
+ if (!d->core_mod_list)
{
d->core_mod_list.reset(new ModList(coreModsDir()));
}
- else
- d->core_mod_list->update();
+ d->core_mod_list->update();
return d->core_mod_list;
}
-QSharedPointer< ModList > LegacyInstance::jarModList()
+std::shared_ptr<ModList> LegacyInstance::jarModList()
{
I_D(LegacyInstance);
- if(!d->jar_mod_list)
+ if (!d->jar_mod_list)
{
auto list = new ModList(jarModsDir(), modListFile());
connect(list, SIGNAL(changed()), SLOT(jarModsChanged()));
d->jar_mod_list.reset(list);
}
- else
- d->jar_mod_list->update();
+ d->jar_mod_list->update();
return d->jar_mod_list;
}
@@ -123,38 +123,33 @@ void LegacyInstance::jarModsChanged()
setShouldRebuild(true);
}
-
-QSharedPointer< ModList > LegacyInstance::loaderModList()
+std::shared_ptr<ModList> LegacyInstance::loaderModList()
{
I_D(LegacyInstance);
- if(!d->loader_mod_list)
+ if (!d->loader_mod_list)
{
d->loader_mod_list.reset(new ModList(loaderModsDir()));
}
- else
- d->loader_mod_list->update();
+ d->loader_mod_list->update();
return d->loader_mod_list;
}
-QSharedPointer< ModList > LegacyInstance::texturePackList()
+std::shared_ptr<ModList> LegacyInstance::texturePackList()
{
I_D(LegacyInstance);
- if(!d->texture_pack_list)
+ if (!d->texture_pack_list)
{
d->texture_pack_list.reset(new ModList(texturePacksDir()));
}
- else
- d->texture_pack_list->update();
+ d->texture_pack_list->update();
return d->texture_pack_list;
}
-
-QDialog * LegacyInstance::createModEditDialog ( QWidget* parent )
+QDialog *LegacyInstance::createModEditDialog(QWidget *parent)
{
return new LegacyModEditDialog(this, parent);
}
-
QString LegacyInstance::jarModsDir() const
{
return PathCombine(instanceRoot(), "instMods");
@@ -204,7 +199,6 @@ QString LegacyInstance::instanceConfigFolder() const
return PathCombine(minecraftRoot(), "config");
}
-
/*
bool LegacyInstance::shouldUpdateCurrentVersion() const
{
@@ -215,21 +209,22 @@ bool LegacyInstance::shouldUpdateCurrentVersion() const
void LegacyInstance::updateCurrentVersion(bool keepCurrent)
{
QFileInfo jar(runnableJar());
-
+
if(!jar.exists())
{
setLastCurrentVersionUpdate(0);
setCurrentVersionId("Unknown");
return;
}
-
+
qint64 time = jar.lastModified().toUTC().toMSecsSinceEpoch();
-
+
setLastCurrentVersionUpdate(time);
if (!keepCurrent)
{
// TODO: Implement GetMinecraftJarVersion function.
- QString newVersion = "Unknown";//javautils::GetMinecraftJarVersion(jar.absoluteFilePath());
+ QString newVersion =
+"Unknown";//javautils::GetMinecraftJarVersion(jar.absoluteFilePath());
setCurrentVersionId(newVersion);
}
}
@@ -247,41 +242,41 @@ void LegacyInstance::setLastCurrentVersionUpdate ( qint64 val )
bool LegacyInstance::shouldRebuild() const
{
I_D(LegacyInstance);
- return d->m_settings->get ( "NeedsRebuild" ).toBool();
+ return d->m_settings->get("NeedsRebuild").toBool();
}
-void LegacyInstance::setShouldRebuild ( bool val )
+void LegacyInstance::setShouldRebuild(bool val)
{
I_D(LegacyInstance);
- d->m_settings->set ( "NeedsRebuild", val );
+ d->m_settings->set("NeedsRebuild", val);
}
QString LegacyInstance::currentVersionId() const
{
I_D(LegacyInstance);
- return d->m_settings->get ( "JarVersion" ).toString();
+ return d->m_settings->get("JarVersion").toString();
}
-void LegacyInstance::setCurrentVersionId ( QString val )
+void LegacyInstance::setCurrentVersionId(QString val)
{
I_D(LegacyInstance);
- d->m_settings->set ( "JarVersion", val );
+ d->m_settings->set("JarVersion", val);
}
QString LegacyInstance::lwjglVersion() const
{
I_D(LegacyInstance);
- return d->m_settings->get ( "LwjglVersion" ).toString();
+ return d->m_settings->get("LwjglVersion").toString();
}
-void LegacyInstance::setLWJGLVersion ( QString val )
+void LegacyInstance::setLWJGLVersion(QString val)
{
I_D(LegacyInstance);
- d->m_settings->set ( "LwjglVersion", val );
+ d->m_settings->set("LwjglVersion", val);
}
QString LegacyInstance::intendedVersionId() const
{
I_D(LegacyInstance);
- return d->m_settings->get ( "IntendedJarVersion" ).toString();
+ return d->m_settings->get("IntendedJarVersion").toString();
}
-bool LegacyInstance::setIntendedVersionId ( QString version )
+bool LegacyInstance::setIntendedVersionId(QString version)
{
settings().set("IntendedJarVersion", version);
setShouldUpdate(true);
@@ -290,16 +285,16 @@ bool LegacyInstance::setIntendedVersionId ( QString version )
bool LegacyInstance::shouldUpdate() const
{
I_D(LegacyInstance);
- QVariant var = settings().get ( "ShouldUpdate" );
- if ( !var.isValid() || var.toBool() == false )
+ QVariant var = settings().get("ShouldUpdate");
+ if (!var.isValid() || var.toBool() == false)
{
return intendedVersionId() != currentVersionId();
}
return true;
}
-void LegacyInstance::setShouldUpdate ( bool val )
+void LegacyInstance::setShouldUpdate(bool val)
{
- settings().set ( "ShouldUpdate", val );
+ settings().set("ShouldUpdate", val);
}
QString LegacyInstance::defaultBaseJar() const
@@ -312,14 +307,16 @@ QString LegacyInstance::defaultCustomBaseJar() const
return PathCombine(binDir(), "mcbackup.jar");
}
-bool LegacyInstance::menuActionEnabled ( QString action_name ) const
+bool LegacyInstance::menuActionEnabled(QString action_name) const
{
+ if (action_name == "actionChangeInstMCVersion")
+ return false;
return true;
}
QString LegacyInstance::getStatusbarDescription()
{
- if(shouldUpdate())
+ if (shouldUpdate())
return "Legacy : " + currentVersionId() + " -> " + intendedVersionId();
else
return "Legacy : " + currentVersionId();
diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h
index b36026fc..8bf334f6 100644
--- a/logic/LegacyInstance.h
+++ b/logic/LegacyInstance.h
@@ -9,21 +9,22 @@ class LegacyInstance : public BaseInstance
{
Q_OBJECT
public:
-
- explicit LegacyInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0);
-
+
+ explicit LegacyInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+
/// Path to the instance's minecraft.jar
QString runnableJar() const;
-
+
//! Path to the instance's modlist file.
QString modListFile() const;
-
+
////// Mod Lists //////
- QSharedPointer<ModList> jarModList();
- QSharedPointer<ModList> coreModList();
- QSharedPointer<ModList> loaderModList();
- QSharedPointer<ModList> texturePackList();
-
+ std::shared_ptr<ModList> jarModList();
+ std::shared_ptr<ModList> coreModList();
+ std::shared_ptr<ModList> loaderModList();
+ std::shared_ptr<ModList> texturePackList();
+
////// Directories //////
QString savesDir() const;
QString texturePacksDir() const;
@@ -33,40 +34,47 @@ public:
QString coreModsDir() const;
QString resourceDir() const;
virtual QString instanceConfigFolder() const;
-
+
/*!
* Whether or not the instance's minecraft.jar needs to be rebuilt.
- * If this is true, when the instance launches, its jar mods will be
+ * If this is true, when the instance launches, its jar mods will be
* re-added to a fresh minecraft.jar file.
*/
bool shouldRebuild() const;
void setShouldRebuild(bool val);
-
+
virtual QString currentVersionId() const;
virtual void setCurrentVersionId(QString val);
-
+
//! The version of LWJGL that this instance uses.
QString lwjglVersion() const;
/// st the version of LWJGL libs this instance will use
void setLWJGLVersion(QString val);
-
+
virtual QString intendedVersionId() const;
- virtual bool setIntendedVersionId ( QString version );
-
+ virtual bool setIntendedVersionId(QString version);
+ // the `version' of Legacy instances is defined by the launcher code.
+ // in contrast with OneSix, where `version' is described in a json file
+ virtual bool versionIsCustom() override
+ {
+ return false;
+ };
+
virtual bool shouldUpdate() const;
virtual void setShouldUpdate(bool val);
- virtual BaseUpdate* doUpdate();
-
- virtual MinecraftProcess* prepareForLaunch( QString user, QString session );
+ virtual BaseUpdate *doUpdate();
+
+ virtual MinecraftProcess *prepareForLaunch(LoginResponse response);
virtual void cleanupAfterRun();
- virtual QDialog * createModEditDialog ( QWidget* parent );
-
+ virtual QDialog *createModEditDialog(QWidget *parent);
+
virtual QString defaultBaseJar() const;
virtual QString defaultCustomBaseJar() const;
-
- bool menuActionEnabled ( QString action_name ) const;
+
+ bool menuActionEnabled(QString action_name) const;
virtual QString getStatusbarDescription();
-
-protected slots:
+
+protected
+slots:
virtual void jarModsChanged();
}; \ No newline at end of file
diff --git a/logic/LegacyInstance_p.h b/logic/LegacyInstance_p.h
index d1f417fe..0809b8d2 100644
--- a/logic/LegacyInstance_p.h
+++ b/logic/LegacyInstance_p.h
@@ -9,8 +9,8 @@ class ModList;
struct LegacyInstancePrivate: public BaseInstancePrivate
{
- QSharedPointer<ModList> jar_mod_list;
- QSharedPointer<ModList> core_mod_list;
- QSharedPointer<ModList> loader_mod_list;
- QSharedPointer<ModList> texture_pack_list;
+ std::shared_ptr<ModList> jar_mod_list;
+ std::shared_ptr<ModList> core_mod_list;
+ std::shared_ptr<ModList> loader_mod_list;
+ std::shared_ptr<ModList> texture_pack_list;
}; \ No newline at end of file
diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp
index 626ad1e0..66b4bf8a 100644
--- a/logic/LegacyUpdate.cpp
+++ b/logic/LegacyUpdate.cpp
@@ -9,9 +9,11 @@
#include <quazip.h>
#include <quazipfile.h>
#include <JlCompress.h>
+#include <logger/QsLog.h>
-
-LegacyUpdate::LegacyUpdate ( BaseInstance* inst, QObject* parent ) : BaseUpdate ( inst, parent ) {}
+LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : BaseUpdate(inst, parent)
+{
+}
void LegacyUpdate::executeTask()
{
@@ -20,85 +22,89 @@ void LegacyUpdate::executeTask()
void LegacyUpdate::lwjglStart()
{
- LegacyInstance * inst = (LegacyInstance *) m_inst;
+ LegacyInstance *inst = (LegacyInstance *)m_inst;
+
+ lwjglVersion = inst->lwjglVersion();
+ lwjglTargetPath = PathCombine("lwjgl", lwjglVersion);
+ lwjglNativesPath = PathCombine(lwjglTargetPath, "natives");
- lwjglVersion = inst->lwjglVersion();
- lwjglTargetPath = PathCombine("lwjgl", lwjglVersion );
- lwjglNativesPath = PathCombine( lwjglTargetPath, "natives");
-
// if the 'done' file exists, we don't have to download this again
QFileInfo doneFile(PathCombine(lwjglTargetPath, "done"));
- if(doneFile.exists())
+ if (doneFile.exists())
{
jarStart();
return;
}
-
- auto &list = LWJGLVersionList::get();
- if(!list.isLoaded())
+
+ auto list = MMC->lwjgllist();
+ if (!list->isLoaded())
{
emitFailed("Too soon! Let the LWJGL list load :)");
return;
}
-
+
setStatus("Downloading new LWJGL.");
- auto version = list.getVersion(lwjglVersion);
- if(!version)
+ auto version = list->getVersion(lwjglVersion);
+ if (!version)
{
emitFailed("Game update failed: the selected LWJGL version is invalid.");
return;
}
-
+
QString url = version->url();
QUrl realUrl(url);
QString hostname = realUrl.host();
auto worker = MMC->qnam();
QNetworkRequest req(realUrl);
req.setRawHeader("Host", hostname.toLatin1());
- req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)");
- QNetworkReply * rep = worker->get ( req );
-
- m_reply = QSharedPointer<QNetworkReply> (rep, &QObject::deleteLater);
- connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64)));
- connect(worker, SIGNAL(finished(QNetworkReply*)), SLOT(lwjglFinished(QNetworkReply*)));
- //connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError)));
+ req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
+ QNetworkReply *rep = worker->get(req);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ connect(worker.get(), SIGNAL(finished(QNetworkReply *)),
+ SLOT(lwjglFinished(QNetworkReply *)));
+ // connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ // SLOT(downloadError(QNetworkReply::NetworkError)));
}
-void LegacyUpdate::lwjglFinished(QNetworkReply* reply)
+void LegacyUpdate::lwjglFinished(QNetworkReply *reply)
{
- if(m_reply != reply)
+ if (m_reply.get() != reply)
{
return;
}
- if(reply->error() != QNetworkReply::NoError)
+ if (reply->error() != QNetworkReply::NoError)
{
- emitFailed( "Failed to download: "+
- reply->errorString()+
- "\nSometimes you have to wait a bit if you download many LWJGL versions in a row. YMMV");
+ emitFailed("Failed to download: " + reply->errorString() +
+ "\nSometimes you have to wait a bit if you download many LWJGL versions in "
+ "a row. YMMV");
return;
}
- auto *worker = MMC->qnam();
- //Here i check if there is a cookie for me in the reply and extract it
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie>>(reply->header(QNetworkRequest::SetCookieHeader));
- if(cookies.count() != 0)
+ auto worker = MMC->qnam();
+ // Here i check if there is a cookie for me in the reply and extract it
+ QList<QNetworkCookie> cookies =
+ qvariant_cast<QList<QNetworkCookie>>(reply->header(QNetworkRequest::SetCookieHeader));
+ if (cookies.count() != 0)
{
- //you must tell which cookie goes with which url
+ // you must tell which cookie goes with which url
worker->cookieJar()->setCookiesFromUrl(cookies, QUrl("sourceforge.net"));
}
- //here you can check for the 302 or whatever other header i need
+ // here you can check for the 302 or whatever other header i need
QVariant newLoc = reply->header(QNetworkRequest::LocationHeader);
- if(newLoc.isValid())
+ if (newLoc.isValid())
{
QString redirectedTo = reply->header(QNetworkRequest::LocationHeader).toString();
QUrl realUrl(redirectedTo);
QString hostname = realUrl.host();
QNetworkRequest req(redirectedTo);
req.setRawHeader("Host", hostname.toLatin1());
- req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)");
- QNetworkReply * rep = worker->get(req);
- connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64)));
- m_reply = QSharedPointer<QNetworkReply> (rep, &QObject::deleteLater);
+ req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
+ QNetworkReply *rep = worker->get(req);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
return;
}
QFile saveMe("lwjgl.zip");
@@ -114,26 +120,26 @@ void LegacyUpdate::extractLwjgl()
// make sure the directories are there
bool success = ensureFolderPathExists(lwjglNativesPath);
-
- if(!success)
+
+ if (!success)
{
emitFailed("Failed to extract the lwjgl libs - error when creating required folders.");
return;
}
-
+
QuaZip zip("lwjgl.zip");
- if(!zip.open(QuaZip::mdUnzip))
+ if (!zip.open(QuaZip::mdUnzip))
{
emitFailed("Failed to extract the lwjgl libs - not a valid archive.");
return;
}
-
+
// and now we are going to access files inside it
QuaZipFile file(&zip);
- const QString jarNames[] = { "jinput.jar", "lwjgl_util.jar", "lwjgl.jar" };
- for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile())
+ const QString jarNames[] = {"jinput.jar", "lwjgl_util.jar", "lwjgl.jar"};
+ for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile())
{
- if(!file.open(QIODevice::ReadOnly))
+ if (!file.open(QIODevice::ReadOnly))
{
zip.close();
emitFailed("Failed to extract the lwjgl libs - error while reading archive.");
@@ -141,7 +147,7 @@ void LegacyUpdate::extractLwjgl()
}
QuaZipFileInfo info;
QString name = file.getActualFileName();
- if(name.endsWith('/'))
+ if (name.endsWith('/'))
{
file.close();
continue;
@@ -156,25 +162,25 @@ void LegacyUpdate::extractLwjgl()
}
}
// Not found? look for the natives
- if(destFileName.isEmpty())
+ if (destFileName.isEmpty())
{
#ifdef Q_OS_WIN32
QString nativesDir = "windows";
#else
- #ifdef Q_OS_MAC
+#ifdef Q_OS_MAC
QString nativesDir = "macosx";
- #else
+#else
QString nativesDir = "linux";
- #endif
+#endif
#endif
if (name.contains(nativesDir))
{
int lastSlash = name.lastIndexOf('/');
- int lastBackSlash = name.lastIndexOf('/');
- if(lastSlash != -1)
- name = name.mid(lastSlash+1);
- else if(lastBackSlash != -1)
- name = name.mid(lastBackSlash+1);
+ int lastBackSlash = name.lastIndexOf('\\');
+ if (lastSlash != -1)
+ name = name.mid(lastSlash + 1);
+ else if (lastBackSlash != -1)
+ name = name.mid(lastBackSlash + 1);
destFileName = PathCombine(lwjglNativesPath, name);
}
}
@@ -190,7 +196,7 @@ void LegacyUpdate::extractLwjgl()
file.close(); // do not forget to close!
}
zip.close();
- m_reply.clear();
+ m_reply.reset();
QFile doneFile(PathCombine(lwjglTargetPath, "done"));
doneFile.open(QIODevice::WriteOnly);
doneFile.write("done.");
@@ -204,13 +210,13 @@ void LegacyUpdate::lwjglFailed()
void LegacyUpdate::jarStart()
{
- LegacyInstance * inst = (LegacyInstance *) m_inst;
- if(!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
+ LegacyInstance *inst = (LegacyInstance *)m_inst;
+ if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
{
ModTheJar();
return;
}
-
+
setStatus("Checking for jar updates...");
// Make directories
QDir binDir(inst->binDir());
@@ -226,11 +232,13 @@ void LegacyUpdate::jarStart()
QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/");
QString intended_version_id = inst->intendedVersionId();
urlstr += intended_version_id + "/" + intended_version_id + ".jar";
-
- legacyDownloadJob.reset(new DownloadJob(QUrl(urlstr), inst->defaultBaseJar()));
- connect(legacyDownloadJob.data(), SIGNAL(finished()), SLOT(jarFinished()));
- connect(legacyDownloadJob.data(), SIGNAL(failed()), SLOT(jarFailed()));
- connect(legacyDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64)));
+
+ auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id);
+ dljob->addFileDownload(QUrl(urlstr), inst->defaultBaseJar());
+ legacyDownloadJob.reset(dljob);
+ connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished()));
+ connect(dljob, SIGNAL(failed()), SLOT(jarFailed()));
+ connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
legacyDownloadJob->start();
}
@@ -246,34 +254,36 @@ void LegacyUpdate::jarFailed()
emitFailed("Failed to download the minecraft jar. Try again later.");
}
-bool LegacyUpdate::MergeZipFiles( QuaZip* into, QFileInfo from, QSet< QString >& contained, MetainfAction metainf )
+bool LegacyUpdate::MergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
+ MetainfAction metainf)
{
setStatus("Installing mods - Adding " + from.fileName());
-
+
QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip);
-
+
QuaZipFile fileInsideMod(&modZip);
- QuaZipFile zipOutFile( into );
- for(bool more=modZip.goToFirstFile(); more; more=modZip.goToNextFile())
+ QuaZipFile zipOutFile(into);
+ for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
{
QString filename = modZip.getCurrentFileName();
- if(filename.contains("META-INF") && metainf == LegacyUpdate::IgnoreMetainf)
+ if (filename.contains("META-INF") && metainf == LegacyUpdate::IgnoreMetainf)
{
- qDebug() << "Skipping META-INF " << filename << " from " << from.fileName();
+ QLOG_INFO() << "Skipping META-INF " << filename << " from " << from.fileName();
continue;
}
- if(contained.contains(filename))
+ if (contained.contains(filename))
{
- qDebug() << "Skipping already contained file " << filename << " from " << from.fileName();
+ QLOG_INFO() << "Skipping already contained file " << filename << " from "
+ << from.fileName();
continue;
}
contained.insert(filename);
- qDebug() << "Adding file " << filename << " from " << from.fileName();
-
- if(!fileInsideMod.open(QIODevice::ReadOnly))
+ QLOG_INFO() << "Adding file " << filename << " from " << from.fileName();
+
+ if (!fileInsideMod.open(QIODevice::ReadOnly))
{
- qDebug() << "Failed to open " << filename << " from " << from.fileName();
+ QLOG_ERROR() << "Failed to open " << filename << " from " << from.fileName();
return false;
}
/*
@@ -284,17 +294,17 @@ bool LegacyUpdate::MergeZipFiles( QuaZip* into, QFileInfo from, QSet< QString >&
/*
info_out.externalAttr = old_info.externalAttr;
*/
- if(!zipOutFile.open(QIODevice::WriteOnly, info_out))
+ if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
{
- qDebug() << "Failed to open " << filename << " in the jar";
+ QLOG_ERROR() << "Failed to open " << filename << " in the jar";
fileInsideMod.close();
return false;
}
- if(!JlCompress::copyData(fileInsideMod, zipOutFile))
+ if (!JlCompress::copyData(fileInsideMod, zipOutFile))
{
zipOutFile.close();
fileInsideMod.close();
- qDebug() << "Failed to copy data of " << filename << " into the jar";
+ QLOG_ERROR() << "Failed to copy data of " << filename << " into the jar";
return false;
}
zipOutFile.close();
@@ -305,34 +315,34 @@ bool LegacyUpdate::MergeZipFiles( QuaZip* into, QFileInfo from, QSet< QString >&
void LegacyUpdate::ModTheJar()
{
- LegacyInstance * inst = (LegacyInstance *) m_inst;
-
- if(!inst->shouldRebuild())
+ LegacyInstance *inst = (LegacyInstance *)m_inst;
+
+ if (!inst->shouldRebuild())
{
emitSucceeded();
return;
}
-
+
// Get the mod list
auto modList = inst->jarModList();
-
- QFileInfo runnableJar (inst->runnableJar());
- QFileInfo baseJar (inst->baseJar());
+
+ QFileInfo runnableJar(inst->runnableJar());
+ QFileInfo baseJar(inst->baseJar());
bool base_is_custom = inst->shouldUseCustomBaseJar();
-
+
// Nothing to do if there are no jar mods to install, no backup and just the mc jar
- if(base_is_custom)
+ if (base_is_custom)
{
// yes, this can happen if the instance only has the runnable jar and not the base jar
// it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
// because that's not something mmc4 guarantees
- if(runnableJar.isFile() && !baseJar.exists() && modList->empty())
+ if (runnableJar.isFile() && !baseJar.exists() && modList->empty())
{
inst->setShouldRebuild(false);
emitSucceeded();
return;
}
-
+
setStatus("Installing mods - backing up minecraft.jar...");
if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
{
@@ -340,24 +350,24 @@ void LegacyUpdate::ModTheJar()
return;
}
}
-
+
if (!baseJar.exists())
{
emitFailed("The base jar " + baseJar.filePath() + " does not exist");
return;
}
-
+
if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
{
emitFailed("Failed to delete old minecraft.jar");
return;
}
-
- //TaskStep(); // STEP 1
+
+ // TaskStep(); // STEP 1
setStatus("Installing mods - Opening minecraft.jar");
QuaZip zipOut(runnableJar.filePath());
- if(!zipOut.open(QuaZip::mdCreate))
+ if (!zipOut.open(QuaZip::mdCreate))
{
QFile::remove(runnableJar.filePath());
emitFailed("Failed to open the minecraft.jar for modding");
@@ -374,7 +384,7 @@ void LegacyUpdate::ModTheJar()
auto &mod = modList->operator[](i);
if (mod.type() == Mod::MOD_ZIPFILE)
{
- if(!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf))
+ if (!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf))
{
zipOut.close();
QFile::remove(runnableJar.filePath());
@@ -385,7 +395,8 @@ void LegacyUpdate::ModTheJar()
else if (mod.type() == Mod::MOD_SINGLEFILE)
{
auto filename = mod.filename();
- if(!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
+ if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(),
+ filename.fileName()))
{
zipOut.close();
QFile::remove(runnableJar.filePath());
@@ -393,7 +404,8 @@ void LegacyUpdate::ModTheJar()
return;
}
addedFiles.insert(filename.fileName());
- qDebug() << "Adding file " << filename.fileName() << " from " << filename.absoluteFilePath();
+ QLOG_INFO() << "Adding file " << filename.fileName() << " from "
+ << filename.absoluteFilePath();
}
else if (mod.type() == Mod::MOD_FOLDER)
{
@@ -402,35 +414,36 @@ void LegacyUpdate::ModTheJar()
QDir dir(what_to_zip);
dir.cdUp();
QString parent_dir = dir.absolutePath();
- if(!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, true, addedFiles))
+ if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, true, addedFiles))
{
zipOut.close();
QFile::remove(runnableJar.filePath());
emitFailed("Failed to add " + filename.fileName() + " to the jar");
return;
}
- qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath();
+ QLOG_INFO() << "Adding folder " << filename.fileName() << " from "
+ << filename.absoluteFilePath();
}
}
-
- if(!MergeZipFiles(&zipOut, baseJar, addedFiles, LegacyUpdate::IgnoreMetainf))
+
+ if (!MergeZipFiles(&zipOut, baseJar, addedFiles, LegacyUpdate::IgnoreMetainf))
{
zipOut.close();
QFile::remove(runnableJar.filePath());
emitFailed("Failed to insert minecraft.jar contents.");
return;
}
-
+
// Recompress the jar
zipOut.close();
- if(zipOut.getZipError()!=0)
+ if (zipOut.getZipError() != 0)
{
QFile::remove(runnableJar.filePath());
emitFailed("Failed to finalize minecraft.jar!");
return;
- }
+ }
inst->setShouldRebuild(false);
- //inst->UpdateVersion(true);
+ // inst->UpdateVersion(true);
emitSucceeded();
return;
} \ No newline at end of file
diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h
index 05c00495..e84ec56a 100644
--- a/logic/LegacyUpdate.h
+++ b/logic/LegacyUpdate.h
@@ -56,7 +56,7 @@ private:
bool MergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString>& contained, MetainfAction metainf);
private:
- QSharedPointer<QNetworkReply> m_reply;
+ std::shared_ptr<QNetworkReply> m_reply;
// target version, determined during this task
// MinecraftVersion *targetVersion;
diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp
index d34be835..06b7a1f1 100644
--- a/logic/MinecraftProcess.cpp
+++ b/logic/MinecraftProcess.cpp
@@ -32,46 +32,45 @@
#define IBUS "@im=ibus"
// constructor
-MinecraftProcess::MinecraftProcess( BaseInstance* inst ) :
- m_instance(inst)
+MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
{
- connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(finish(int, QProcess::ExitStatus)));
-
+ connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
+ SLOT(finish(int, QProcess::ExitStatus)));
+
// prepare the process environment
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
-
+
#ifdef LINUX
// Strip IBus
if (env.value("XMODIFIERS").contains(IBUS))
env.insert("XMODIFIERS", env.value("XMODIFIERS").replace(IBUS, ""));
#endif
-
+
// export some infos
env.insert("INST_NAME", inst->name());
env.insert("INST_ID", inst->id());
env.insert("INST_DIR", QDir(inst->instanceRoot()).absolutePath());
-
+
this->setProcessEnvironment(env);
m_prepostlaunchprocess.setProcessEnvironment(env);
-
+
// std channels
connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr()));
connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut()));
}
-void MinecraftProcess::setMinecraftArguments ( QStringList args )
+void MinecraftProcess::setMinecraftArguments(QStringList args)
{
m_args = args;
}
-void MinecraftProcess::setMinecraftWorkdir ( QString path )
+void MinecraftProcess::setMinecraftWorkdir(QString path)
{
QDir mcDir(path);
this->setWorkingDirectory(mcDir.absolutePath());
m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath());
}
-
// console window
void MinecraftProcess::on_stdErr()
{
@@ -80,18 +79,14 @@ void MinecraftProcess::on_stdErr()
m_err_leftover.clear();
QStringList lines = str.split("\n");
bool complete = str.endsWith("\n");
-
- for(int i = 0; i < lines.size() - 1; i++)
+
+ for (int i = 0; i < lines.size() - 1; i++)
{
- QString & line = lines[i];
- MessageLevel::Enum level = MessageLevel::Error;
- if(line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]") )
- level = MessageLevel::Message;
- if(line.contains("[SEVERE]") || line.contains("[WARNING]") || line.contains("[STDERR]"))
- level = MessageLevel::Error;
- emit log(lines[i].toLocal8Bit(), level);
+ QString &line = lines[i];
+ emit log(line /*.replace(username, "<Username>").replace(sessionID, "<Session ID>")*/,
+ getLevel(line, MessageLevel::Error));
}
- if(!complete)
+ if (!complete)
m_err_leftover = lines.last();
}
@@ -102,13 +97,14 @@ void MinecraftProcess::on_stdOut()
m_out_leftover.clear();
QStringList lines = str.split("\n");
bool complete = str.endsWith("\n");
-
- for(int i = 0; i < lines.size() - 1; i++)
+
+ for (int i = 0; i < lines.size() - 1; i++)
{
- QString & line = lines[i];
- emit log(lines[i].toLocal8Bit(), MessageLevel::Message);
+ QString &line = lines[i];
+ emit log(line /*.replace(username, "<Username>").replace(sessionID, "<Session ID>")*/,
+ getLevel(line, MessageLevel::Message));
}
- if(!complete)
+ if (!complete)
m_out_leftover = lines.last();
}
@@ -117,13 +113,20 @@ void MinecraftProcess::finish(int code, ExitStatus status)
{
if (status != NormalExit)
{
- //TODO: error handling
+ // TODO: error handling
}
-
- emit log("Minecraft exited.");
-
+
+ // TODO: Localization
+
+ if (!killed)
+ //: Message displayed on instance exit
+ emit log(tr("Minecraft exited with exitcode %1.").arg(status));
+ else
+ //: Message displayed after the instance exits due to kill request
+ emit log(tr("Minecraft was killed by user."), MessageLevel::Error);
+
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
-
+
// run post-exit
if (!m_instance->settings().get("PostExitCommand").toString().isEmpty())
{
@@ -131,13 +134,19 @@ void MinecraftProcess::finish(int code, ExitStatus status)
m_prepostlaunchprocess.waitForFinished();
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
{
- //TODO: error handling
+ // TODO: error handling
}
}
m_instance->cleanupAfterRun();
emit ended();
}
+void MinecraftProcess::killMinecraft()
+{
+ killed = true;
+ kill();
+}
+
void MinecraftProcess::launch()
{
if (!m_instance->settings().get("PreLaunchCommand").toString().isEmpty())
@@ -146,24 +155,42 @@ void MinecraftProcess::launch()
m_prepostlaunchprocess.waitForFinished();
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
{
- //TODO: error handling
+ // TODO: error handling
return;
}
}
-
+
m_instance->setLastLaunch();
-
+
emit log(QString("Minecraft folder is: '%1'").arg(workingDirectory()));
QString JavaPath = m_instance->settings().get("JavaPath").toString();
emit log(QString("Java path: '%1'").arg(JavaPath));
- emit log(QString("Arguments: '%1'").arg(m_args.join("' '")));
+ emit log(QString("Arguments: '%1'").arg(
+ m_args.join("' '") /*.replace(username, "<Username>").replace(sessionID, "<Session
+ID>")*/));
start(JavaPath, m_args);
if (!waitForStarted())
{
- emit log("Could not launch minecraft!");
+ //: Error message displayed if instace can't start
+ emit log(tr("Could not launch minecraft!"));
return;
- //TODO: error handling
+ // TODO: error handling
}
}
+MessageLevel::Enum MinecraftProcess::getLevel(const QString &line, MessageLevel::Enum level)
+{
+ if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") ||
+ line.contains("[FINER]") || line.contains("[FINEST]"))
+ level = MessageLevel::Message;
+ if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
+ level = MessageLevel::Error;
+ if (line.contains("[WARNING]"))
+ level = MessageLevel::Warning;
+ if (line.contains("Exception in thread") || line.contains(" at "))
+ level = MessageLevel::Fatal;
+ if (line.contains("[DEBUG]"))
+ level = MessageLevel::Debug;
+ return level;
+} \ No newline at end of file
diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h
index 516bf986..a1dfa23f 100644
--- a/logic/MinecraftProcess.h
+++ b/logic/MinecraftProcess.h
@@ -59,6 +59,10 @@ public:
void setMinecraftArguments(QStringList args);
+ void killMinecraft();
+
+ inline void setLogin(QString user, QString sid) { username = user; sessionID = sid; }
+
signals:
/**
* @brief emitted when mc has finished and the PostLaunchCommand was run
@@ -83,4 +87,9 @@ protected slots:
void finish(int, QProcess::ExitStatus status);
void on_stdErr();
void on_stdOut();
+private:
+ bool killed;
+ MessageLevel::Enum getLevel(const QString &message, MessageLevel::Enum defaultLevel);
+ QString sessionID;
+ QString username;
};
diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h
index 27977262..53c2f5ef 100644
--- a/logic/MinecraftVersion.h
+++ b/logic/MinecraftVersion.h
@@ -15,17 +15,16 @@
#pragma once
-#include "InstanceVersion.h"
+#include "BaseVersion.h"
#include <QStringList>
-struct MinecraftVersion : public InstVersion
+struct MinecraftVersion : public BaseVersion
{
- // From InstVersion:
- /*
- QString m_descriptor;
- QString m_name;
- qint64 m_timestamp;
- */
+ /*!
+ * Gets the version's timestamp.
+ * This is primarily used for sorting versions in a list.
+ */
+ qint64 timestamp;
/// The URL that this version will be downloaded from. maybe.
QString download_url;
@@ -44,6 +43,20 @@ struct MinecraftVersion : public InstVersion
/// is this a snapshot?
bool is_snapshot = false;
+ QString m_name;
+
+ QString m_descriptor;
+
+ virtual QString descriptor()
+ {
+ return m_descriptor;
+ }
+
+ virtual QString name()
+ {
+ return m_name;
+ }
+
virtual QString typeString() const
{
QStringList pre_final;
diff --git a/logic/Mod.cpp b/logic/Mod.cpp
index 38faa760..c45e3ad2 100644
--- a/logic/Mod.cpp
+++ b/logic/Mod.cpp
@@ -1,12 +1,12 @@
-//
+//
// Copyright 2012 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.
@@ -20,26 +20,25 @@
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
-#include <QDebug>
#include <quazip.h>
#include <quazipfile.h>
#include "Mod.h"
#include <pathutils.h>
#include <inifile.h>
+#include <logger/QsLog.h>
-
-Mod::Mod( const QFileInfo& file )
+Mod::Mod(const QFileInfo &file)
{
repath(file);
}
-void Mod::repath ( const QFileInfo& file )
+void Mod::repath(const QFileInfo &file)
{
m_file = file;
m_name = file.completeBaseName();
m_id = file.fileName();
-
+
m_type = Mod::MOD_UNKNOWN;
if (m_file.isDir())
m_type = MOD_FOLDER;
@@ -51,19 +50,19 @@ void Mod::repath ( const QFileInfo& file )
else
m_type = MOD_SINGLEFILE;
}
- if(m_type == MOD_ZIPFILE)
+ if (m_type == MOD_ZIPFILE)
{
QuaZip zip(m_file.filePath());
- if(!zip.open(QuaZip::mdUnzip))
+ if (!zip.open(QuaZip::mdUnzip))
return;
-
+
QuaZipFile file(&zip);
- for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile())
+ for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile())
{
QString name = zip.getCurrentFileName();
- if(name == "mcmod.info")
+ if (name == "mcmod.info")
{
- if(!file.open(QIODevice::ReadOnly))
+ if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
@@ -73,9 +72,9 @@ void Mod::repath ( const QFileInfo& file )
zip.close();
return;
}
- else if(name == "forgeversion.properties")
+ else if (name == "forgeversion.properties")
{
- if(!file.open(QIODevice::ReadOnly))
+ if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
@@ -88,16 +87,16 @@ void Mod::repath ( const QFileInfo& file )
}
zip.close();
}
- else if(m_type == MOD_FOLDER)
+ else if (m_type == MOD_FOLDER)
{
QFileInfo mcmod_info(PathCombine(m_file.filePath(), "mcmod.info"));
if (mcmod_info.isFile())
{
QFile mcmod(mcmod_info.filePath());
- if(!mcmod.open(QIODevice::ReadOnly))
+ if (!mcmod.open(QIODevice::ReadOnly))
return;
auto data = mcmod.readAll();
- if(data.isEmpty() || data.isNull())
+ if (data.isEmpty() || data.isNull())
return;
ReadMCModInfo(data);
}
@@ -111,35 +110,49 @@ void Mod::repath ( const QFileInfo& file )
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
void Mod::ReadMCModInfo(QByteArray contents)
{
- auto getInfoFromArray = [&]( QJsonArray arr ) -> void
+ auto getInfoFromArray = [&](QJsonArray arr)->void
{
- if(!arr.at(0).isObject())
+ if (!arr.at(0).isObject())
return;
auto firstObj = arr.at(0).toObject();
m_id = firstObj.value("modid").toString();
m_name = firstObj.value("name").toString();
m_version = firstObj.value("version").toString();
+ m_homeurl = firstObj.value("url").toString();
+ m_description = firstObj.value("description").toString();
+ QJsonArray authors = firstObj.value("authors").toArray();
+ if(authors.size() == 0) m_authors = "";
+ else if(authors.size() >= 1)
+ {
+ m_authors = authors.at(0).toString();
+ for(int i = 1; i < authors.size(); i++)
+ {
+ m_authors += ", " + authors.at(i).toString();
+ }
+ }
+ m_credits = firstObj.value("credits").toString();
return;
- };
+ }
+ ;
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
// this is the very old format that had just the array
- if(jsonDoc.isArray())
+ if (jsonDoc.isArray())
{
getInfoFromArray(jsonDoc.array());
}
- else if(jsonDoc.isObject())
+ else if (jsonDoc.isObject())
{
auto val = jsonDoc.object().value("modinfoversion");
int version = val.toDouble();
- if(version != 2)
+ if (version != 2)
{
- qDebug() << "BAD stuff happened to mod json:";
- qDebug() << contents;
+ QLOG_ERROR() << "BAD stuff happened to mod json:";
+ QLOG_ERROR() << contents;
return;
}
auto arrVal = jsonDoc.object().value("modlist");
- if(arrVal.isArray())
+ if (arrVal.isArray())
{
getInfoFromArray(arrVal.toArray());
}
@@ -151,33 +164,34 @@ void Mod::ReadForgeInfo(QByteArray contents)
// Read the data
m_name = "Minecraft Forge";
m_id = "Forge";
+ m_homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini;
- if(!ini.loadFile(contents))
+ if (!ini.loadFile(contents))
return;
-
- QString major = ini.get("forge.major.number","0").toString();
- QString minor = ini.get("forge.minor.number","0").toString();
- QString revision = ini.get("forge.revision.number","0").toString();
- QString build = ini.get("forge.build.number","0").toString();
-
+
+ QString major = ini.get("forge.major.number", "0").toString();
+ QString minor = ini.get("forge.minor.number", "0").toString();
+ QString revision = ini.get("forge.revision.number", "0").toString();
+ QString build = ini.get("forge.build.number", "0").toString();
+
m_version = major + "." + minor + "." + revision + "." + build;
}
-bool Mod::replace ( Mod& with )
+bool Mod::replace(Mod &with)
{
- if(!destroy())
+ if (!destroy())
return false;
bool success = false;
auto t = with.type();
- if(t == MOD_ZIPFILE || t == MOD_SINGLEFILE)
+ if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE)
{
success = QFile::copy(with.m_file.filePath(), m_file.path());
}
- if(t == MOD_FOLDER)
+ if (t == MOD_FOLDER)
{
success = copyPath(with.m_file.filePath(), m_file.path());
}
- if(success)
+ if (success)
{
m_id = with.m_id;
m_mcversion = with.m_mcversion;
@@ -190,10 +204,10 @@ bool Mod::replace ( Mod& with )
bool Mod::destroy()
{
- if(m_type == MOD_FOLDER)
+ if (m_type == MOD_FOLDER)
{
QDir d(m_file.filePath());
- if(d.removeRecursively())
+ if (d.removeRecursively())
{
m_type = MOD_UNKNOWN;
return true;
@@ -203,7 +217,7 @@ bool Mod::destroy()
else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE)
{
QFile f(m_file.filePath());
- if(f.remove())
+ if (f.remove())
{
m_type = MOD_UNKNOWN;
return true;
@@ -213,18 +227,17 @@ bool Mod::destroy()
return true;
}
-
QString Mod::version() const
{
- switch(type())
+ switch (type())
{
- case MOD_ZIPFILE:
- return m_version;
- case MOD_FOLDER:
- return "Folder";
- case MOD_SINGLEFILE:
- return "File";
- default:
- return "VOID";
+ case MOD_ZIPFILE:
+ return m_version;
+ case MOD_FOLDER:
+ return "Folder";
+ case MOD_SINGLEFILE:
+ return "File";
+ default:
+ return "VOID";
}
}
diff --git a/logic/Mod.h b/logic/Mod.h
index fcfcc139..f3aaf18b 100644
--- a/logic/Mod.h
+++ b/logic/Mod.h
@@ -1,12 +1,12 @@
-//
+//
// Copyright 2012 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.
@@ -22,46 +22,87 @@ class Mod
public:
enum ModType
{
- MOD_UNKNOWN, //!< Indicates an unspecified mod type.
- MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files.
+ MOD_UNKNOWN, //!< Indicates an unspecified mod type.
+ MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files.
MOD_SINGLEFILE, //!< The mod is a single file (not a zip file).
- MOD_FOLDER, //!< The mod is in a folder on the filesystem.
+ MOD_FOLDER, //!< The mod is in a folder on the filesystem.
};
Mod(const QFileInfo &file);
-
- QFileInfo filename() const { return m_file; }
- QString id() const { return m_id; }
- ModType type() const { return m_type; }
- QString mcversion() const { return m_mcversion; };
- bool valid() {return m_type != MOD_UNKNOWN;}
- QString name() const {return m_name; };
-
+
+ QFileInfo filename() const
+ {
+ return m_file;
+ }
+ QString id() const
+ {
+ return m_id;
+ }
+ ModType type() const
+ {
+ return m_type;
+ }
+ QString mcversion() const
+ {
+ return m_mcversion;
+ }
+ ;
+ bool valid()
+ {
+ return m_type != MOD_UNKNOWN;
+ }
+ QString name() const
+ {
+ return m_name;
+ }
+
QString version() const;
-
-
+
+ QString homeurl() const
+ {
+ return m_homeurl;
+ }
+
+ QString description() const
+ {
+ return m_description;
+ }
+
+ QString authors() const
+ {
+ return m_authors;
+ }
+
+ QString credits() const
+ {
+ return m_credits;
+ }
+
// delete all the files of this mod
bool destroy();
// replace this mod with a copy of the other
- bool replace(Mod & with);
+ bool replace(Mod &with);
// change the mod's filesystem path (used by mod lists for *MAGIC* purposes)
void repath(const QFileInfo &file);
// WEAK compare operator - used for replacing mods
- bool operator ==(const Mod &other) const
+ bool operator==(const Mod &other) const
{
return filename() == other.filename();
}
bool strongCompare(const Mod &other) const
{
- return filename() == other.filename() && id() == other.id() && version() == other.version() && type() == other.type();
+ return filename() == other.filename() && id() == other.id() &&
+ version() == other.version() && type() == other.type();
}
+
private:
void ReadMCModInfo(QByteArray contents);
void ReadForgeInfo(QByteArray contents);
+
protected:
- //FIXME: what do do with those? HMM...
+ // FIXME: what do do with those? HMM...
/*
void ReadModInfoData(QString info);
void ReadForgeInfoData(QString infoFileData);
@@ -72,6 +113,10 @@ protected:
QString m_name;
QString m_version;
QString m_mcversion;
+ QString m_homeurl;
+ QString m_description;
+ QString m_authors;
+ QString m_credits;
ModType m_type;
};
diff --git a/logic/ModList.cpp b/logic/ModList.cpp
index 84511e4c..236f0db6 100644
--- a/logic/ModList.cpp
+++ b/logic/ModList.cpp
@@ -1,12 +1,12 @@
-//
+//
// 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.
@@ -19,60 +19,60 @@
#include <pathutils.h>
#include <QMimeData>
#include <QUrl>
-#include <QDebug>
#include <QUuid>
#include <QFileSystemWatcher>
+#include <logger/QsLog.h>
-ModList::ModList ( const QString& dir, const QString& list_file )
-: QAbstractListModel(), m_dir(dir), m_list_file(list_file)
+ModList::ModList(const QString &dir, const QString &list_file)
+ : QAbstractListModel(), m_dir(dir), m_list_file(list_file)
{
- m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks);
+ m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
+ QDir::NoSymLinks);
m_dir.setSorting(QDir::Name);
m_list_id = QUuid::createUuid().toString();
m_watcher = new QFileSystemWatcher(this);
is_watching = false;
- connect(m_watcher,SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString)));
- update();
+ connect(m_watcher, SIGNAL(directoryChanged(QString)), this,
+ SLOT(directoryChanged(QString)));
}
void ModList::startWatching()
{
is_watching = m_watcher->addPath(m_dir.absolutePath());
- if(is_watching)
- qDebug() << "Started watching " << m_dir.absolutePath();
+ if (is_watching)
+ QLOG_INFO() << "Started watching " << m_dir.absolutePath();
else
- qDebug() << "Failed to start watching " << m_dir.absolutePath();
+ QLOG_INFO() << "Failed to start watching " << m_dir.absolutePath();
}
void ModList::stopWatching()
{
is_watching = !m_watcher->removePath(m_dir.absolutePath());
- if(!is_watching)
- qDebug() << "Stopped watching " << m_dir.absolutePath();
+ if (!is_watching)
+ QLOG_INFO() << "Stopped watching " << m_dir.absolutePath();
else
- qDebug() << "Failed to stop watching " << m_dir.absolutePath();
+ QLOG_INFO() << "Failed to stop watching " << m_dir.absolutePath();
}
-
bool ModList::update()
{
if (!isValid())
return false;
-
+
QList<Mod> newMods;
m_dir.refresh();
auto folderContents = m_dir.entryInfoList();
bool orderWasInvalid = false;
-
+
// first, process the ordered items (if any)
int currentOrderIndex = 0;
QStringList listOrder = readListFile();
- for(auto item: listOrder)
+ for (auto item : listOrder)
{
- QFileInfo info (m_dir.filePath(item));
+ QFileInfo info(m_dir.filePath(item));
int idx = folderContents.indexOf(info);
// if the file from the index file exists
- if(idx != -1)
+ if (idx != -1)
{
// remove from the actual folder contents list
folderContents.takeAt(idx);
@@ -84,26 +84,27 @@ bool ModList::update()
orderWasInvalid = true;
}
}
- for(auto entry: folderContents)
+ for (auto entry : folderContents)
{
newMods.append(Mod(entry));
}
- if(mods.size() != newMods.size())
+ if (mods.size() != newMods.size())
{
orderWasInvalid = true;
}
- else for(int i = 0; i < mods.size(); i++)
- {
- if(!mods[i].strongCompare(newMods[i]))
+ else
+ for (int i = 0; i < mods.size(); i++)
{
- orderWasInvalid = true;
- break;
+ if (!mods[i].strongCompare(newMods[i]))
+ {
+ orderWasInvalid = true;
+ break;
+ }
}
- }
beginResetModel();
mods.swap(newMods);
endResetModel();
- if(orderWasInvalid)
+ if (orderWasInvalid)
{
saveListFile();
emit changed();
@@ -111,22 +112,21 @@ bool ModList::update()
return true;
}
-void ModList::directoryChanged ( QString path )
+void ModList::directoryChanged(QString path)
{
update();
}
-
QStringList ModList::readListFile()
{
QStringList stringList;
- if(m_list_file.isNull() || m_list_file.isEmpty())
+ if (m_list_file.isNull() || m_list_file.isEmpty())
return stringList;
-
+
QFile textFile(m_list_file);
- if(!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
return QStringList();
-
+
QTextStream textStream(&textFile);
while (true)
{
@@ -144,13 +144,13 @@ QStringList ModList::readListFile()
bool ModList::saveListFile()
{
- if(m_list_file.isNull() || m_list_file.isEmpty())
+ if (m_list_file.isNull() || m_list_file.isEmpty())
return false;
QFile textFile(m_list_file);
- if(!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
+ if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
return false;
QTextStream textStream(&textFile);
- for(auto mod:mods)
+ for (auto mod : mods)
{
auto pathname = mod.filename();
QString filename = pathname.fileName();
@@ -160,29 +160,28 @@ bool ModList::saveListFile()
return false;
}
-
bool ModList::isValid()
{
return m_dir.exists() && m_dir.isReadable();
}
-bool ModList::installMod ( const QFileInfo& filename, int index )
+bool ModList::installMod(const QFileInfo &filename, int index)
{
- if(!filename.exists() || !filename.isReadable() || index < 0)
+ if (!filename.exists() || !filename.isReadable() || index < 0)
{
return false;
}
Mod m(filename);
- if(!m.valid())
+ if (!m.valid())
return false;
-
+
// if it's already there, replace the original mod (in place)
int idx = mods.indexOf(m);
- if(idx != -1)
+ if (idx != -1)
{
- if(mods[idx].replace(m))
+ if (mods[idx].replace(m))
{
-
+
auto left = this->index(index);
auto right = this->index(index, columnCount(QModelIndex()) - 1);
emit dataChanged(left, right);
@@ -192,33 +191,33 @@ bool ModList::installMod ( const QFileInfo& filename, int index )
}
return false;
}
-
+
auto type = m.type();
- if(type == Mod::MOD_UNKNOWN)
+ if (type == Mod::MOD_UNKNOWN)
return false;
- if(type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE)
+ if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE)
{
QString newpath = PathCombine(m_dir.path(), filename.fileName());
- if(!QFile::copy(filename.filePath(), newpath))
+ if (!QFile::copy(filename.filePath(), newpath))
return false;
m.repath(newpath);
beginInsertRows(QModelIndex(), index, index);
- mods.insert(index,m);
+ mods.insert(index, m);
endInsertRows();
saveListFile();
emit changed();
return true;
}
- else if(type == Mod::MOD_FOLDER)
+ else if (type == Mod::MOD_FOLDER)
{
-
+
QString from = filename.filePath();
QString to = PathCombine(m_dir.path(), filename.fileName());
- if(!copyPath(from, to))
+ if (!copyPath(from, to))
return false;
m.repath(to);
beginInsertRows(QModelIndex(), index, index);
- mods.insert(index,m);
+ mods.insert(index, m);
endInsertRows();
saveListFile();
emit changed();
@@ -227,12 +226,12 @@ bool ModList::installMod ( const QFileInfo& filename, int index )
return false;
}
-bool ModList::deleteMod ( int index )
+bool ModList::deleteMod(int index)
{
- if(index >= mods.size() || index < 0)
+ if (index >= mods.size() || index < 0)
return false;
- Mod & m = mods[index];
- if(m.destroy())
+ Mod &m = mods[index];
+ if (m.destroy())
{
beginRemoveRows(QModelIndex(), index, index);
mods.removeAt(index);
@@ -244,11 +243,11 @@ bool ModList::deleteMod ( int index )
return false;
}
-bool ModList::deleteMods ( int first, int last )
+bool ModList::deleteMods(int first, int last)
{
- for(int i = first; i <= last; i++)
+ for (int i = first; i <= last; i++)
{
- Mod & m = mods[i];
+ Mod &m = mods[i];
m.destroy();
}
beginRemoveRows(QModelIndex(), first, last);
@@ -259,18 +258,17 @@ bool ModList::deleteMods ( int first, int last )
return true;
}
-
-bool ModList::moveModTo ( int from, int to )
+bool ModList::moveModTo(int from, int to)
{
- if(from < 0 || from >= mods.size())
+ if (from < 0 || from >= mods.size())
return false;
if (to >= rowCount())
to = rowCount() - 1;
if (to == -1)
to = rowCount() - 1;
- if(from == to)
+ if (from == to)
return false;
- int togap = to > from ? to + 1: to;
+ int togap = to > from ? to + 1 : to;
beginMoveRows(QModelIndex(), from, from, QModelIndex(), togap);
mods.move(from, to);
endMoveRows();
@@ -279,83 +277,81 @@ bool ModList::moveModTo ( int from, int to )
return true;
}
-bool ModList::moveModUp ( int from )
+bool ModList::moveModUp(int from)
{
- if(from > 0)
+ if (from > 0)
return moveModTo(from, from - 1);
return false;
}
-bool ModList::moveModsUp ( int first, int last )
+bool ModList::moveModsUp(int first, int last)
{
- if(first == 0)
+ if (first == 0)
return false;
-
+
beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
- mods.move(first-1, last);
+ mods.move(first - 1, last);
endMoveRows();
saveListFile();
emit changed();
return true;
}
-
-bool ModList::moveModDown ( int from )
+bool ModList::moveModDown(int from)
{
- if(from < 0)
+ if (from < 0)
return false;
- if(from < mods.size() - 1)
+ if (from < mods.size() - 1)
return moveModTo(from, from + 1);
return false;
}
-bool ModList::moveModsDown ( int first, int last )
+bool ModList::moveModsDown(int first, int last)
{
- if(last == mods.size() - 1)
+ if (last == mods.size() - 1)
return false;
-
+
beginMoveRows(QModelIndex(), first, last, QModelIndex(), last + 2);
- mods.move(last+1, first);
+ mods.move(last + 1, first);
endMoveRows();
saveListFile();
emit changed();
return true;
}
-
-int ModList::columnCount ( const QModelIndex& parent ) const
+int ModList::columnCount(const QModelIndex &parent) const
{
return 2;
}
-QVariant ModList::data ( const QModelIndex& index, int role ) const
+QVariant ModList::data(const QModelIndex &index, int role) const
{
- if(!index.isValid())
+ if (!index.isValid())
return QVariant();
-
+
int row = index.row();
int column = index.column();
-
- if(row < 0 || row >= mods.size())
+
+ if (row < 0 || row >= mods.size())
return QVariant();
-
- if(role != Qt::DisplayRole)
+
+ if (role != Qt::DisplayRole)
return QVariant();
-
- switch(column)
+
+ switch (column)
{
- case 0:
- return mods[row].name();
- case 1:
- return mods[row].version();
- case 2:
- return mods[row].mcversion();
- default:
- return QVariant();
+ case 0:
+ return mods[row].name();
+ case 1:
+ return mods[row].version();
+ case 2:
+ return mods[row].mcversion();
+ default:
+ return QVariant();
}
}
-QVariant ModList::headerData ( int section, Qt::Orientation orientation, int role ) const
+QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
return QVariant();
@@ -370,10 +366,9 @@ QVariant ModList::headerData ( int section, Qt::Orientation orientation, int rol
}
}
-
-Qt::ItemFlags ModList::flags ( const QModelIndex& index ) const
+Qt::ItemFlags ModList::flags(const QModelIndex &index) const
{
- Qt::ItemFlags defaultFlags = QAbstractListModel::flags ( index );
+ Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
if (index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
else
@@ -400,78 +395,79 @@ Qt::DropActions ModList::supportedDragActions() const
return Qt::MoveAction;
}
-QMimeData* ModList::mimeData ( const QModelIndexList& indexes ) const
+QMimeData *ModList::mimeData(const QModelIndexList &indexes) const
{
- QMimeData * data = new QMimeData();
-
- if(indexes.size() == 0)
+ QMimeData *data = new QMimeData();
+
+ if (indexes.size() == 0)
return data;
-
+
auto idx = indexes[0];
int row = idx.row();
- if(row <0 || row >= mods.size())
+ if (row < 0 || row >= mods.size())
return data;
-
+
QStringList params;
params << m_list_id << QString::number(row);
data->setText(params.join('|'));
return data;
}
-bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent )
+bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
+ const QModelIndex &parent)
{
if (action == Qt::IgnoreAction)
- return true;
+ return true;
// check if the action is supported
if (!data || !(action & supportedDropActions()))
return false;
- if(parent.isValid())
+ if (parent.isValid())
{
row = parent.row();
column = parent.column();
}
-
+
if (row > rowCount())
row = rowCount();
if (row == -1)
row = rowCount();
if (column == -1)
column = 0;
- qDebug() << "Drop row: " << row << " column: " << column;
-
+ QLOG_INFO() << "Drop row: " << row << " column: " << column;
+
// files dropped from outside?
- if(data->hasUrls())
+ if (data->hasUrls())
{
bool was_watching = is_watching;
- if(was_watching)
+ if (was_watching)
stopWatching();
auto urls = data->urls();
- for(auto url: urls)
+ for (auto url : urls)
{
// only local files may be dropped...
- if(!url.isLocalFile())
+ if (!url.isLocalFile())
continue;
QString filename = url.toLocalFile();
installMod(filename, row);
- qDebug() << "installing: " << filename;
+ QLOG_INFO() << "installing: " << filename;
}
- if(was_watching)
+ if (was_watching)
startWatching();
return true;
}
- else if(data->hasText())
+ else if (data->hasText())
{
QString sourcestr = data->text();
auto list = sourcestr.split('|');
- if(list.size() != 2)
+ if (list.size() != 2)
return false;
QString remoteId = list[0];
int remoteIndex = list[1].toInt();
- qDebug() << "move: " << sourcestr;
+ QLOG_INFO() << "move: " << sourcestr;
// no moving of things between two lists
- if(remoteId != m_list_id)
+ if (remoteId != m_list_id)
return false;
// no point moving to the same place...
- if(row == remoteIndex)
+ if (row == remoteIndex)
return false;
// otherwise, move the mod :D
moveModTo(remoteIndex, row);
@@ -479,4 +475,3 @@ bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int r
}
return false;
}
-
diff --git a/logic/ModList.h b/logic/ModList.h
index 5395e9ae..e99b6c82 100644
--- a/logic/ModList.h
+++ b/logic/ModList.h
@@ -112,3 +112,6 @@ protected:
QString m_list_id;
QList<Mod> mods;
};
+
+
+
diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp
index 039cd9ce..efd8f46b 100644
--- a/logic/NostalgiaInstance.cpp
+++ b/logic/NostalgiaInstance.cpp
@@ -11,6 +11,10 @@ QString NostalgiaInstance::getStatusbarDescription()
return "Nostalgia : " + intendedVersionId();
}
+bool NostalgiaInstance::menuActionEnabled(QString action_name) const
+{
+ return false;
+}
/*
ADD MORE
diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h
index f2df1828..64eb7a81 100644
--- a/logic/NostalgiaInstance.h
+++ b/logic/NostalgiaInstance.h
@@ -8,4 +8,6 @@ class NostalgiaInstance : public OneSixInstance
public:
explicit NostalgiaInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0);
virtual QString getStatusbarDescription();
+ virtual bool menuActionEnabled(QString action_name) const;
};
+
diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp
index c65ee607..6aa0a207 100644
--- a/logic/OneSixAssets.cpp
+++ b/logic/OneSixAssets.cpp
@@ -1,8 +1,10 @@
#include <QString>
-#include <QDebug>
+#include <logger/QsLog.h>
#include <QtXml/QtXml>
#include "OneSixAssets.h"
#include "net/DownloadJob.h"
+#include "net/HttpMetaCache.h"
+#include "MultiMC.h"
inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
{
@@ -19,6 +21,7 @@ class ThreadedDeleter : public QThread
public:
void run()
{
+ QLOG_INFO() << "Cleaning up assets folder...";
QDirIterator iter ( m_base, QDirIterator::Subdirectories );
int base_length = m_base.length();
while ( iter.hasNext() )
@@ -32,12 +35,12 @@ public:
trimmedf.remove ( 0, base_length + 1 );
if ( m_whitelist.contains ( trimmedf ) )
{
- // qDebug() << trimmedf << " gets to live";
+ QLOG_TRACE() << trimmedf << " gets to live";
}
else
{
// DO NOT TOLERATE JUNK
- // qDebug() << trimmedf << " dies";
+ QLOG_TRACE() << trimmedf << " dies";
QFile f ( filename );
f.remove();
}
@@ -65,21 +68,25 @@ void OneSixAssets::fetchXMLFinished()
nuke_whitelist.clear();
auto firstJob = index_job->first();
- QByteArray ba = firstJob->m_data;
+ QByteArray ba = std::dynamic_pointer_cast<ByteArrayDownload>(firstJob)->m_data;
QString xmlErrorMsg;
QDomDocument doc;
if ( !doc.setContent ( ba, false, &xmlErrorMsg ) )
{
- qDebug() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << xmlErrorMsg << ba;
+ QLOG_ERROR() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << xmlErrorMsg << ba;
+ emit failed();
+ return;
}
//QRegExp etag_match(".*([a-f0-9]{32}).*");
QDomNodeList contents = doc.elementsByTagName ( "Contents" );
- DownloadJob *job = new DownloadJob();
+ DownloadJob *job = new DownloadJob("Assets");
connect ( job, SIGNAL(succeeded()), SLOT(downloadFinished()) );
connect ( job, SIGNAL(failed()), SIGNAL(failed()) );
+ auto metacache = MMC->metacache();
+
for ( int i = 0; i < contents.length(); i++ )
{
QDomElement element = contents.at ( i ).toElement();
@@ -104,22 +111,12 @@ void OneSixAssets::fetchXMLFinished()
if ( sizeStr == "0" )
continue;
- QString filename = fprefix + keyStr;
- QFile check_file ( filename );
- QString client_etag = "nonsense";
- // if there already is a file and md5 checking is in effect and it can be opened
- if ( check_file.exists() && check_file.open ( QIODevice::ReadOnly ) )
- {
- // check the md5 against the expected one
- client_etag = QCryptographicHash::hash ( check_file.readAll(), QCryptographicHash::Md5 ).toHex().constData();
- check_file.close();
- }
-
- QString trimmedEtag = etagStr.remove ( '"' );
nuke_whitelist.append ( keyStr );
- if(trimmedEtag != client_etag)
+
+ auto entry = metacache->resolveEntry("assets", keyStr, etagStr);
+ if(entry->stale)
{
- job->add ( QUrl ( prefix + keyStr ), filename );
+ job->addCacheDownload(QUrl(prefix + keyStr), entry);
}
}
if(job->size())
@@ -135,7 +132,8 @@ void OneSixAssets::fetchXMLFinished()
}
void OneSixAssets::start()
{
- DownloadJob * job = new DownloadJob(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" ));
+ auto job = new DownloadJob("Assets index");
+ job->addByteArrayDownload(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" ));
connect ( job, SIGNAL(succeeded()), SLOT ( fetchXMLFinished() ) );
index_job.reset ( job );
job->start();
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index c926df60..d80f6b37 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -2,16 +2,18 @@
#include "OneSixInstance_p.h"
#include "OneSixUpdate.h"
#include "MinecraftProcess.h"
-#include "VersionFactory.h"
+#include "OneSixVersion.h"
#include <setting.h>
#include <pathutils.h>
#include <cmdutils.h>
#include <JlCompress.h>
#include <gui/OneSixModEditDialog.h>
+#include <logger/QsLog.h>
-OneSixInstance::OneSixInstance ( const QString& rootDir, SettingsObject* setting_obj, QObject* parent )
-: BaseInstance ( new OneSixInstancePrivate(), rootDir, setting_obj, parent )
+OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj,
+ QObject *parent)
+ : BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent)
{
I_D(OneSixInstance);
d->m_settings->registerSetting(new Setting("IntendedVersion", ""));
@@ -19,7 +21,7 @@ OneSixInstance::OneSixInstance ( const QString& rootDir, SettingsObject* setting
reloadFullVersion();
}
-BaseUpdate* OneSixInstance::doUpdate()
+BaseUpdate *OneSixInstance::doUpdate()
{
return new OneSixUpdate(this);
}
@@ -34,10 +36,10 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
int head = 0;
while ((head = token_regexp.indexIn(text, head)) != -1)
{
- result.append(text.mid(tail, head-tail));
+ result.append(text.mid(tail, head - tail));
QString key = token_regexp.cap(1);
auto iter = with.find(key);
- if(iter != with.end())
+ if (iter != with.end())
{
result.append(*iter);
}
@@ -48,26 +50,27 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result;
}
-QStringList OneSixInstance::processMinecraftArgs( QString user, QString session )
+QStringList OneSixInstance::processMinecraftArgs(LoginResponse response)
{
I_D(OneSixInstance);
auto version = d->version;
QString args_pattern = version->minecraftArguments;
-
+
QMap<QString, QString> token_mapping;
- token_mapping["auth_username"] = user;
- token_mapping["auth_session"] = session;
- //FIXME: user and player name are DIFFERENT!
- token_mapping["auth_player_name"] = user;
- //FIXME: WTF is this. I just plugged in a random UUID here.
- token_mapping["auth_uuid"] = "7d4bacf0-fd62-11e2-b778-0800200c9a66"; // obviously fake.
-
- // this is for offline:
+ // yggdrasil!
+ token_mapping["auth_username"] = response.username;
+ token_mapping["auth_session"] = response.session_id;
+ token_mapping["auth_access_token"] = response.access_token;
+ token_mapping["auth_player_name"] = response.player_name;
+ token_mapping["auth_uuid"] = response.player_id;
+
+ // this is for offline?:
/*
map["auth_player_name"] = "Player";
map["auth_player_name"] = "00000000-0000-0000-0000-000000000000";
*/
-
+
+ // these do nothing and are stupid.
token_mapping["profile_name"] = name();
token_mapping["version_name"] = version->id;
@@ -75,8 +78,8 @@ QStringList OneSixInstance::processMinecraftArgs( QString user, QString session
token_mapping["game_directory"] = absRootDir;
QString absAssetsDir = QDir("assets/").absolutePath();
token_mapping["game_assets"] = absAssetsDir;
-
- QStringList parts = args_pattern.split(' ',QString::SkipEmptyParts);
+
+ QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
for (int i = 0; i < parts.length(); i++)
{
parts[i] = replaceTokensIn(parts[i], token_mapping);
@@ -84,27 +87,28 @@ QStringList OneSixInstance::processMinecraftArgs( QString user, QString session
return parts;
}
-MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString session )
+MinecraftProcess *OneSixInstance::prepareForLaunch(LoginResponse response)
{
I_D(OneSixInstance);
cleanupAfterRun();
auto version = d->version;
- if(!version)
+ if (!version)
return nullptr;
auto libs_to_extract = version->getActiveNativeLibs();
QString natives_dir_raw = PathCombine(instanceRoot(), "natives/");
bool success = ensureFolderPathExists(natives_dir_raw);
- if(!success)
+ if (!success)
{
// FIXME: handle errors
return nullptr;
}
-
- for(auto lib: libs_to_extract)
+
+ for (auto lib : libs_to_extract)
{
QString path = "libraries/" + lib->storagePath();
- qDebug() << "Will extract " << path.toLocal8Bit();
- if(JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes).isEmpty())
+ QLOG_INFO() << "Will extract " << path.toLocal8Bit();
+ if (JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes)
+ .isEmpty())
{
return nullptr;
}
@@ -116,11 +120,11 @@ MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString sessi
args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt());
args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt());
QDir natives_dir(natives_dir_raw);
- args << QString("-Djava.library.path=%1").arg( natives_dir.absolutePath() );
+ args << QString("-Djava.library.path=%1").arg(natives_dir.absolutePath());
QString classPath;
{
auto libs = version->getActiveNormalLibs();
- for (auto lib: libs)
+ for (auto lib : libs)
{
QFileInfo fi(QString("libraries/") + lib->storagePath());
classPath.append(fi.absoluteFilePath());
@@ -134,16 +138,29 @@ MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString sessi
QFileInfo fi(targetstr);
classPath.append(fi.absoluteFilePath());
}
- if(classPath.size())
+ if (classPath.size())
{
args << "-cp";
args << classPath;
}
args << version->mainClass;
- args.append(processMinecraftArgs(user, session));
-
+ args.append(processMinecraftArgs(response));
+
+ // Set the width and height for 1.6 instances
+ bool maximize = settings().get("LaunchMaximized").toBool();
+ if(maximize)
+ {
+ // this is probably a BAD idea
+ // args << QString("--fullscreen");
+ }
+ else
+ {
+ args << QString("--width") << settings().get("MinecraftWinWidth").toString();
+ args << QString("--height") << settings().get("MinecraftWinHeight").toString();
+ }
+
// create the process and set its parameters
- MinecraftProcess * proc = new MinecraftProcess(this);
+ MinecraftProcess *proc = new MinecraftProcess(this);
proc->setMinecraftArguments(args);
proc->setMinecraftWorkdir(minecraftRoot());
return proc;
@@ -156,40 +173,42 @@ void OneSixInstance::cleanupAfterRun()
dir.removeRecursively();
}
-QSharedPointer< ModList > OneSixInstance::loaderModList()
+std::shared_ptr<ModList> OneSixInstance::loaderModList()
{
I_D(OneSixInstance);
- if(!d->loader_mod_list)
+ if (!d->loader_mod_list)
{
d->loader_mod_list.reset(new ModList(loaderModsDir()));
}
- else
- d->loader_mod_list->update();
+ d->loader_mod_list->update();
return d->loader_mod_list;
}
-QSharedPointer< ModList > OneSixInstance::resourcePackList()
+std::shared_ptr<ModList> OneSixInstance::resourcePackList()
{
I_D(OneSixInstance);
- if(!d->resource_pack_list)
+ if (!d->resource_pack_list)
{
d->resource_pack_list.reset(new ModList(resourcePacksDir()));
}
- else
- d->resource_pack_list->update();
+ d->resource_pack_list->update();
return d->resource_pack_list;
}
-
-QDialog * OneSixInstance::createModEditDialog ( QWidget* parent )
+QDialog *OneSixInstance::createModEditDialog(QWidget *parent)
{
return new OneSixModEditDialog(this, parent);
}
-bool OneSixInstance::setIntendedVersionId ( QString version )
+bool OneSixInstance::setIntendedVersionId(QString version)
{
settings().set("IntendedVersion", version);
setShouldUpdate(true);
+ auto pathCustom = PathCombine(instanceRoot(), "custom.json");
+ auto pathOrig = PathCombine(instanceRoot(), "version.json");
+ QFile::remove(pathCustom);
+ QFile::remove(pathOrig);
+ reloadFullVersion();
return true;
}
@@ -198,48 +217,85 @@ QString OneSixInstance::intendedVersionId() const
return settings().get("IntendedVersion").toString();
}
-void OneSixInstance::setShouldUpdate ( bool val )
+void OneSixInstance::setShouldUpdate(bool val)
{
- settings().set ( "ShouldUpdate", val );
+ settings().set("ShouldUpdate", val);
}
bool OneSixInstance::shouldUpdate() const
{
I_D(OneSixInstance);
- QVariant var = settings().get ( "ShouldUpdate" );
- if ( !var.isValid() || var.toBool() == false )
+ QVariant var = settings().get("ShouldUpdate");
+ if (!var.isValid() || var.toBool() == false)
{
return intendedVersionId() != currentVersionId();
}
return true;
}
+bool OneSixInstance::versionIsCustom()
+{
+ QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
+ QFile versionfile(verpath_custom);
+ return versionfile.exists();
+}
+
QString OneSixInstance::currentVersionId() const
{
return intendedVersionId();
}
+bool OneSixInstance::customizeVersion()
+{
+ if (!versionIsCustom())
+ {
+ auto pathCustom = PathCombine(instanceRoot(), "custom.json");
+ auto pathOrig = PathCombine(instanceRoot(), "version.json");
+ QFile::copy(pathOrig, pathCustom);
+ return reloadFullVersion();
+ }
+ else
+ return true;
+}
+
+bool OneSixInstance::revertCustomVersion()
+{
+ if (versionIsCustom())
+ {
+ auto path = PathCombine(instanceRoot(), "custom.json");
+ QFile::remove(path);
+ return reloadFullVersion();
+ }
+ else
+ return true;
+}
+
bool OneSixInstance::reloadFullVersion()
{
I_D(OneSixInstance);
-
+
QString verpath = PathCombine(instanceRoot(), "version.json");
- QFile versionfile(verpath);
- if(versionfile.exists() && versionfile.open(QIODevice::ReadOnly))
{
- FullVersionFactory fvf;
- auto version = fvf.parse(versionfile.readAll());
- versionfile.close();
- if(version)
- {
- d->version = version;
- return true;
- }
- };
- return false;
+ QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
+ QFile versionfile(verpath_custom);
+ if (versionfile.exists())
+ verpath = verpath_custom;
+ }
+
+ auto version = OneSixVersion::fromFile(verpath);
+ if (version)
+ {
+ d->version = version;
+ return true;
+ }
+ else
+ {
+ d->version.reset();
+ return false;
+ }
}
-QSharedPointer< OneSixVersion > OneSixInstance::getFullVersion()
+std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion()
{
I_D(OneSixInstance);
return d->version;
@@ -255,16 +311,21 @@ QString OneSixInstance::defaultCustomBaseJar() const
return PathCombine(instanceRoot(), "custom.jar");
}
-bool OneSixInstance::menuActionEnabled ( QString action_name ) const
+bool OneSixInstance::menuActionEnabled(QString action_name) const
{
- if(action_name == "actionChangeInstLWJGLVersion")
+ if (action_name == "actionChangeInstLWJGLVersion")
return false;
return true;
}
QString OneSixInstance::getStatusbarDescription()
{
- return "One Six : " + intendedVersionId();
+ QString descr = "One Six : " + intendedVersionId();
+ if (versionIsCustom())
+ {
+ descr + " (custom)";
+ }
+ return descr;
}
QString OneSixInstance::loaderModsDir() const
@@ -281,3 +342,4 @@ QString OneSixInstance::instanceConfigFolder() const
{
return PathCombine(minecraftRoot(), "config");
}
+
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index a4c67ed1..d2276afc 100644
--- a/logic/OneSixInstance.h
+++ b/logic/OneSixInstance.h
@@ -10,43 +10,50 @@ class OneSixInstance : public BaseInstance
{
Q_OBJECT
public:
- explicit OneSixInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0);
-
-
+ explicit OneSixInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+
////// Mod Lists //////
- QSharedPointer<ModList> loaderModList();
- QSharedPointer<ModList> resourcePackList();
-
+ std::shared_ptr<ModList> loaderModList();
+ std::shared_ptr<ModList> resourcePackList();
+
////// Directories //////
QString resourcePacksDir() const;
QString loaderModsDir() const;
virtual QString instanceConfigFolder() const;
-
- virtual BaseUpdate* doUpdate();
- virtual MinecraftProcess* prepareForLaunch ( QString user, QString session );
+
+ virtual BaseUpdate *doUpdate();
+ virtual MinecraftProcess *prepareForLaunch(LoginResponse response);
virtual void cleanupAfterRun();
-
+
virtual QString intendedVersionId() const;
- virtual bool setIntendedVersionId ( QString version );
-
+ virtual bool setIntendedVersionId(QString version);
+
virtual QString currentVersionId() const;
// virtual void setCurrentVersionId ( QString val ) {};
-
+
virtual bool shouldUpdate() const;
virtual void setShouldUpdate(bool val);
-
- virtual QDialog * createModEditDialog ( QWidget* parent );
-
+
+ virtual QDialog *createModEditDialog(QWidget *parent);
+
/// reload the full version json file. return true on success!
bool reloadFullVersion();
/// get the current full version info
- QSharedPointer<OneSixVersion> getFullVersion();
-
+ std::shared_ptr<OneSixVersion> getFullVersion();
+ /// revert the current custom version back to base
+ bool revertCustomVersion();
+ /// customize the current base version
+ bool customizeVersion();
+ /// is the current version original, or custom?
+ virtual bool versionIsCustom() override;
+
virtual QString defaultBaseJar() const;
virtual QString defaultCustomBaseJar() const;
-
- virtual bool menuActionEnabled ( QString action_name ) const;
+
+ virtual bool menuActionEnabled(QString action_name) const;
virtual QString getStatusbarDescription();
+
private:
- QStringList processMinecraftArgs( QString user, QString session );
+ QStringList processMinecraftArgs(LoginResponse response);
}; \ No newline at end of file
diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h
index c098c9e2..06737b6f 100644
--- a/logic/OneSixInstance_p.h
+++ b/logic/OneSixInstance_p.h
@@ -2,11 +2,12 @@
#include "BaseInstance_p.h"
#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
#include "ModList.h"
struct OneSixInstancePrivate: public BaseInstancePrivate
{
- QSharedPointer<OneSixVersion> version;
- QSharedPointer<ModList> loader_mod_list;
- QSharedPointer<ModList> resource_pack_list;
+ std::shared_ptr<OneSixVersion> version;
+ std::shared_ptr<ModList> loader_mod_list;
+ std::shared_ptr<ModList> resource_pack_list;
}; \ No newline at end of file
diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp
new file mode 100644
index 00000000..9c1caaa7
--- /dev/null
+++ b/logic/OneSixLibrary.cpp
@@ -0,0 +1,162 @@
+#include "OneSixLibrary.h"
+#include "OneSixRule.h"
+#include "OpSys.h"
+#include <QJsonArray>
+void OneSixLibrary::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_decentname = parts[1];
+ m_decentversion = parts[2];
+ m_storage_path = relative;
+ m_download_url = 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);
+ m_decenttype = "Native";
+ }
+ else
+ {
+ m_decenttype = "Java";
+ }
+}
+
+void OneSixLibrary::setName(QString name)
+{
+ m_name = name;
+}
+void OneSixLibrary::setBaseUrl(QString base_url)
+{
+ m_base_url = base_url;
+}
+void OneSixLibrary::setIsNative()
+{
+ m_is_native = true;
+}
+void OneSixLibrary::addNative(OpSys os, QString suffix)
+{
+ m_is_native = true;
+ m_native_suffixes[os] = suffix;
+}
+void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules)
+{
+ m_rules = rules;
+}
+bool OneSixLibrary::isActive()
+{
+ return m_is_active;
+}
+bool OneSixLibrary::isNative()
+{
+ return m_is_native;
+}
+QString OneSixLibrary::downloadUrl()
+{
+ if (m_absolute_url.size())
+ return m_absolute_url;
+ return m_download_url;
+}
+QString OneSixLibrary::storagePath()
+{
+ return m_storage_path;
+}
+
+void OneSixLibrary::setAbsoluteUrl(QString absolute_url)
+{
+ m_absolute_url = absolute_url;
+}
+
+QString OneSixLibrary::absoluteUrl()
+{
+ return m_absolute_url;
+}
+
+void OneSixLibrary::setHint(QString hint)
+{
+ m_hint = hint;
+}
+
+QString OneSixLibrary::hint()
+{
+ return m_hint;
+}
+
+QJsonObject OneSixLibrary::toJson()
+{
+ QJsonObject libRoot;
+ libRoot.insert("name", m_name);
+ if (m_absolute_url.size())
+ libRoot.insert("MMC-absoluteUrl", m_absolute_url);
+ if (m_hint.size())
+ libRoot.insert("MMC-hint", m_hint);
+ if (m_base_url != "http://s3.amazonaws.com/Minecraft.Download/libraries/" &&
+ m_base_url != "https://s3.amazonaws.com/Minecraft.Download/libraries/")
+ libRoot.insert("url", m_base_url);
+ if (isNative() && m_native_suffixes.size())
+ {
+ QJsonObject nativeList;
+ auto iter = m_native_suffixes.begin();
+ while (iter != m_native_suffixes.end())
+ {
+ nativeList.insert(OpSys_toString(iter.key()), iter.value());
+ iter++;
+ }
+ libRoot.insert("natives", nativeList);
+ }
+ if (isNative() && extract_excludes.size())
+ {
+ QJsonArray excludes;
+ QJsonObject extract;
+ for (auto exclude : extract_excludes)
+ {
+ excludes.append(exclude);
+ }
+ extract.insert("exclude", excludes);
+ libRoot.insert("extract", extract);
+ }
+ if (m_rules.size())
+ {
+ QJsonArray allRules;
+ for (auto &rule : m_rules)
+ {
+ QJsonObject ruleObj = rule->toJson();
+ allRules.append(ruleObj);
+ }
+ libRoot.insert("rules", allRules);
+ }
+ return libRoot;
+}
diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h
new file mode 100644
index 00000000..5e58ef89
--- /dev/null
+++ b/logic/OneSixLibrary.h
@@ -0,0 +1,104 @@
+#pragma once
+#include <QString>
+#include <QStringList>
+#include <QMap>
+#include <memory>
+#include <QJsonObject>
+#include "OpSys.h"
+
+class Rule;
+
+class OneSixLibrary
+{
+private:
+ // basic values used internally (so far)
+ QString m_name;
+ QString m_base_url = "http://s3.amazonaws.com/Minecraft.Download/libraries/";
+ QList<std::shared_ptr<Rule> > m_rules;
+
+ // custom values
+ /// absolute URL. takes precedence over m_download_path, if defined
+ QString m_absolute_url;
+ /// download hint - how to actually get the library
+ QString m_hint;
+
+ // derived values used for real things
+ /// a decent name fit for display
+ QString m_decentname;
+ /// a decent version fit for display
+ QString m_decentversion;
+ /// a decent type fit for display
+ QString m_decenttype;
+ /// where to store the lib locally
+ QString m_storage_path;
+ /// where to download the lib from
+ QString m_download_url;
+ /// is this lib actually active on the current OS?
+ bool m_is_active = false;
+ /// is the library a native?
+ bool m_is_native = false;
+ /// native suffixes per OS
+ QMap<OpSys, QString> m_native_suffixes;
+public:
+ QStringList extract_excludes;
+
+public:
+ /// Constructor
+ OneSixLibrary(QString name)
+ {
+ m_name = name;
+ }
+
+ QJsonObject toJson();
+
+ /**
+ * finalize the library, processing the input values into derived values and state
+ *
+ * This SHALL be called after all the values are parsed or after any further change.
+ */
+ void finalize();
+
+ /// Set the library composite name
+ void setName(QString name);
+ /// get a decent-looking name
+ QString name()
+ {
+ return m_decentname;
+ }
+ /// get a decent-looking version
+ QString version()
+ {
+ return m_decentversion;
+ }
+ /// what kind of library is it? (for display)
+ QString type()
+ {
+ return m_decenttype;
+ }
+ /// Set the url base for downloads
+ void setBaseUrl(QString base_url);
+
+ /// Call this to mark the library as 'native' (it's a zip archive with DLLs)
+ void setIsNative();
+ /// Attach a name suffix to the specified OS native
+ void addNative(OpSys os, QString suffix);
+ /// Set the load rules
+ void setRules(QList<std::shared_ptr<Rule> > rules);
+
+ /// Returns true if the library should be loaded (or extracted, in case of natives)
+ bool isActive();
+ /// Returns true if the library is native
+ bool isNative();
+ /// Get the URL to download the library from
+ QString downloadUrl();
+ /// Get the relative path where the library should be saved
+ QString storagePath();
+
+ /// set an absolute URL for the library. This is an MMC extension.
+ void setAbsoluteUrl(QString absolute_url);
+ QString absoluteUrl();
+
+ /// set a hint about how to treat the library. This is an MMC extension.
+ void setHint(QString hint);
+ QString hint();
+};
diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp
new file mode 100644
index 00000000..cb64c9ba
--- /dev/null
+++ b/logic/OneSixRule.cpp
@@ -0,0 +1,72 @@
+#include "OneSixRule.h"
+#include <QJsonObject>
+#include <QJsonArray>
+
+QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules)
+{
+ QList<std::shared_ptr<Rule>> rules;
+ auto rulesVal = objectWithRules.value("rules");
+ if (!rulesVal.isArray())
+ return rules;
+
+ QJsonArray ruleList = rulesVal.toArray();
+ for (auto ruleVal : ruleList)
+ {
+ std::shared_ptr<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));
+ continue;
+ }
+
+ 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));
+ }
+}
+
+QJsonObject ImplicitRule::toJson()
+{
+ QJsonObject ruleObj;
+ ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow"));
+ return ruleObj;
+}
+
+QJsonObject OsRule::toJson()
+{
+ QJsonObject ruleObj;
+ ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow"));
+ QJsonObject osObj;
+ {
+ osObj.insert("name", OpSys_toString(m_system));
+ osObj.insert("version", m_version_regexp);
+ }
+ ruleObj.insert("os", osObj);
+ return ruleObj;
+}
+
+RuleAction RuleAction_fromString(QString name)
+{
+ if (name == "allow")
+ return Allow;
+ if (name == "disallow")
+ return Disallow;
+ return Defer;
+} \ No newline at end of file
diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h
new file mode 100644
index 00000000..6be01f1b
--- /dev/null
+++ b/logic/OneSixRule.h
@@ -0,0 +1,72 @@
+#pragma once
+#include <QString>
+#include <QSharedPointer>
+#include "OneSixLibrary.h"
+
+enum RuleAction
+{
+ Allow,
+ Disallow,
+ Defer
+};
+
+RuleAction RuleAction_fromString(QString);
+QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules);
+
+class Rule
+{
+protected:
+ RuleAction m_result;
+ virtual bool applies(OneSixLibrary * parent) = 0;
+public:
+ Rule(RuleAction result)
+ :m_result(result) {}
+ virtual ~Rule(){};
+ virtual QJsonObject toJson() = 0;
+ RuleAction apply(OneSixLibrary * 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 ( OneSixLibrary* )
+ {
+ return (m_system == currentSystem);
+ }
+ OsRule(RuleAction result, OpSys system, QString version_regexp)
+ : Rule(result), m_system(system), m_version_regexp(version_regexp) {}
+public:
+ virtual QJsonObject toJson();
+ static std::shared_ptr<OsRule> create(RuleAction result, OpSys system, QString version_regexp)
+ {
+ return std::shared_ptr<OsRule> (new OsRule(result, system, version_regexp));
+ }
+};
+
+class ImplicitRule : public Rule
+{
+protected:
+ virtual bool applies ( OneSixLibrary* )
+ {
+ return true;
+ }
+ ImplicitRule(RuleAction result)
+ : Rule(result) {}
+public:
+ virtual QJsonObject toJson();
+ static std::shared_ptr<ImplicitRule> create(RuleAction result)
+ {
+ return std::shared_ptr<ImplicitRule> (new ImplicitRule(result));
+ }
+};
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index 428d6ef7..b5f1d78b 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -3,7 +3,7 @@
* 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
@@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+#include "MultiMC.h"
#include "OneSixUpdate.h"
#include <QtNetwork>
@@ -22,23 +22,22 @@
#include <QTextStream>
#include <QDataStream>
-#include <QDebug>
-
#include "BaseInstance.h"
#include "lists/MinecraftVersionList.h"
-#include "VersionFactory.h"
#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
#include "OneSixInstance.h"
#include "pathutils.h"
-
-OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent):BaseUpdate(inst, parent){}
+OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent) : BaseUpdate(inst, parent)
+{
+}
void OneSixUpdate::executeTask()
{
QString intendedVersion = m_inst->intendedVersionId();
-
+
// Make directories
QDir mcDir(m_inst->minecraftRoot());
if (!mcDir.exists() && !mcDir.mkpath("."))
@@ -46,17 +45,18 @@ void OneSixUpdate::executeTask()
emitFailed("Failed to create bin folder.");
return;
}
-
+
// Get a pointer to the version object that corresponds to the instance's version.
- targetVersion = MinecraftVersionList::getMainList().findVersion(intendedVersion).dynamicCast<MinecraftVersion>();
- if(targetVersion == nullptr)
+ targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
+ MMC->minecraftlist()->findVersion(intendedVersion));
+ if (targetVersion == nullptr)
{
// don't do anything if it was invalid
emitSucceeded();
return;
}
-
- if(m_inst->shouldUpdate())
+
+ if (m_inst->shouldUpdate())
{
versionFileStart();
}
@@ -69,45 +69,64 @@ void OneSixUpdate::executeTask()
void OneSixUpdate::versionFileStart()
{
setStatus("Getting the version files from Mojang.");
-
+
QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/");
- urlstr += targetVersion->descriptor + "/" + targetVersion->descriptor + ".json";
- specificVersionDownloadJob.reset(new DownloadJob(QUrl(urlstr)));
- connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), SLOT(versionFileFinished()));
- connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed()));
- connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64)));
+ urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json";
+ auto job = new DownloadJob("Version index");
+ job->addByteArrayDownload(QUrl(urlstr));
+ specificVersionDownloadJob.reset(job);
+ connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished()));
+ connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed()));
+ connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
specificVersionDownloadJob->start();
}
void OneSixUpdate::versionFileFinished()
{
DownloadPtr DlJob = specificVersionDownloadJob->first();
-
- QString version_id = targetVersion->descriptor;
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+
+ QString version_id = targetVersion->descriptor();
QString inst_dir = m_inst->instanceRoot();
// save the version file in $instanceId/version.json
{
- QString version1 = PathCombine(inst_dir, "/version.json");
+ QString version1 = PathCombine(inst_dir, "/version.json");
ensureFilePathExists(version1);
// FIXME: detect errors here, download to a temp file, swap
- QFile vfile1 (version1);
- vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly );
- vfile1.write(DlJob->m_data);
- vfile1.close();
+ QSaveFile vfile1(version1);
+ if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
+ {
+ emitFailed("Can't open " + version1 + " for writing.");
+ return;
+ }
+ auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
+ qint64 actual = 0;
+ if ((actual = vfile1.write(data)) != data.size())
+ {
+ emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " +
+ data.size() + '.');
+ return;
+ }
+ if (!vfile1.commit())
+ {
+ emitFailed("Can't commit changes to " + version1);
+ return;
+ }
}
-
+
// the version is downloaded safely. update is 'done' at this point
m_inst->setShouldUpdate(false);
- // save the version file in versions/$version/$version.json
- /*
- //QString version2 = QString("versions/") + version_id + "/" + version_id + ".json";
- //ensurePathExists(version2);
- //QFile vfile2 (version2);
- //vfile2.open(QIODevice::Truncate | QIODevice::WriteOnly );
- //vfile2.write(DlJob->m_data);
- //vfile2.close();
- */
-
+
+ // delete any custom version inside the instance (it's no longer relevant, we did an update)
+ QString custom = PathCombine(inst_dir, "/custom.json");
+ QFile finfo(custom);
+ if (finfo.exists())
+ {
+ finfo.remove();
+ }
+ inst->reloadFullVersion();
+
jarlibStart();
}
@@ -118,36 +137,49 @@ void OneSixUpdate::versionFileFailed()
void OneSixUpdate::jarlibStart()
{
- OneSixInstance * inst = (OneSixInstance *) m_inst;
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
bool successful = inst->reloadFullVersion();
- if(!successful)
+ if (!successful)
{
- emitFailed("Failed to load the version description file (version.json). It might be corrupted, missing or simply too new.");
+ emitFailed("Failed to load the version description file (version.json). It might be "
+ "corrupted, missing or simply too new.");
return;
}
-
- QSharedPointer<OneSixVersion> version = inst->getFullVersion();
-
+
+ std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+
// download the right jar, save it in versions/$version/$version.jar
QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/");
urlstr += version->id + "/" + version->id + ".jar";
- QString targetstr ("versions/");
+ QString targetstr("versions/");
targetstr += version->id + "/" + version->id + ".jar";
-
- jarlibDownloadJob.reset(new DownloadJob(QUrl(urlstr), targetstr));
-
+
+ auto job = new DownloadJob("Libraries for instance " + inst->name());
+ job->addFileDownload(QUrl(urlstr), targetstr);
+ jarlibDownloadJob.reset(job);
+
auto libs = version->getActiveNativeLibs();
libs.append(version->getActiveNormalLibs());
-
- for(auto lib: libs)
+
+ auto metacache = MMC->metacache();
+ for (auto lib : libs)
{
- QString download_path = lib->downloadPath();
- QString storage_path = "libraries/" + lib->storagePath();
- jarlibDownloadJob->add(download_path, storage_path);
+ if (lib->hint() == "local")
+ continue;
+ QString download_path = lib->downloadUrl();
+ auto entry = metacache->resolveEntry("libraries", lib->storagePath());
+ if (entry->stale)
+ {
+ if (lib->hint() == "forge-pack-xz")
+ jarlibDownloadJob->addForgeXzDownload(download_path, entry);
+ else
+ jarlibDownloadJob->addCacheDownload(download_path, entry);
+ }
}
- connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished()));
- connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed()));
- connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64)));
+ connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished()));
+ connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed()));
+ connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
jarlibDownloadJob->start();
}
@@ -159,6 +191,8 @@ void OneSixUpdate::jarlibFinished()
void OneSixUpdate::jarlibFailed()
{
- emitFailed("Failed to download the binary garbage. Try again. Maybe. IF YOU DARE");
+ QStringList failed = jarlibDownloadJob->getFailedFiles();
+ QString failed_all = failed.join("\n");
+ emitFailed("Failed to download the following files:\n" + failed_all +
+ "\n\nPlease try again.");
}
-
diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h
index 7314a6d1..ed8dcbcd 100644
--- a/logic/OneSixUpdate.h
+++ b/logic/OneSixUpdate.h
@@ -47,7 +47,7 @@ private:
DownloadJobPtr jarlibDownloadJob;
// target version, determined during this task
- QSharedPointer<MinecraftVersion> targetVersion;
+ std::shared_ptr<MinecraftVersion> targetVersion;
};
diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp
index 56e272e2..51958389 100644
--- a/logic/OneSixVersion.cpp
+++ b/logic/OneSixVersion.cpp
@@ -1,132 +1,311 @@
#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
+#include "OneSixRule.h"
-RuleAction RuleAction_fromString(QString name)
+std::shared_ptr<OneSixVersion> fromJsonV4(QJsonObject root,
+ std::shared_ptr<OneSixVersion> fullVersion)
{
- 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;
-}
+ fullVersion->id = root.value("id").toString();
-void Library::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
+ fullVersion->mainClass = root.value("mainClass").toString();
+ auto procArgsValue = root.value("processArguments");
+ if (procArgsValue.isString())
{
- if ( m_native_suffixes.contains ( currentSystem ) )
+ fullVersion->processArguments = procArgsValue.toString();
+ QString toCompare = fullVersion->processArguments.toLower();
+ if (toCompare == "legacy")
{
- relative += "-" + m_native_suffixes[currentSystem] + ".jar";
+ fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
- else
+ else if (toCompare == "username_session")
{
- // really, bad.
- relative += ".jar";
+ 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}";
}
}
- m_storage_path = relative;
- m_download_path = m_base_url + relative;
- if ( m_rules.empty() )
+ auto minecraftArgsValue = root.value("minecraftArguments");
+ if (minecraftArgsValue.isString())
{
- m_is_active = true;
+ fullVersion->minecraftArguments = minecraftArgsValue.toString();
}
- else
+
+ auto minecraftTypeValue = root.value("type");
+ if (minecraftTypeValue.isString())
{
- RuleAction result = Disallow;
- for ( auto rule: m_rules )
- {
- RuleAction temp = rule->apply ( this );
- if ( temp != Defer )
- result = temp;
- }
- m_is_active = ( result == Allow );
+ fullVersion->type = minecraftTypeValue.toString();
}
- if ( m_is_native )
+
+ 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)
{
- m_is_active = m_is_active && m_native_suffixes.contains ( currentSystem );
+ if (!libVal.isObject())
+ {
+ continue;
+ }
+
+ QJsonObject libObj = libVal.toObject();
+
+ // Library name
+ auto nameVal = libObj.value("name");
+ if (!nameVal.isString())
+ continue;
+ std::shared_ptr<OneSixLibrary> library(new OneSixLibrary(nameVal.toString()));
+
+ auto urlVal = libObj.value("url");
+ if (urlVal.isString())
+ {
+ library->setBaseUrl(urlVal.toString());
+ }
+ auto hintVal = libObj.value("MMC-hint");
+ if (hintVal.isString())
+ {
+ library->setHint(hintVal.toString());
+ }
+ auto urlAbsVal = libObj.value("MMC-absoluteUrl");
+ auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility
+ if (urlAbsVal.isString())
+ {
+ library->setAbsoluteUrl(urlAbsVal.toString());
+ }
+ else if(urlAbsuVal.isString())
+ {
+ library->setAbsoluteUrl(urlAbsuVal.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())
+ {
+ auto excludesList = excludesVal.toArray();
+ for (auto excludeVal : excludesList)
+ {
+ if (excludeVal.isString())
+ excludes.append(excludeVal.toString());
+ }
+ library->extract_excludes = excludes;
+ }
+ }
+
+ 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(rulesFromJsonV4(libObj));
+ library->finalize();
+ fullVersion->libraries.append(library);
}
+ return fullVersion;
}
-void Library::setName ( QString name )
+std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(QJsonObject root)
{
- m_name = name;
-}
-void Library::setBaseUrl ( QString base_url )
-{
- m_base_url = base_url;
+ std::shared_ptr<OneSixVersion> readVersion(new OneSixVersion());
+ int launcher_ver = readVersion->minimumLauncherVersion =
+ root.value("minimumLauncherVersion").toDouble();
+
+ // ADD MORE HERE :D
+ if (launcher_ver > 0 && launcher_ver <= 9)
+ return fromJsonV4(root, readVersion);
+ else
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
}
-void Library::setIsNative()
+
+std::shared_ptr<OneSixVersion> OneSixVersion::fromFile(QString filepath)
{
- m_is_native = true;
+ QFile file(filepath);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
+
+ auto data = file.readAll();
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
+
+ if (!jsonDoc.isObject())
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
+ QJsonObject root = jsonDoc.object();
+ auto version = fromJson(root);
+ if(version)
+ version->original_file = filepath;
+ return version;
}
-void Library::addNative ( OpSys os, QString suffix )
+
+bool OneSixVersion::toOriginalFile()
{
- m_is_native = true;
- m_native_suffixes[os] = suffix;
+ if (original_file.isEmpty())
+ return false;
+ QSaveFile file(original_file);
+ if (!file.open(QIODevice::WriteOnly))
+ {
+ return false;
+ }
+ // serialize base attributes (those we care about anyway)
+ QJsonObject root;
+ root.insert("minecraftArguments", minecraftArguments);
+ root.insert("mainClass", mainClass);
+ root.insert("minimumLauncherVersion", minimumLauncherVersion);
+ root.insert("time", time);
+ root.insert("id", id);
+ root.insert("type", type);
+ // screw processArguments
+ root.insert("releaseTime", releaseTime);
+ QJsonArray libarray;
+ for(const auto & lib: libraries)
+ {
+ libarray.append(lib->toJson());
+ }
+ if(libarray.count())
+ root.insert("libraries", libarray);
+ QJsonDocument doc(root);
+ file.write(doc.toJson());
+ return file.commit();
}
-void Library::setRules ( QList< QSharedPointer< Rule > > rules )
+
+QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNormalLibs()
{
- m_rules = rules;
+ QList<std::shared_ptr<OneSixLibrary>> output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && !lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
}
-bool Library::isActive()
+
+QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNativeLibs()
{
- return m_is_active;
+ QList<std::shared_ptr<OneSixLibrary>> output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
}
-bool Library::isNative()
+
+void OneSixVersion::externalUpdateStart()
{
- return m_is_native;
+ beginResetModel();
}
-QString Library::downloadPath()
+
+void OneSixVersion::externalUpdateFinish()
{
- return m_download_path;
+ endResetModel();
}
-QString Library::storagePath()
+
+QVariant OneSixVersion::data(const QModelIndex &index, int role) const
{
- return m_storage_path;
-}
+ if (!index.isValid())
+ return QVariant();
+ int row = index.row();
+ int column = index.column();
-QList<QSharedPointer<Library> > OneSixVersion::getActiveNormalLibs()
-{
- QList<QSharedPointer<Library> > output;
- for ( auto lib: libraries )
+ if (row < 0 || row >= libraries.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
{
- if (lib->isActive() && !lib->isNative())
+ switch (column)
{
- output.append(lib);
+ case 0:
+ return libraries[row]->name();
+ case 1:
+ return libraries[row]->type();
+ case 2:
+ return libraries[row]->version();
+ default:
+ return QVariant();
}
}
- return output;
+ return QVariant();
}
-QList<QSharedPointer<Library> > OneSixVersion::getActiveNativeLibs()
+Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
{
- QList<QSharedPointer<Library> > output;
- for ( auto lib: libraries )
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ int row = index.row();
+ if (libraries[row]->isActive())
{
- if (lib->isActive() && lib->isNative())
- {
- output.append(lib);
- }
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
+ }
+ else
+ {
+ return Qt::ItemNeverHasChildren;
+ }
+ // return QAbstractListModel::flags(index);
+}
+
+QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
+ return QVariant();
+ switch (section)
+ {
+ case 0:
+ return QString("Name");
+ case 1:
+ return QString("Type");
+ case 2:
+ return QString("Version");
+ default:
+ return QString();
}
- return output;
}
+int OneSixVersion::rowCount(const QModelIndex &parent) const
+{
+ return libraries.size();
+}
+int OneSixVersion::columnCount(const QModelIndex &parent) const
+{
+ return 3;
+}
diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h
index 89b7c911..3529138c 100644
--- a/logic/OneSixVersion.h
+++ b/logic/OneSixVersion.h
@@ -1,155 +1,38 @@
#pragma once
#include <QtCore>
+#include <memory>
-class Library;
+class OneSixLibrary;
-enum OpSys
+class OneSixVersion : public QAbstractListModel
{
- Os_Windows,
- Os_Linux,
- Os_OSX,
- Os_Other
-};
-
-OpSys OpSys_fromString(QString);
-
-#ifdef Q_OS_WIN32
- #define currentSystem Os_Windows
-#else
- #ifdef Q_OS_MAC
- #define currentSystem Os_OSX
- #else
- #define currentSystem Os_Linux
- #endif
-#endif
-enum RuleAction
-{
- Allow,
- Disallow,
- Defer
-};
-
-RuleAction RuleAction_fromString(QString);
-
-class Rule
-{
-protected:
- RuleAction m_result;
- virtual bool applies(Library * parent) = 0;
+ // Things required to implement the Qt list model
public:
- Rule(RuleAction result)
- :m_result(result) {}
- virtual ~Rule(){};
- RuleAction apply(Library * parent)
- {
- if(applies(parent))
- return m_result;
- else
- return Defer;
- };
-};
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
-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) {}
+ // serialization/deserialization
public:
- static QSharedPointer<OsRule> create(RuleAction result, OpSys system, QString version_regexp)
- {
- return QSharedPointer<OsRule> (new OsRule(result, system, version_regexp));
- }
-};
+ bool toOriginalFile();
+ static std::shared_ptr<OneSixVersion> fromJson(QJsonObject root);
+ static std::shared_ptr<OneSixVersion> fromFile(QString filepath);
-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));
- }
-};
+ QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
+ QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
+ // called when something starts/stops messing with the object
+ // FIXME: these are ugly in every possible way.
+ void externalUpdateStart();
+ void externalUpdateFinish();
-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 actually active on the current OS?
- bool m_is_active;
- /// is the library a native?
- bool m_is_native;
- /// native suffixes per OS
- QMap<OpSys, QString> m_native_suffixes;
-public:
- QStringList extract_excludes;
-
-public:
- /// Constructor
- Library(QString name)
- {
- m_is_native = false;
- m_is_native = false;
- m_name = name;
- m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/";
- }
-
- /**
- * finalize the library, processing the input values into derived values and state
- *
- * This SHALL be called after all the values are parsed or after any further change.
- */
- void finalize();
-
- /// Set the library composite name
- void setName(QString name);
- /// Set the url base for downloads
- void setBaseUrl(QString base_url);
- /// Call this to mark the library as 'native' (it's a zip archive with DLLs)
- void setIsNative();
- /// Attach a name suffix to the specified OS native
- void addNative(OpSys os, QString suffix);
- /// Set the load rules
- void setRules(QList<QSharedPointer<Rule> > rules);
-
- /// Returns true if the library should be loaded (or extracted, in case of natives)
- bool isActive();
- /// Returns true if the library is native
- bool isNative();
- /// Get the URL to download the library from
- QString downloadPath();
- /// Get the relative path where the library should be saved
- QString storagePath();
-};
-
-
-class OneSixVersion
-{
+ // data members
public:
+ /// file this was read from. blank, if none
+ QString original_file;
/// the ID - determines which jar to use! ACTUALLY IMPORTANT!
QString id;
/// Last updated time - as a string
@@ -162,26 +45,27 @@ public:
* DEPRECATED: Old versions of the new vanilla launcher used this
* ex: "username_session_version"
*/
- QString processArguments;
+ 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)
+ * the minimum launcher version required by this version ... current is 4 (at point of
+ * writing)
*/
- int minimumLauncherVersion;
+ int minimumLauncherVersion = 0xDEADBEEF;
/**
* The main class to load first
*/
QString mainClass;
-
+
/// the list of libs - both active and inactive, native and java
- QList<QSharedPointer<Library> > libraries;
-
+ QList<std::shared_ptr<OneSixLibrary>> libraries;
+
/*
FIXME: add support for those rules here? Looks like a pile of quick hacks to me though.
@@ -197,17 +81,11 @@ public:
}
}
],
- "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!"
+ "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;
-
-public:
- OneSixVersion()
- {
- minimumLauncherVersion = 0xDEADBEEF;
- }
-
- QList<QSharedPointer<Library> > getActiveNormalLibs();
- QList<QSharedPointer<Library> > getActiveNativeLibs();
-}; \ No newline at end of file
+
+
+};
diff --git a/logic/OpSys.cpp b/logic/OpSys.cpp
new file mode 100644
index 00000000..f101fd08
--- /dev/null
+++ b/logic/OpSys.cpp
@@ -0,0 +1,23 @@
+#include "OpSys.h"
+
+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;
+}
+
+QString OpSys_toString(OpSys name)
+{
+ switch(name)
+ {
+ case Os_Linux: return "linux";
+ case Os_OSX: return "osx";
+ case Os_Windows: return "windows";
+ default: return "other";
+ }
+} \ No newline at end of file
diff --git a/logic/OpSys.h b/logic/OpSys.h
new file mode 100644
index 00000000..aaa2eb65
--- /dev/null
+++ b/logic/OpSys.h
@@ -0,0 +1,22 @@
+#pragma once
+#include <QString>
+enum OpSys
+{
+ Os_Windows,
+ Os_Linux,
+ Os_OSX,
+ Os_Other
+};
+
+OpSys OpSys_fromString(QString);
+QString OpSys_toString(OpSys);
+
+#ifdef Q_OS_WIN32
+ #define currentSystem Os_Windows
+#else
+ #ifdef Q_OS_MAC
+ #define currentSystem Os_OSX
+ #else
+ #define currentSystem Os_Linux
+ #endif
+#endif \ No newline at end of file
diff --git a/logic/VersionFactory.cpp b/logic/VersionFactory.cpp
deleted file mode 100644
index 71c4d747..00000000
--- a/logic/VersionFactory.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-#include "VersionFactory.h"
-#include "OneSixVersion.h"
-
-// 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<OneSixVersion> FullVersionFactory::parse4(QJsonObject root, QSharedPointer<OneSixVersion> fullVersion)
-{
- fullVersion->id = root.value("id").toString();
-
- 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}";
- }
- 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();
- }
-
- auto minecraftTypeValue = root.value("type");
- if(minecraftTypeValue.isString())
- {
- fullVersion->type = minecraftTypeValue.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()));
-
- auto urlVal = libObj.value("url");
- if(urlVal.isString())
- {
- library->setBaseUrl(urlVal.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<OneSixVersion> FullVersionFactory::parse(QByteArray data)
-{
- QSharedPointer<OneSixVersion> readVersion(new OneSixVersion());
-
- 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<OneSixVersion>();
- }
-
- if(!jsonDoc.isObject())
- {
- error_string = "Error reading version file.";
- m_error = FullVersionFactory::ParseError;
- return QSharedPointer<OneSixVersion>();
- }
- QJsonObject root = jsonDoc.object();
-
- int launcher_ver = readVersion->minimumLauncherVersion = root.value("minimumLauncherVersion").toDouble();
- // ADD MORE HERE :D
- if(launcher_ver > 0 && launcher_ver <= 7)
- return parse4(root, readVersion);
- else
- {
- error_string = "Version file was for an unrecognized launcher version. RIP";
- m_error = FullVersionFactory::UnsupportedVersion;
- return QSharedPointer<OneSixVersion>();
- }
-}
-
-
-FullVersionFactory::FullVersionFactory()
-{
- m_error = FullVersionFactory::AllOK;
-}
diff --git a/logic/VersionFactory.h b/logic/VersionFactory.h
deleted file mode 100644
index 0c0ee2d4..00000000
--- a/logic/VersionFactory.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include <QtCore>
-
-struct OneSixVersion;
-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<OneSixVersion> parse(QByteArray data);
-private:
- QSharedPointer<OneSixVersion> parse4(QJsonObject root, QSharedPointer<OneSixVersion> product);
- QList<QSharedPointer<Rule> > parse4rules(QJsonObject & baseObj);
-}; \ No newline at end of file
diff --git a/logic/lists/InstVersionList.cpp b/logic/lists/BaseVersionList.cpp
index 7dc67155..61da5eeb 100644
--- a/logic/lists/InstVersionList.cpp
+++ b/logic/lists/BaseVersionList.cpp
@@ -13,33 +13,33 @@
* limitations under the License.
*/
-#include "logic/lists/InstVersionList.h"
-#include "logic/InstanceVersion.h"
+#include "logic/lists/BaseVersionList.h"
+#include "logic/BaseVersion.h"
-InstVersionList::InstVersionList(QObject *parent) :
+BaseVersionList::BaseVersionList(QObject *parent) :
QAbstractListModel(parent)
{
}
-InstVersionPtr InstVersionList::findVersion( const QString& descriptor )
+BaseVersionPtr BaseVersionList::findVersion( const QString& descriptor )
{
for (int i = 0; i < count(); i++)
{
- if (at(i)->descriptor == descriptor)
+ if (at(i)->descriptor() == descriptor)
return at(i);
}
- return InstVersionPtr();
+ return BaseVersionPtr();
}
-InstVersionPtr InstVersionList::getLatestStable() const
+BaseVersionPtr BaseVersionList::getLatestStable() const
{
if (count() <= 0)
- return InstVersionPtr();
+ return BaseVersionPtr();
else
return at(0);
}
-QVariant InstVersionList::data(const QModelIndex &index, int role) const
+QVariant BaseVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
@@ -48,7 +48,7 @@ QVariant InstVersionList::data(const QModelIndex &index, int role) const
return QVariant();
- InstVersionPtr version = at(index.row());
+ BaseVersionPtr version = at(index.row());
switch (role)
{
@@ -56,20 +56,17 @@ QVariant InstVersionList::data(const QModelIndex &index, int role) const
switch (index.column())
{
case NameColumn:
- return version->name;
+ return version->name();
case TypeColumn:
return version->typeString();
- case TimeColumn:
- return version->timestamp;
-
default:
return QVariant();
}
case Qt::ToolTipRole:
- return version->descriptor;
+ return version->descriptor();
case VersionPointerRole:
return qVariantFromValue(version);
@@ -79,7 +76,7 @@ QVariant InstVersionList::data(const QModelIndex &index, int role) const
}
}
-QVariant InstVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+QVariant BaseVersionList::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (role)
{
@@ -91,9 +88,6 @@ QVariant InstVersionList::headerData(int section, Qt::Orientation orientation, i
case TypeColumn:
return "Type";
-
- case TimeColumn:
- return "Time";
default:
return QVariant();
@@ -117,13 +111,13 @@ QVariant InstVersionList::headerData(int section, Qt::Orientation orientation, i
}
}
-int InstVersionList::rowCount(const QModelIndex &parent) const
+int BaseVersionList::rowCount(const QModelIndex &parent) const
{
// Return count
return count();
}
-int InstVersionList::columnCount(const QModelIndex &parent) const
+int BaseVersionList::columnCount(const QModelIndex &parent) const
{
return 2;
}
diff --git a/logic/lists/InstVersionList.h b/logic/lists/BaseVersionList.h
index bc6aa5d4..d37431ed 100644
--- a/logic/lists/InstVersionList.h
+++ b/logic/lists/BaseVersionList.h
@@ -20,7 +20,7 @@
#include <QAbstractListModel>
#include <QSharedPointer>
-#include "logic/InstanceVersion.h"
+#include "logic/BaseVersion.h"
class Task;
@@ -36,7 +36,7 @@ class Task;
* all have a default implementation, but they can be overridden by plugins to
* change the behavior of the list.
*/
-class InstVersionList : public QAbstractListModel
+class BaseVersionList : public QAbstractListModel
{
Q_OBJECT
public:
@@ -57,7 +57,7 @@ public:
TimeColumn
};
- explicit InstVersionList(QObject *parent = 0);
+ explicit BaseVersionList(QObject *parent = 0);
/*!
* \brief Gets a task that will reload the version list.
@@ -71,7 +71,7 @@ public:
virtual bool isLoaded() = 0;
//! Gets the version at the given index.
- virtual const InstVersionPtr at(int i) const = 0;
+ virtual const BaseVersionPtr at(int i) const = 0;
//! Returns the number of versions in the list.
virtual int count() const = 0;
@@ -90,14 +90,14 @@ public:
* \return A const pointer to the version with the given descriptor. NULL if
* one doesn't exist.
*/
- virtual InstVersionPtr findVersion(const QString &descriptor);
+ virtual BaseVersionPtr findVersion(const QString &descriptor);
/*!
* \brief Gets the latest stable version of this instance type.
* This is the version that will be selected by default.
* By default, this is simply the first version in the list.
*/
- virtual InstVersionPtr getLatestStable() const;
+ virtual BaseVersionPtr getLatestStable() const;
/*!
* Sorts the version list.
@@ -117,5 +117,5 @@ protected slots:
* then copies the versions and sets their parents correctly.
* \param versions List of versions whose parents should be set.
*/
- virtual void updateListData(QList<InstVersionPtr > versions) = 0;
+ virtual void updateListData(QList<BaseVersionPtr > versions) = 0;
};
diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp
new file mode 100644
index 00000000..491a43d7
--- /dev/null
+++ b/logic/lists/ForgeVersionList.cpp
@@ -0,0 +1,297 @@
+/* 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 "ForgeVersionList.h"
+#include <logic/net/DownloadJob.h>
+#include "MultiMC.h"
+
+#include <QtNetwork>
+#include <QtXml>
+#include <QRegExp>
+
+#include <logger/QsLog.h>
+
+#define JSON_URL "http://files.minecraftforge.net/minecraftforge/json"
+
+ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent)
+{
+}
+
+Task *ForgeVersionList::getLoadTask()
+{
+ return new ForgeListLoadTask(this);
+}
+
+bool ForgeVersionList::isLoaded()
+{
+ return m_loaded;
+}
+
+const BaseVersionPtr ForgeVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+int ForgeVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+int ForgeVersionList::columnCount(const QModelIndex &parent) const
+{
+ return 3;
+}
+
+QVariant ForgeVersionList::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+ auto version = std::dynamic_pointer_cast<ForgeVersion>(m_vlist[index.row()]);
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case 0:
+ return version->name();
+
+ case 1:
+ return version->mcver;
+
+ case 2:
+ return version->typeString();
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return version->descriptor();
+
+ case VersionPointerRole:
+ return qVariantFromValue(m_vlist[index.row()]);
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant ForgeVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case 0:
+ return "Version";
+
+ case 1:
+ return "Minecraft";
+
+ case 2:
+ return "Type";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case 0:
+ return "The name of the version.";
+
+ case 1:
+ return "Minecraft version";
+
+ case 2:
+ return "The version's type.";
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+BaseVersionPtr ForgeVersionList::getLatestStable() const
+{
+ return BaseVersionPtr();
+}
+
+void ForgeVersionList::updateListData(QList<BaseVersionPtr> versions)
+{
+ beginResetModel();
+ m_vlist = versions;
+ m_loaded = true;
+ endResetModel();
+ // NOW SORT!!
+ // sort();
+}
+
+void ForgeVersionList::sort()
+{
+ // NO-OP for now
+}
+
+ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task()
+{
+ m_list = vlist;
+}
+
+void ForgeListLoadTask::executeTask()
+{
+ auto job = new DownloadJob("Version index");
+ // we do not care if the version is stale or not.
+ auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json");
+
+ // verify by poking the server.
+ forgeListEntry->stale = true;
+
+ job->addCacheDownload(QUrl(JSON_URL), forgeListEntry);
+ listJob.reset(job);
+ connect(listJob.get(), SIGNAL(succeeded()), SLOT(list_downloaded()));
+ connect(listJob.get(), SIGNAL(failed()), SLOT(list_failed()));
+ connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ listJob->start();
+}
+
+void ForgeListLoadTask::list_failed()
+{
+ auto DlJob = listJob->first();
+ auto reply = DlJob->m_reply;
+ if(reply)
+ {
+ QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString();
+ }
+ else
+ QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
+}
+
+void ForgeListLoadTask::list_downloaded()
+{
+ QByteArray data;
+ {
+ auto DlJob = listJob->first();
+ auto filename = std::dynamic_pointer_cast<CacheDownload>(DlJob)->m_target_path;
+ QFile listFile(filename);
+ if(!listFile.open(QIODevice::ReadOnly))
+ return;
+ data = listFile.readAll();
+ DlJob.reset();
+ }
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ emitFailed("Error parsing version list JSON:" + jsonError.errorString());
+ return;
+ }
+
+ if (!jsonDoc.isObject())
+ {
+ emitFailed("Error parsing version list JSON: jsonDoc is not an object");
+ return;
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ // Now, get the array of versions.
+ if (!root.value("builds").isArray())
+ {
+ emitFailed(
+ "Error parsing version list JSON: version list object is missing 'builds' array");
+ return;
+ }
+ QJsonArray builds = root.value("builds").toArray();
+
+ QList<BaseVersionPtr> tempList;
+ for (int i = 0; i < builds.count(); i++)
+ {
+ // Load the version info.
+ if (!builds[i].isObject())
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ QJsonObject obj = builds[i].toObject();
+ int build_nr = obj.value("build").toDouble(0);
+ if (!build_nr)
+ continue;
+ QJsonArray files = obj.value("files").toArray();
+ QString url, jobbuildver, mcver, buildtype, filename;
+ QString changelog_url, installer_url;
+ QString installer_filename;
+ bool valid = false;
+ for (int j = 0; j < files.count(); j++)
+ {
+ if (!files[j].isObject())
+ continue;
+ QJsonObject file = files[j].toObject();
+ buildtype = file.value("buildtype").toString();
+ if ((buildtype == "client" || buildtype == "universal") && !valid)
+ {
+ mcver = file.value("mcver").toString();
+ url = file.value("url").toString();
+ jobbuildver = file.value("jobbuildver").toString();
+ int lastSlash = url.lastIndexOf('/');
+ filename = url.mid(lastSlash + 1);
+ valid = true;
+ }
+ else if (buildtype == "changelog")
+ {
+ QString ext = file.value("ext").toString();
+ if (ext.isEmpty())
+ continue;
+ changelog_url = file.value("url").toString();
+ }
+ else if (buildtype == "installer")
+ {
+ installer_url = file.value("url").toString();
+ int lastSlash = installer_url.lastIndexOf('/');
+ installer_filename = installer_url.mid(lastSlash + 1);
+ }
+ }
+ if (valid)
+ {
+ // Now, we construct the version object and add it to the list.
+ std::shared_ptr<ForgeVersion> fVersion(new ForgeVersion());
+ fVersion->universal_url = url;
+ fVersion->changelog_url = changelog_url;
+ fVersion->installer_url = installer_url;
+ fVersion->jobbuildver = jobbuildver;
+ fVersion->mcver = mcver;
+ if (installer_filename.isEmpty())
+ fVersion->filename = filename;
+ else
+ fVersion->filename = installer_filename;
+ fVersion->m_buildnr = build_nr;
+ tempList.append(fVersion);
+ }
+ }
+ m_list->updateListData(tempList);
+
+ emitSucceeded();
+ return;
+}
diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h
new file mode 100644
index 00000000..56a207c8
--- /dev/null
+++ b/logic/lists/ForgeVersionList.h
@@ -0,0 +1,109 @@
+/* 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 <QObject>
+#include <QAbstractListModel>
+#include <QSharedPointer>
+#include <QUrl>
+
+#include <QNetworkReply>
+#include "BaseVersionList.h"
+#include "logic/tasks/Task.h"
+#include "logic/net/DownloadJob.h"
+
+class ForgeVersion;
+typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
+
+struct ForgeVersion : public BaseVersion
+{
+ virtual QString descriptor()
+ {
+ return filename;
+ }
+ ;
+ virtual QString name()
+ {
+ return "Forge " + jobbuildver;
+ }
+ ;
+ virtual QString typeString() const
+ {
+ if (installer_url.isEmpty())
+ return "Universal";
+ else
+ return "Installer";
+ }
+ ;
+
+ int m_buildnr = 0;
+ QString universal_url;
+ QString changelog_url;
+ QString installer_url;
+ QString jobbuildver;
+ QString mcver;
+ QString filename;
+};
+
+class ForgeVersionList : public BaseVersionList
+{
+ Q_OBJECT
+public:
+ friend class ForgeListLoadTask;
+
+ explicit ForgeVersionList(QObject *parent = 0);
+
+ virtual Task *getLoadTask();
+ virtual bool isLoaded();
+ virtual const BaseVersionPtr at(int i) const;
+ virtual int count() const;
+ virtual void sort();
+
+ virtual BaseVersionPtr getLatestStable() const;
+
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation,
+ int role) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+
+protected:
+ QList<BaseVersionPtr> m_vlist;
+
+ bool m_loaded;
+
+protected
+slots:
+ virtual void updateListData(QList<BaseVersionPtr> versions);
+};
+
+class ForgeListLoadTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit ForgeListLoadTask(ForgeVersionList *vlist);
+
+ virtual void executeTask();
+
+protected
+slots:
+ void list_downloaded();
+ void list_failed();
+
+protected:
+ DownloadJobPtr listJob;
+ ForgeVersionList *m_list;
+};
diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp
index b930f781..9740d5a5 100644
--- a/logic/lists/InstanceList.cpp
+++ b/logic/lists/InstanceList.cpp
@@ -3,7 +3,7 @@
* 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
@@ -29,13 +29,13 @@
#include "logic/lists/IconList.h"
#include "logic/BaseInstance.h"
#include "logic/InstanceFactory.h"
+#include <logger/QsLog.h>
const static int GROUP_FILE_FORMAT_VERSION = 1;
-InstanceList::InstanceList(const QString &instDir, QObject *parent) :
- QAbstractListModel ( parent ), m_instDir("instances")
+InstanceList::InstanceList(const QString &instDir, QObject *parent)
+ : QAbstractListModel(parent), m_instDir("instances")
{
-
}
InstanceList::~InstanceList()
@@ -43,32 +43,32 @@ InstanceList::~InstanceList()
saveGroupList();
}
-int InstanceList::rowCount ( const QModelIndex& parent ) const
+int InstanceList::rowCount(const QModelIndex &parent) const
{
- Q_UNUSED ( parent );
+ Q_UNUSED(parent);
return m_instances.count();
}
-QModelIndex InstanceList::index ( int row, int column, const QModelIndex& parent ) const
+QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
{
- Q_UNUSED ( parent );
- if ( row < 0 || row >= m_instances.size() )
+ Q_UNUSED(parent);
+ if (row < 0 || row >= m_instances.size())
return QModelIndex();
- return createIndex ( row, column, ( void* ) m_instances.at ( row ).data() );
+ return createIndex(row, column, (void *)m_instances.at(row).get());
}
-QVariant InstanceList::data ( const QModelIndex& index, int role ) const
+QVariant InstanceList::data(const QModelIndex &index, int role) const
{
- if ( !index.isValid() )
+ if (!index.isValid())
{
return QVariant();
}
- BaseInstance *pdata = static_cast<BaseInstance*> ( index.internalPointer() );
- switch ( role )
+ BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
+ switch (role)
{
case InstancePointerRole:
{
- QVariant v = qVariantFromValue((void *) pdata);
+ QVariant v = qVariantFromValue((void *)pdata);
return v;
}
case Qt::DisplayRole:
@@ -96,12 +96,12 @@ QVariant InstanceList::data ( const QModelIndex& index, int role ) const
return QVariant();
}
-Qt::ItemFlags InstanceList::flags ( const QModelIndex& index ) const
+Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
{
Qt::ItemFlags f;
- if ( index.isValid() )
+ if (index.isValid())
{
- f |= ( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
+ f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
}
return f;
}
@@ -116,23 +116,23 @@ void InstanceList::saveGroupList()
{
QString groupFileName = m_instDir + "/instgroups.json";
QFile groupFile(groupFileName);
-
+
// if you can't open the file, fail
- if (!groupFile.open(QIODevice::WriteOnly| QIODevice::Truncate))
+ if (!groupFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
// An error occurred. Ignore it.
- qDebug("Failed to read instance group file.");
+ QLOG_ERROR() << "Failed to read instance group file.";
return;
}
QTextStream out(&groupFile);
- QMap<QString, QSet<QString> > groupMap;
- for(auto instance: m_instances)
+ QMap<QString, QSet<QString>> groupMap;
+ for (auto instance : m_instances)
{
QString id = instance->id();
QString group = instance->group();
- if(group.isEmpty())
+ if (group.isEmpty())
continue;
- if(!groupMap.count(group))
+ if (!groupMap.count(group))
{
QSet<QString> set;
set.insert(id);
@@ -145,109 +145,114 @@ void InstanceList::saveGroupList()
}
}
QJsonObject toplevel;
- toplevel.insert("formatVersion",QJsonValue(QString("1")));
+ toplevel.insert("formatVersion", QJsonValue(QString("1")));
QJsonObject groupsArr;
- for(auto iter = groupMap.begin(); iter != groupMap.end(); iter++)
+ for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++)
{
auto list = iter.value();
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
- groupObj.insert("hidden",QJsonValue(QString("false")));
- for(auto item: list)
+ groupObj.insert("hidden", QJsonValue(QString("false")));
+ for (auto item : list)
{
instanceArr.append(QJsonValue(item));
}
- groupObj.insert("instances",instanceArr);
- groupsArr.insert(name,groupObj);
+ groupObj.insert("instances", instanceArr);
+ groupsArr.insert(name, groupObj);
}
- toplevel.insert("groups",groupsArr);
+ toplevel.insert("groups", groupsArr);
QJsonDocument doc(toplevel);
groupFile.write(doc.toJson());
groupFile.close();
}
-void InstanceList::loadGroupList(QMap<QString, QString> & groupMap)
+void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
{
QString groupFileName = m_instDir + "/instgroups.json";
-
+
// if there's no group file, fail
- if(!QFileInfo(groupFileName).exists())
+ if (!QFileInfo(groupFileName).exists())
return;
-
+
QFile groupFile(groupFileName);
-
+
// if you can't open the file, fail
if (!groupFile.open(QIODevice::ReadOnly))
{
// An error occurred. Ignore it.
- qDebug("Failed to read instance group file.");
+ QLOG_ERROR() << "Failed to read instance group file.";
return;
}
-
+
QTextStream in(&groupFile);
QString jsonStr = in.readAll();
groupFile.close();
-
+
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error);
-
+
// if the json was bad, fail
if (error.error != QJsonParseError::NoError)
{
- qWarning(QString("Failed to parse instance group file: %1 at offset %2").
- arg(error.errorString(), QString::number(error.offset)).toUtf8());
+ QLOG_ERROR() << QString("Failed to parse instance group file: %1 at offset %2")
+ .arg(error.errorString(), QString::number(error.offset))
+ .toUtf8();
return;
}
-
+
// if the root of the json wasn't an object, fail
if (!jsonDoc.isObject())
{
qWarning("Invalid group file. Root entry should be an object.");
return;
}
-
+
QJsonObject rootObj = jsonDoc.object();
-
+
// Make sure the format version matches, otherwise fail.
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
return;
-
+
// Get the groups. if it's not an object, fail
if (!rootObj.value("groups").isObject())
{
qWarning("Invalid group list JSON: 'groups' should be an object.");
return;
}
-
+
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
{
QString groupName = iter.key();
-
+
// If not an object, complain and skip to the next one.
if (!iter.value().isObject())
{
qWarning(QString("Group '%1' in the group list should "
- "be an object.").arg(groupName).toUtf8());
+ "be an object.")
+ .arg(groupName)
+ .toUtf8());
continue;
}
-
+
QJsonObject groupObj = iter.value().toObject();
if (!groupObj.value("instances").isArray())
{
qWarning(QString("Group '%1' in the group list is invalid. "
- "It should contain an array "
- "called 'instances'.").arg(groupName).toUtf8());
+ "It should contain an array "
+ "called 'instances'.")
+ .arg(groupName)
+ .toUtf8());
continue;
}
-
+
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
-
- for (QJsonArray::iterator iter2 = instancesArray.begin();
- iter2 != instancesArray.end(); iter2++)
+
+ for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end();
+ iter2++)
{
groupMap[(*iter2).toString()] = groupName;
}
@@ -259,9 +264,9 @@ InstanceList::InstListError InstanceList::loadList()
// load the instance groups
QMap<QString, QString> groupMap;
loadGroupList(groupMap);
-
+
beginResetModel();
-
+
m_instances.clear();
QDir dir(m_instDir);
QDirIterator iter(dir);
@@ -270,53 +275,55 @@ InstanceList::InstListError InstanceList::loadList()
QString subDir = iter.next();
if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
continue;
-
+
BaseInstance *instPtr = NULL;
auto &loader = InstanceFactory::get();
auto error = loader.loadInstance(instPtr, subDir);
-
- switch(error)
+
+ switch (error)
{
- case InstanceFactory::NoLoadError:
- break;
- case InstanceFactory::NotAnInstance:
- break;
+ case InstanceFactory::NoLoadError:
+ break;
+ case InstanceFactory::NotAnInstance:
+ break;
}
-
- if (error != InstanceFactory::NoLoadError &&
- error != InstanceFactory::NotAnInstance)
+
+ if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance)
{
- QString errorMsg = QString("Failed to load instance %1: ").
- arg(QFileInfo(subDir).baseName()).toUtf8();
-
+ QString errorMsg = QString("Failed to load instance %1: ")
+ .arg(QFileInfo(subDir).baseName())
+ .toUtf8();
+
switch (error)
{
default:
- errorMsg += QString("Unknown instance loader error %1").
- arg(error);
+ errorMsg += QString("Unknown instance loader error %1").arg(error);
break;
}
- qDebug(errorMsg.toUtf8());
+ QLOG_ERROR() << errorMsg.toUtf8();
}
else if (!instPtr)
{
- qDebug(QString("Error loading instance %1. Instance loader returned null.").
- arg(QFileInfo(subDir).baseName()).toUtf8());
+ QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
+ .arg(QFileInfo(subDir).baseName())
+ .toUtf8();
}
else
{
- QSharedPointer<BaseInstance> inst(instPtr);
+ std::shared_ptr<BaseInstance> inst(instPtr);
auto iter = groupMap.find(inst->id());
- if(iter != groupMap.end())
+ if (iter != groupMap.end())
{
inst->setGroupInitial((*iter));
}
- qDebug(QString("Loaded instance %1").arg(inst->name()).toUtf8());
+ QLOG_INFO() << "Loaded instance " << inst->name();
inst->setParent(this);
m_instances.append(inst);
- connect(instPtr, SIGNAL(propertiesChanged(BaseInstance*)),this, SLOT(propertiesChanged(BaseInstance*)));
- connect(instPtr, SIGNAL(groupChanged()),this, SLOT(groupChanged()));
- connect(instPtr, SIGNAL(nuked(BaseInstance*)), this, SLOT(instanceNuked(BaseInstance*)));
+ connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
+ SLOT(propertiesChanged(BaseInstance *)));
+ connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged()));
+ connect(instPtr, SIGNAL(nuked(BaseInstance *)), this,
+ SLOT(instanceNuked(BaseInstance *)));
}
}
endResetModel();
@@ -332,7 +339,8 @@ void InstanceList::clear()
m_instances.clear();
endResetModel();
emit dataIsInvalid();
-};
+}
+;
/// Add an instance. Triggers notifications, returns the new index
int InstanceList::add(InstancePtr t)
@@ -340,9 +348,10 @@ int InstanceList::add(InstancePtr t)
beginInsertRows(QModelIndex(), m_instances.size(), m_instances.size());
m_instances.append(t);
t->setParent(this);
- connect(t.data(), SIGNAL(propertiesChanged(BaseInstance*)),this, SLOT(propertiesChanged(BaseInstance*)));
- connect(t.data(), SIGNAL(groupChanged()),this, SLOT(groupChanged()));
- connect(t.data(), SIGNAL(nuked(BaseInstance*)), this, SLOT(instanceNuked(BaseInstance*)));
+ connect(t.get(), SIGNAL(propertiesChanged(BaseInstance *)), this,
+ SLOT(propertiesChanged(BaseInstance *)));
+ connect(t.get(), SIGNAL(groupChanged()), this, SLOT(groupChanged()));
+ connect(t.get(), SIGNAL(nuked(BaseInstance *)), this, SLOT(instanceNuked(BaseInstance *)));
endInsertRows();
return count() - 1;
}
@@ -351,7 +360,7 @@ InstancePtr InstanceList::getInstanceById(QString instId)
{
QListIterator<InstancePtr> iter(m_instances);
InstancePtr inst;
- while(iter.hasNext())
+ while (iter.hasNext())
{
inst = iter.next();
if (inst->id() == instId)
@@ -363,11 +372,11 @@ InstancePtr InstanceList::getInstanceById(QString instId)
return iter.peekPrevious();
}
-int InstanceList::getInstIndex ( BaseInstance* inst )
+int InstanceList::getInstIndex(BaseInstance *inst)
{
- for(int i = 0; i < m_instances.count(); i++)
+ for (int i = 0; i < m_instances.count(); i++)
{
- if(inst == m_instances[i].data())
+ if (inst == m_instances[i].get())
{
return i;
}
@@ -375,40 +384,39 @@ int InstanceList::getInstIndex ( BaseInstance* inst )
return -1;
}
-
-void InstanceList::instanceNuked ( BaseInstance* inst )
+void InstanceList::instanceNuked(BaseInstance *inst)
{
int i = getInstIndex(inst);
- if(i != -1)
+ if (i != -1)
{
- beginRemoveRows(QModelIndex(),i,i);
+ beginRemoveRows(QModelIndex(), i, i);
m_instances.removeAt(i);
endRemoveRows();
}
}
-
-void InstanceList::propertiesChanged(BaseInstance * inst)
+void InstanceList::propertiesChanged(BaseInstance *inst)
{
int i = getInstIndex(inst);
- if(i != -1)
+ if (i != -1)
{
emit dataChanged(index(i), index(i));
}
}
-InstanceProxyModel::InstanceProxyModel ( QObject *parent )
- : KCategorizedSortFilterProxyModel ( parent )
+InstanceProxyModel::InstanceProxyModel(QObject *parent)
+ : KCategorizedSortFilterProxyModel(parent)
{
// disable since by default we are globally sorting by date:
setCategorizedModel(true);
}
-bool InstanceProxyModel::subSortLessThan (const QModelIndex& left, const QModelIndex& right ) const
+bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
+ const QModelIndex &right) const
{
- BaseInstance *pdataLeft = static_cast<BaseInstance*> ( left.internalPointer() );
- BaseInstance *pdataRight = static_cast<BaseInstance*> ( right.internalPointer() );
- //kDebug() << *pdataLeft << *pdataRight;
+ BaseInstance *pdataLeft = static_cast<BaseInstance *>(left.internalPointer());
+ BaseInstance *pdataRight = static_cast<BaseInstance *>(right.internalPointer());
+ // kDebug() << *pdataLeft << *pdataRight;
return QString::localeAwareCompare(pdataLeft->name(), pdataRight->name()) < 0;
- //return pdataLeft->name() < pdataRight->name();
+ // return pdataLeft->name() < pdataRight->name();
}
diff --git a/logic/lists/JavaVersionList.cpp b/logic/lists/JavaVersionList.cpp
new file mode 100644
index 00000000..5389c4cc
--- /dev/null
+++ b/logic/lists/JavaVersionList.cpp
@@ -0,0 +1,203 @@
+/* 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 "JavaVersionList.h"
+#include "MultiMC.h"
+
+#include <QtNetwork>
+#include <QtXml>
+#include <QRegExp>
+
+#include <logger/QsLog.h>
+#include <logic/JavaUtils.h>
+
+JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent)
+{
+}
+
+Task *JavaVersionList::getLoadTask()
+{
+ return new JavaListLoadTask(this);
+}
+
+
+const BaseVersionPtr JavaVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+bool JavaVersionList::isLoaded()
+{
+ return m_loaded;
+}
+
+int JavaVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+int JavaVersionList::columnCount(const QModelIndex &parent) const
+{
+ return 4;
+}
+
+QVariant JavaVersionList::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+ auto version = std::dynamic_pointer_cast<JavaVersion>(m_vlist[index.row()]);
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case 0:
+ return version->id;
+
+ case 1:
+ return version->arch;
+
+ case 2:
+ return version->path;
+
+ case 3:
+ return version->recommended ? tr("Yes") : tr("No");
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return version->descriptor();
+
+ case VersionPointerRole:
+ return qVariantFromValue(m_vlist[index.row()]);
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case 0:
+ return "Version";
+
+ case 1:
+ return "Arch";
+
+ case 2:
+ return "Path";
+
+ case 3:
+ return "Recommended";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case 0:
+ return "The name of the version.";
+
+ case 1:
+ return "The architecture this version is for.";
+
+ case 2:
+ return "Path to this Java version.";
+
+ case 3:
+ return "Whether the version is recommended or not.";
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+BaseVersionPtr JavaVersionList::getTopRecommended() const
+{
+ for (int i = 0; i < m_vlist.length(); i++)
+ {
+ auto ver = std::dynamic_pointer_cast<JavaVersion>(m_vlist.at(i));
+ if (ver->recommended)
+ {
+ return m_vlist.at(i);
+ }
+ }
+ return BaseVersionPtr();
+}
+
+void JavaVersionList::updateListData(QList<BaseVersionPtr> versions)
+{
+ beginResetModel();
+ m_vlist = versions;
+ m_loaded = true;
+ endResetModel();
+ // NOW SORT!!
+ // sort();
+}
+
+void JavaVersionList::sort()
+{
+ // NO-OP for now
+}
+
+JavaListLoadTask::JavaListLoadTask(JavaVersionList *vlist)
+{
+ m_list = vlist;
+ m_currentRecommended = NULL;
+}
+
+JavaListLoadTask::~JavaListLoadTask()
+{
+}
+
+void JavaListLoadTask::executeTask()
+{
+ setStatus("Detecting Java installations...");
+
+ JavaUtils ju;
+ QList<JavaVersionPtr> javas = ju.FindJavaPaths();
+
+ QList<BaseVersionPtr> javas_bvp;
+ for(int i = 0; i < javas.length(); i++)
+ {
+ BaseVersionPtr java = std::dynamic_pointer_cast<BaseVersion>(javas.at(i));
+
+ if(java)
+ {
+ javas_bvp.append(java);
+ }
+ }
+
+ m_list->updateListData(javas_bvp);
+
+ emitSucceeded();
+}
diff --git a/logic/lists/JavaVersionList.h b/logic/lists/JavaVersionList.h
new file mode 100644
index 00000000..23bccfe4
--- /dev/null
+++ b/logic/lists/JavaVersionList.h
@@ -0,0 +1,93 @@
+/* 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 <QObject>
+#include <QAbstractListModel>
+#include <QSharedPointer>
+
+#include "BaseVersionList.h"
+#include "logic/tasks/Task.h"
+
+class JavaListLoadTask;
+
+struct JavaVersion : public BaseVersion
+{
+ virtual QString descriptor()
+ {
+ return id;
+ }
+
+ virtual QString name()
+ {
+ return id;
+ }
+
+ virtual QString typeString() const
+ {
+ return arch;
+ }
+
+ QString id;
+ QString arch;
+ QString path;
+ bool recommended;
+};
+
+typedef std::shared_ptr<JavaVersion> JavaVersionPtr;
+
+class JavaVersionList : public BaseVersionList
+{
+ Q_OBJECT
+public:
+ explicit JavaVersionList(QObject *parent = 0);
+
+ virtual Task *getLoadTask();
+ virtual bool isLoaded();
+ virtual const BaseVersionPtr at(int i) const;
+ virtual int count() const;
+ virtual void sort();
+
+ virtual BaseVersionPtr getTopRecommended() const;
+
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation,
+ int role) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+
+public slots:
+ virtual void updateListData(QList<BaseVersionPtr> versions);
+
+protected:
+ QList<BaseVersionPtr> m_vlist;
+
+ bool m_loaded = false;
+};
+
+class JavaListLoadTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit JavaListLoadTask(JavaVersionList *vlist);
+ ~JavaListLoadTask();
+
+ virtual void executeTask();
+
+protected:
+ JavaVersionList *m_list;
+ JavaVersion *m_currentRecommended;
+};
diff --git a/logic/lists/LwjglVersionList.cpp b/logic/lists/LwjglVersionList.cpp
index d7826a82..6bf401ec 100644
--- a/logic/lists/LwjglVersionList.cpp
+++ b/logic/lists/LwjglVersionList.cpp
@@ -3,7 +3,7 @@
* 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
@@ -17,23 +17,14 @@
#include "MultiMC.h"
#include <QtNetwork>
-
#include <QtXml>
-
#include <QRegExp>
-#define RSS_URL "http://sourceforge.net/api/file/index/project-id/58488/mtime/desc/rss"
-
-LWJGLVersionList mainVersionList;
-
-LWJGLVersionList &LWJGLVersionList::get()
-{
- return mainVersionList;
-}
+#include <logger/QsLog.h>
+#define RSS_URL "http://sourceforge.net/api/file/index/project-id/58488/mtime/desc/rss"
-LWJGLVersionList::LWJGLVersionList(QObject *parent) :
- QAbstractListModel(parent)
+LWJGLVersionList::LWJGLVersionList(QObject *parent) : QAbstractListModel(parent)
{
setLoading(false);
}
@@ -42,20 +33,20 @@ QVariant LWJGLVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
-
+
if (index.row() > count())
return QVariant();
-
+
const PtrLWJGLVersion version = at(index.row());
-
+
switch (role)
{
case Qt::DisplayRole:
return version->name();
-
+
case Qt::ToolTipRole:
return version->url();
-
+
default:
return QVariant();
}
@@ -67,10 +58,10 @@ QVariant LWJGLVersionList::headerData(int section, Qt::Orientation orientation,
{
case Qt::DisplayRole:
return "Version";
-
+
case Qt::ToolTipRole:
return "LWJGL version name.";
-
+
default:
return QVariant();
}
@@ -89,7 +80,7 @@ bool LWJGLVersionList::isLoading() const
void LWJGLVersionList::loadList()
{
Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)");
-
+
setLoading(true);
auto worker = MMC->qnam();
reply = worker->get(QNetworkRequest(QUrl(RSS_URL)));
@@ -110,68 +101,68 @@ void LWJGLVersionList::netRequestComplete()
if (reply->error() == QNetworkReply::NoError)
{
QRegExp lwjglRegex("lwjgl-(([0-9]\\.?)+)\\.zip");
- Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list",
- "LWJGL regex is invalid");
-
+ Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", "LWJGL regex is invalid");
+
QDomDocument doc;
-
+
QString xmlErrorMsg;
int errorLine;
if (!doc.setContent(reply->readAll(), false, &xmlErrorMsg, &errorLine))
{
- failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " + QString::number(errorLine));
+ failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " +
+ QString::number(errorLine));
setLoading(false);
return;
}
-
+
QDomNodeList items = doc.elementsByTagName("item");
-
+
QList<PtrLWJGLVersion> tempList;
-
+
for (int i = 0; i < items.length(); i++)
{
Q_ASSERT_X(items.at(i).isElement(), "load LWJGL list",
"XML element isn't an element... wat?");
-
+
QDomElement linkElement = getDomElementByTagName(items.at(i).toElement(), "link");
if (linkElement.isNull())
{
qWarning() << "Link element" << i << "in RSS feed doesn't exist! Skipping.";
continue;
}
-
+
QString link = linkElement.text();
-
+
// Make sure it's a download link.
if (link.endsWith("/download") && link.contains(lwjglRegex))
{
QString name = link.mid(lwjglRegex.indexIn(link) + 6);
// Subtract 4 here to remove the .zip file extension.
name = name.left(lwjglRegex.matchedLength() - 10);
-
+
QUrl url(link);
if (!url.isValid())
{
qWarning() << "LWJGL version URL isn't valid:" << link << "Skipping.";
continue;
}
-
+
tempList.append(LWJGLVersion::Create(name, link));
}
}
-
+
beginResetModel();
m_vlist.swap(tempList);
endResetModel();
-
- qDebug("Loaded LWJGL list.");
+
+ QLOG_INFO() << "Loaded LWJGL list.";
finished();
}
else
{
failed("Failed to load LWJGL list. Network error: " + reply->errorString());
}
-
+
setLoading(false);
reply->deleteLater();
}
@@ -181,13 +172,12 @@ const PtrLWJGLVersion LWJGLVersionList::getVersion(const QString &versionName)
for (int i = 0; i < count(); i++)
{
QString name = at(i)->name();
- if ( name == versionName)
+ if (name == versionName)
return at(i);
}
return PtrLWJGLVersion();
}
-
void LWJGLVersionList::failed(QString msg)
{
qWarning() << msg;
diff --git a/logic/lists/LwjglVersionList.h b/logic/lists/LwjglVersionList.h
index 638a0b67..99712292 100644
--- a/logic/lists/LwjglVersionList.h
+++ b/logic/lists/LwjglVersionList.h
@@ -17,13 +17,13 @@
#include <QObject>
#include <QAbstractListModel>
-#include <QSharedPointer>
#include <QUrl>
-
#include <QNetworkReply>
+#include <memory>
+
class LWJGLVersion;
-typedef QSharedPointer<LWJGLVersion> PtrLWJGLVersion;
+typedef std::shared_ptr<LWJGLVersion> PtrLWJGLVersion;
class LWJGLVersion : public QObject
{
@@ -53,8 +53,6 @@ class LWJGLVersionList : public QAbstractListModel
public:
explicit LWJGLVersionList(QObject *parent = 0);
- static LWJGLVersionList &get();
-
bool isLoaded() { return m_vlist.length() > 0; }
const PtrLWJGLVersion getVersion(const QString &versionName);
diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp
index 42fb1b50..dd26714a 100644
--- a/logic/lists/MinecraftVersionList.cpp
+++ b/logic/lists/MinecraftVersionList.cpp
@@ -3,7 +3,7 @@
* 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
@@ -16,8 +16,6 @@
#include "MinecraftVersionList.h"
#include <MultiMC.h>
-#include <QDebug>
-
#include <QtXml>
#include <QJsonDocument>
@@ -34,12 +32,8 @@
#define ASSETS_URLBASE "http://assets.minecraft.net/"
#define MCN_URLBASE "http://sonicrules.org/mcnweb.py"
-MinecraftVersionList mcVList;
-
-MinecraftVersionList::MinecraftVersionList(QObject *parent) :
- InstVersionList(parent)
+MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
{
-
}
Task *MinecraftVersionList::getLoadTask()
@@ -52,7 +46,7 @@ bool MinecraftVersionList::isLoaded()
return m_loaded;
}
-const InstVersionPtr MinecraftVersionList::at(int i) const
+const BaseVersionPtr MinecraftVersionList::at(int i) const
{
return m_vlist.at(i);
}
@@ -62,11 +56,11 @@ int MinecraftVersionList::count() const
return m_vlist.count();
}
-bool cmpVersions(InstVersionPtr first, InstVersionPtr second)
+bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
{
- const InstVersion & left = *first;
- const InstVersion & right = *second;
- return left > right;
+ auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
+ auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
+ return left->timestamp > right->timestamp;
}
void MinecraftVersionList::sort()
@@ -76,25 +70,20 @@ void MinecraftVersionList::sort()
endResetModel();
}
-InstVersionPtr MinecraftVersionList::getLatestStable() const
+BaseVersionPtr MinecraftVersionList::getLatestStable() const
{
for (int i = 0; i < m_vlist.length(); i++)
{
- auto ver = m_vlist.at(i).dynamicCast<MinecraftVersion>();
+ auto ver = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist.at(i));
if (ver->is_latest && !ver->is_snapshot)
{
return m_vlist.at(i);
}
}
- return InstVersionPtr();
-}
-
-MinecraftVersionList &MinecraftVersionList::getMainList()
-{
- return mcVList;
+ return BaseVersionPtr();
}
-void MinecraftVersionList::updateListData(QList<InstVersionPtr > versions)
+void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
{
beginResetModel();
m_vlist = versions;
@@ -118,7 +107,6 @@ inline QDateTime timeFromS3Time(QString str)
return QDateTime::fromString(str, Qt::ISODate);
}
-
MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
{
m_list = vlist;
@@ -131,9 +119,13 @@ MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
legacyWhitelist.insert("1.4.6");
legacyWhitelist.insert("1.4.5");
legacyWhitelist.insert("1.4.4");
+ legacyWhitelist.insert("1.4.3");
legacyWhitelist.insert("1.4.2");
+ legacyWhitelist.insert("1.4.1");
+ legacyWhitelist.insert("1.4");
legacyWhitelist.insert("1.3.2");
legacyWhitelist.insert("1.3.1");
+ legacyWhitelist.insert("1.3");
legacyWhitelist.insert("1.2.5");
legacyWhitelist.insert("1.2.4");
legacyWhitelist.insert("1.2.3");
@@ -156,91 +148,91 @@ void MCVListLoadTask::executeTask()
connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
}
-
void MCVListLoadTask::list_downloaded()
{
- if(vlistReply->error() != QNetworkReply::QNetworkReply::NoError)
+ if (vlistReply->error() != QNetworkReply::NoError)
{
vlistReply->deleteLater();
emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString());
return;
}
-
+
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
vlistReply->deleteLater();
-
+
if (jsonError.error != QJsonParseError::NoError)
{
emitFailed("Error parsing version list JSON:" + jsonError.errorString());
return;
}
- if(!jsonDoc.isObject())
+ if (!jsonDoc.isObject())
{
emitFailed("Error parsing version list JSON: jsonDoc is not an object");
return;
}
-
+
QJsonObject root = jsonDoc.object();
-
+
// Get the ID of the latest release and the latest snapshot.
- if(!root.value("latest").isObject())
+ if (!root.value("latest").isObject())
{
emitFailed("Error parsing version list JSON: version list is missing 'latest' object");
return;
}
-
+
QJsonObject latest = root.value("latest").toObject();
-
+
QString latestReleaseID = latest.value("release").toString("");
QString latestSnapshotID = latest.value("snapshot").toString("");
- if(latestReleaseID.isEmpty())
+ if (latestReleaseID.isEmpty())
{
emitFailed("Error parsing version list JSON: latest release field is missing");
return;
}
- if(latestSnapshotID.isEmpty())
+ if (latestSnapshotID.isEmpty())
{
emitFailed("Error parsing version list JSON: latest snapshot field is missing");
return;
}
// Now, get the array of versions.
- if(!root.value("versions").isArray())
+ if (!root.value("versions").isArray())
{
- emitFailed("Error parsing version list JSON: version list object is missing 'versions' array");
+ emitFailed(
+ "Error parsing version list JSON: version list object is missing 'versions' array");
return;
}
QJsonArray versions = root.value("versions").toArray();
-
- QList<InstVersionPtr > tempList;
+
+ QList<BaseVersionPtr> tempList;
for (int i = 0; i < versions.count(); i++)
{
bool is_snapshot = false;
bool is_latest = false;
-
+
// Load the version info.
- if(!versions[i].isObject())
+ if (!versions[i].isObject())
{
- //FIXME: log this somewhere
+ // FIXME: log this somewhere
continue;
}
QJsonObject version = versions[i].toObject();
QString versionID = version.value("id").toString("");
QString versionTimeStr = version.value("releaseTime").toString("");
QString versionTypeStr = version.value("type").toString("");
- if(versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
+ if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
{
- //FIXME: log this somewhere
+ // FIXME: log this somewhere
continue;
}
-
+
// Parse the timestamp.
QDateTime versionTime = timeFromS3Time(versionTimeStr);
- if(!versionTime.isValid())
+ if (!versionTime.isValid())
{
- //FIXME: log this somewhere
+ // FIXME: log this somewhere
continue;
}
// Parse the type.
@@ -248,23 +240,25 @@ void MCVListLoadTask::list_downloaded()
// OneSix or Legacy. use filter to determine type
if (versionTypeStr == "release")
{
- versionType = legacyWhitelist.contains(versionID)?MinecraftVersion::Legacy:MinecraftVersion::OneSix;
+ versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
+ : MinecraftVersion::OneSix;
is_latest = (versionID == latestReleaseID);
is_snapshot = false;
}
- else if(versionTypeStr == "snapshot") // It's a snapshot... yay
+ else if (versionTypeStr == "snapshot") // It's a snapshot... yay
{
- versionType = legacyWhitelist.contains(versionID)?MinecraftVersion::Legacy:MinecraftVersion::OneSix;
+ versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
+ : MinecraftVersion::OneSix;
is_latest = (versionID == latestSnapshotID);
is_snapshot = true;
}
- else if(versionTypeStr == "old_alpha")
+ else if (versionTypeStr == "old_alpha")
{
versionType = MinecraftVersion::Nostalgia;
is_latest = false;
is_snapshot = false;
}
- else if(versionTypeStr == "old_beta")
+ else if (versionTypeStr == "old_beta")
{
versionType = MinecraftVersion::Legacy;
is_latest = false;
@@ -272,15 +266,15 @@ void MCVListLoadTask::list_downloaded()
}
else
{
- //FIXME: log this somewhere
+ // FIXME: log this somewhere
continue;
}
// Get the download URL.
QString dlUrl = QString(MCVLIST_URLBASE) + versionID + "/";
-
+
// Now, we construct the version object and add it to the list.
- QSharedPointer<MinecraftVersion> mcVersion(new MinecraftVersion());
- mcVersion->name = mcVersion->descriptor = versionID;
+ std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
+ mcVersion->m_name = mcVersion->m_descriptor = versionID;
mcVersion->timestamp = versionTime.toMSecsSinceEpoch();
mcVersion->download_url = dlUrl;
mcVersion->is_latest = is_latest;
@@ -289,12 +283,7 @@ void MCVListLoadTask::list_downloaded()
tempList.append(mcVersion);
}
m_list->updateListData(tempList);
-
+
emitSucceeded();
return;
}
-
-// FIXME: we should have a local cache of the version list and a local cache of version data
-bool MCVListLoadTask::loadFromVList()
-{
-}
diff --git a/logic/lists/MinecraftVersionList.h b/logic/lists/MinecraftVersionList.h
index 0477379f..ed68efbb 100644
--- a/logic/lists/MinecraftVersionList.h
+++ b/logic/lists/MinecraftVersionList.h
@@ -20,14 +20,14 @@
#include <QSet>
#include <QSharedPointer>
-#include "InstVersionList.h"
+#include "BaseVersionList.h"
#include "logic/tasks/Task.h"
#include "logic/MinecraftVersion.h"
class MCVListLoadTask;
class QNetworkReply;
-class MinecraftVersionList : public InstVersionList
+class MinecraftVersionList : public BaseVersionList
{
Q_OBJECT
public:
@@ -37,25 +37,19 @@ public:
virtual Task *getLoadTask();
virtual bool isLoaded();
- virtual const InstVersionPtr at(int i) const;
+ virtual const BaseVersionPtr at(int i) const;
virtual int count() const;
virtual void sort();
- virtual InstVersionPtr getLatestStable() const;
-
- /*!
- * Gets the main version list instance.
- */
- static MinecraftVersionList &getMainList();
-
+ virtual BaseVersionPtr getLatestStable() const;
protected:
- QList<InstVersionPtr > m_vlist;
+ QList<BaseVersionPtr> m_vlist;
- bool m_loaded;
+ bool m_loaded = false;
protected slots:
- virtual void updateListData(QList<InstVersionPtr > versions);
+ virtual void updateListData(QList<BaseVersionPtr> versions);
};
class MCVListLoadTask : public Task
@@ -72,9 +66,6 @@ protected slots:
void list_downloaded();
protected:
- //! Loads versions from Mojang's official version list.
- bool loadFromVList();
-
QNetworkReply *vlistReply;
MinecraftVersionList *m_list;
MinecraftVersion *m_currentStable;
diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp
new file mode 100644
index 00000000..ba771eef
--- /dev/null
+++ b/logic/net/ByteArrayDownload.cpp
@@ -0,0 +1,65 @@
+#include "ByteArrayDownload.h"
+#include "MultiMC.h"
+#include <logger/QsLog.h>
+
+ByteArrayDownload::ByteArrayDownload(QUrl url) : Download()
+{
+ m_url = url;
+ m_status = Job_NotStarted;
+}
+
+void ByteArrayDownload::start()
+{
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void ByteArrayDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ emit progress(index_within_job, bytesReceived, bytesTotal);
+}
+
+void ByteArrayDownload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit()
+ << "Network error: " << error;
+ m_status = Job_Failed;
+}
+
+void ByteArrayDownload::downloadFinished()
+{
+ // if the download succeeded
+ if (m_status != Job_Failed)
+ {
+ // nothing went wrong...
+ m_status = Job_Finished;
+ m_data = m_reply->readAll();
+ m_reply.reset();
+ emit succeeded(index_within_job);
+ return;
+ }
+ // else the download failed
+ else
+ {
+ m_reply.reset();
+ emit failed(index_within_job);
+ return;
+ }
+}
+
+void ByteArrayDownload::downloadReadyRead()
+{
+ // ~_~
+}
diff --git a/logic/net/ByteArrayDownload.h b/logic/net/ByteArrayDownload.h
new file mode 100644
index 00000000..cfc6a8d0
--- /dev/null
+++ b/logic/net/ByteArrayDownload.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "Download.h"
+
+class ByteArrayDownload: public Download
+{
+ Q_OBJECT
+public:
+ ByteArrayDownload(QUrl url);
+
+public:
+ /// if not saving to file, downloaded data is placed here
+ QByteArray m_data;
+
+public slots:
+ virtual void start();
+
+protected slots:
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ void downloadError(QNetworkReply::NetworkError error);
+ void downloadFinished();
+ void downloadReadyRead();
+};
+
+typedef std::shared_ptr<ByteArrayDownload> ByteArrayDownloadPtr;
diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp
new file mode 100644
index 00000000..309eb345
--- /dev/null
+++ b/logic/net/CacheDownload.cpp
@@ -0,0 +1,136 @@
+#include "MultiMC.h"
+#include "CacheDownload.h"
+#include <pathutils.h>
+
+#include <QCryptographicHash>
+#include <QFileInfo>
+#include <QDateTime>
+#include <logger/QsLog.h>
+
+CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry)
+ : Download(), md5sum(QCryptographicHash::Md5)
+{
+ m_url = url;
+ m_entry = entry;
+ m_target_path = entry->getFullPath();
+ m_status = Job_NotStarted;
+ m_opened_for_saving = false;
+}
+
+void CacheDownload::start()
+{
+ if (!m_entry->stale)
+ {
+ emit succeeded(index_within_job);
+ return;
+ }
+ m_output_file.setFileName(m_target_path);
+ // if there already is a file and md5 checking is in effect and it can be opened
+ if (!ensureFilePathExists(m_target_path))
+ {
+ emit failed(index_within_job);
+ return;
+ }
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+ if(m_entry->remote_changed_timestamp.size())
+ request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->remote_changed_timestamp.toLatin1());
+ if(m_entry->etag.size())
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1());
+
+ request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Cached)");
+
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void CacheDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ emit progress(index_within_job, bytesReceived, bytesTotal);
+}
+
+void CacheDownload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ QLOG_ERROR() << "Failed" << m_url.toString() << "with reason" << error;
+ m_status = Job_Failed;
+}
+void CacheDownload::downloadFinished()
+{
+ // if the download succeeded
+ if (m_status != Job_Failed)
+ {
+
+ // nothing went wrong...
+ m_status = Job_Finished;
+ if (m_opened_for_saving)
+ {
+ // save the data to the downloadable if we aren't saving to file
+ m_output_file.close();
+ m_entry->md5sum = md5sum.result().toHex().constData();
+ }
+ else
+ {
+ if (m_output_file.open(QIODevice::ReadOnly))
+ {
+ m_entry->md5sum =
+ QCryptographicHash::hash(m_output_file.readAll(), QCryptographicHash::Md5)
+ .toHex()
+ .constData();
+ m_output_file.close();
+ }
+ }
+ QFileInfo output_file_info(m_target_path);
+
+ m_entry->etag = m_reply->rawHeader("ETag").constData();
+ if(m_reply->hasRawHeader("Last-Modified"))
+ {
+ m_entry->remote_changed_timestamp = m_reply->rawHeader("Last-Modified").constData();
+ }
+ m_entry->local_changed_timestamp =
+ output_file_info.lastModified().toUTC().toMSecsSinceEpoch();
+ m_entry->stale = false;
+ MMC->metacache()->updateEntry(m_entry);
+
+ m_reply.reset();
+ emit succeeded(index_within_job);
+ return;
+ }
+ // else the download failed
+ else
+ {
+ m_output_file.close();
+ m_output_file.remove();
+ m_reply.reset();
+ emit failed(index_within_job);
+ return;
+ }
+}
+
+void CacheDownload::downloadReadyRead()
+{
+ if (!m_opened_for_saving)
+ {
+ if (!m_output_file.open(QIODevice::WriteOnly))
+ {
+ /*
+ * Can't open the file... the job failed
+ */
+ m_reply->abort();
+ emit failed(index_within_job);
+ return;
+ }
+ m_opened_for_saving = true;
+ }
+ QByteArray ba = m_reply->readAll();
+ md5sum.addData(ba);
+ m_output_file.write(ba);
+}
diff --git a/logic/net/CacheDownload.h b/logic/net/CacheDownload.h
new file mode 100644
index 00000000..295391b1
--- /dev/null
+++ b/logic/net/CacheDownload.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "Download.h"
+#include "HttpMetaCache.h"
+#include <QFile>
+#include <qcryptographichash.h>
+
+class CacheDownload : public Download
+{
+ Q_OBJECT
+public:
+ MetaEntryPtr m_entry;
+ /// is the saving file already open?
+ bool m_opened_for_saving;
+ /// if saving to file, use the one specified in this string
+ QString m_target_path;
+ /// this is the output file, if any
+ QFile m_output_file;
+ /// the hash-as-you-download
+ QCryptographicHash md5sum;
+public:
+ explicit CacheDownload(QUrl url, MetaEntryPtr entry);
+
+protected slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead();
+
+public slots:
+ virtual void start();
+};
+
+typedef std::shared_ptr<CacheDownload> CacheDownloadPtr;
diff --git a/logic/net/Download.h b/logic/net/Download.h
new file mode 100644
index 00000000..ca4bee9f
--- /dev/null
+++ b/logic/net/Download.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <QObject>
+#include <QUrl>
+#include <memory>
+#include <QNetworkReply>
+
+enum JobStatus
+{
+ Job_NotStarted,
+ Job_InProgress,
+ Job_Finished,
+ Job_Failed
+};
+
+class Download : public QObject
+{
+ Q_OBJECT
+protected:
+ explicit Download() : QObject(0) {};
+
+public:
+ virtual ~Download() {};
+
+public:
+ /// the network reply
+ std::shared_ptr<QNetworkReply> m_reply;
+
+ /// source URL
+ QUrl m_url;
+
+ /// The file's status
+ JobStatus m_status;
+
+ /// index within the parent job
+ int index_within_job = 0;
+
+signals:
+ void started(int index);
+ void progress(int index, qint64 current, qint64 total);
+ void succeeded(int index);
+ void failed(int index);
+
+protected slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0;
+ virtual void downloadError(QNetworkReply::NetworkError error) = 0;
+ virtual void downloadFinished() = 0;
+ virtual void downloadReadyRead() = 0;
+
+public slots:
+ virtual void start() = 0;
+};
+
+typedef std::shared_ptr<Download> DownloadPtr;
diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp
index cad9ae72..fa3e655e 100644
--- a/logic/net/DownloadJob.cpp
+++ b/logic/net/DownloadJob.cpp
@@ -1,181 +1,135 @@
#include "DownloadJob.h"
#include "pathutils.h"
#include "MultiMC.h"
+#include "FileDownload.h"
+#include "ByteArrayDownload.h"
+#include "CacheDownload.h"
-Download::Download (QUrl url, QString target_path, QString expected_md5 )
- :Job()
-{
- m_url = url;
- m_target_path = target_path;
- m_expected_md5 = expected_md5;
-
- m_check_md5 = m_expected_md5.size();
- m_save_to_file = m_target_path.size();
- m_status = Job_NotStarted;
- m_opened_for_saving = false;
-}
-
-void Download::start()
-{
- if ( m_save_to_file )
- {
- QString filename = m_target_path;
- m_output_file.setFileName ( filename );
- // 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 ) )
- {
- // 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 )
- {
- qDebug() << "Skipping " << m_url.toString() << ": md5 match.";
- emit succeeded(index_within_job);
- return;
- }
- else
- {
- m_expected_md5 = hash;
- }
- }
- if(!ensureFilePathExists(filename))
- {
- emit failed(index_within_job);
- return;
- }
- }
- qDebug() << "Downloading " << m_url.toString();
- QNetworkRequest request ( m_url );
- request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1());
-
- auto worker = MMC->qnam();
- QNetworkReply * rep = worker->get ( request );
-
- m_reply = QSharedPointer<QNetworkReply> ( rep, &QObject::deleteLater );
- connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) );
- connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) );
- connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) );
- connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) );
-}
-
-void Download::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal )
-{
- emit progress (index_within_job, bytesReceived, bytesTotal );
-}
+#include <logger/QsLog.h>
-void Download::downloadError ( QNetworkReply::NetworkError error )
+ByteArrayDownloadPtr DownloadJob::addByteArrayDownload(QUrl url)
{
- // error happened during download.
- // TODO: log the reason why
- m_status = Job_Failed;
+ ByteArrayDownloadPtr ptr(new ByteArrayDownload(url));
+ ptr->index_within_job = downloads.size();
+ downloads.append(ptr);
+ parts_progress.append(part_info());
+ total_progress++;
+ return ptr;
}
-void Download::downloadFinished()
+FileDownloadPtr DownloadJob::addFileDownload(QUrl url, QString rel_target_path)
{
- // if the download succeeded
- if ( m_status != Job_Failed )
- {
- // nothing went wrong...
- m_status = Job_Finished;
- // save the data to the downloadable if we aren't saving to file
- if ( !m_save_to_file )
- {
- m_data = m_reply->readAll();
- }
- else
- {
- m_output_file.close();
- }
-
- //TODO: check md5 here!
- m_reply.clear();
- emit succeeded(index_within_job);
- return;
- }
- // else the download failed
- else
- {
- if ( m_save_to_file )
- {
- m_output_file.close();
- m_output_file.remove();
- }
- m_reply.clear();
- emit failed(index_within_job);
- return;
- }
+ FileDownloadPtr ptr(new FileDownload(url, rel_target_path));
+ ptr->index_within_job = downloads.size();
+ downloads.append(ptr);
+ parts_progress.append(part_info());
+ total_progress++;
+ return ptr;
}
-void Download::downloadReadyRead()
+CacheDownloadPtr DownloadJob::addCacheDownload(QUrl url, MetaEntryPtr entry)
{
- 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 failed(index_within_job);
- return;
- }
- m_opened_for_saving = true;
- }
- m_output_file.write ( m_reply->readAll() );
- }
+ CacheDownloadPtr ptr(new CacheDownload(url, entry));
+ ptr->index_within_job = downloads.size();
+ downloads.append(ptr);
+ parts_progress.append(part_info());
+ total_progress++;
+ return ptr;
}
-DownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path, QString expected_md5 )
+ForgeXzDownloadPtr DownloadJob::addForgeXzDownload(QUrl url, MetaEntryPtr entry)
{
- DownloadPtr ptr (new Download(url, rel_target_path, expected_md5));
+ ForgeXzDownloadPtr ptr(new ForgeXzDownload(url, entry));
ptr->index_within_job = downloads.size();
downloads.append(ptr);
+ parts_progress.append(part_info());
+ total_progress++;
return ptr;
}
-void DownloadJob::partSucceeded ( int index )
+void DownloadJob::partSucceeded(int index)
{
+ // do progress. all slots are 1 in size at least
+ auto &slot = parts_progress[index];
+ partProgress(index, slot.total_progress, slot.total_progress);
+
num_succeeded++;
- if(num_failed + num_succeeded == downloads.size())
+ QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_succeeded << "/"
+ << downloads.size();
+ if (num_failed + num_succeeded == downloads.size())
{
- if(num_failed)
+ if (num_failed)
{
- qDebug() << "Download JOB failed: " << this;
+ QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed.";
emit failed();
}
else
{
- qDebug() << "Download JOB succeeded: " << this;
+ QLOG_INFO() << m_job_name.toLocal8Bit() << "succeeded.";
emit succeeded();
}
}
}
-void DownloadJob::partFailed ( int index )
+void DownloadJob::partFailed(int index)
{
- num_failed++;
- if(num_failed + num_succeeded == downloads.size())
+ auto &slot = parts_progress[index];
+ if (slot.failures == 3)
{
- qDebug() << "Download JOB failed: " << this;
- emit failed();
+ QLOG_ERROR() << "Part" << index << "failed 3 times (" << downloads[index]->m_url << ")";
+ num_failed++;
+ if (num_failed + num_succeeded == downloads.size())
+ {
+ QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed.";
+ emit failed();
+ }
+ }
+ else
+ {
+ QLOG_ERROR() << "Part" << index << "failed, restarting (" << downloads[index]->m_url
+ << ")";
+ // restart the job
+ slot.failures++;
+ downloads[index]->start();
}
}
-void DownloadJob::partProgress ( int index, qint64 bytesReceived, qint64 bytesTotal )
+void DownloadJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal)
{
- // PROGRESS? DENIED!
-}
+ auto &slot = parts_progress[index];
+
+ current_progress -= slot.current_progress;
+ slot.current_progress = bytesReceived;
+ current_progress += slot.current_progress;
+ total_progress -= slot.total_progress;
+ slot.total_progress = bytesTotal;
+ total_progress += slot.total_progress;
+ emit progress(current_progress, total_progress);
+}
void DownloadJob::start()
{
- qDebug() << "Download JOB started: " << this;
- for(auto iter: downloads)
+ QLOG_INFO() << m_job_name.toLocal8Bit() << " started.";
+ for (auto iter : downloads)
{
- connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+ connect(iter.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+ connect(iter.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
+ connect(iter.get(), SIGNAL(progress(int, qint64, qint64)),
+ SLOT(partProgress(int, qint64, qint64)));
iter->start();
}
}
+
+QStringList DownloadJob::getFailedFiles()
+{
+ QStringList failed;
+ for (auto download : downloads)
+ {
+ if (download->m_status == Job_Failed)
+ {
+ failed.push_back(download->m_url.toString());
+ }
+ }
+ return failed;
+}
diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h
index adeef646..91b014ad 100644
--- a/logic/net/DownloadJob.h
+++ b/logic/net/DownloadJob.h
@@ -1,98 +1,31 @@
#pragma once
#include <QtNetwork>
+#include "Download.h"
+#include "ByteArrayDownload.h"
+#include "FileDownload.h"
+#include "CacheDownload.h"
+#include "HttpMetaCache.h"
+#include "ForgeXzDownload.h"
+#include "logic/tasks/ProgressProvider.h"
class DownloadJob;
-class Download;
-typedef QSharedPointer<DownloadJob> DownloadJobPtr;
-typedef QSharedPointer<Download> DownloadPtr;
-
-enum JobStatus
-{
- Job_NotStarted,
- Job_InProgress,
- Job_Finished,
- Job_Failed
-};
-
-class Job : public QObject
-{
- Q_OBJECT
-protected:
- explicit Job(): QObject(0){};
-public:
- virtual ~Job() {};
-
-public slots:
- virtual void start() = 0;
-};
-
-class Download: public Job
-{
- friend class DownloadJob;
- Q_OBJECT
-protected:
- Download(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString());
-public:
- /// the network reply
- QSharedPointer<QNetworkReply> m_reply;
- /// source URL
- QUrl m_url;
-
- /// if true, check the md5sum against a provided md5sum
- /// also, if a file exists, perform an md5sum first and don't download only if they don't match
- bool m_check_md5;
- /// the expected md5 checksum
- QString m_expected_md5;
-
- /// 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_target_path;
- /// this is the output file, if any
- QFile m_output_file;
- /// if not saving to file, downloaded data is placed here
- QByteArray m_data;
-
- int currentProgress = 0;
- int totalProgress = 0;
-
- /// The file's status
- JobStatus m_status;
-
- int index_within_job = 0;
-signals:
- void started(int index);
- void progress(int index, qint64 current, qint64 total);
- void succeeded(int index);
- void failed(int index);
-public slots:
- virtual void start();
-
-private slots:
- void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);;
- void downloadError(QNetworkReply::NetworkError error);
- void downloadFinished();
- void downloadReadyRead();
-};
+typedef std::shared_ptr<DownloadJob> DownloadJobPtr;
/**
* A single file for the downloader/cache to process.
*/
-class DownloadJob : public Job
+class DownloadJob : public ProgressProvider
{
Q_OBJECT
public:
- explicit DownloadJob()
- :Job(){};
- explicit DownloadJob(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString())
- :Job()
- {
- add(url, rel_target_path, expected_md5);
- };
+ explicit DownloadJob(QString job_name)
+ :ProgressProvider(), m_job_name(job_name){};
+
+ ByteArrayDownloadPtr addByteArrayDownload(QUrl url);
+ FileDownloadPtr addFileDownload(QUrl url, QString rel_target_path);
+ CacheDownloadPtr addCacheDownload(QUrl url, MetaEntryPtr entry);
+ ForgeXzDownloadPtr addForgeXzDownload(QUrl url, MetaEntryPtr entry);
- DownloadPtr add(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString());
DownloadPtr operator[](int index)
{
return downloads[index];
@@ -107,6 +40,20 @@ public:
{
return downloads.size();
}
+ virtual void getProgress(qint64& current, qint64& total)
+ {
+ current = current_progress;
+ total = total_progress;
+ };
+ virtual QString getStatus() const
+ {
+ return m_job_name;
+ };
+ virtual bool isRunning() const
+ {
+ return m_running;
+ };
+ QStringList getFailedFiles();
signals:
void started();
void progress(qint64 current, qint64 total);
@@ -119,8 +66,19 @@ private slots:
void partSucceeded(int index);
void partFailed(int index);
private:
+ struct part_info
+ {
+ qint64 current_progress = 0;
+ qint64 total_progress = 1;
+ int failures = 0;
+ };
+ QString m_job_name;
QList<DownloadPtr> downloads;
+ QList<part_info> parts_progress;
+ qint64 current_progress = 0;
+ qint64 total_progress = 0;
int num_succeeded = 0;
int num_failed = 0;
+ bool m_running = false;
};
diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp
new file mode 100644
index 00000000..3f38b0fa
--- /dev/null
+++ b/logic/net/FileDownload.cpp
@@ -0,0 +1,112 @@
+#include "MultiMC.h"
+#include "FileDownload.h"
+#include <pathutils.h>
+#include <QCryptographicHash>
+#include <logger/QsLog.h>
+
+
+FileDownload::FileDownload ( QUrl url, QString target_path )
+ :Download()
+{
+ m_url = url;
+ m_target_path = target_path;
+ m_check_md5 = false;
+ m_status = Job_NotStarted;
+ m_opened_for_saving = false;
+}
+
+void FileDownload::start()
+{
+ QString filename = m_target_path;
+ m_output_file.setFileName ( filename );
+ // 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 ) )
+ {
+ // 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 )
+ {
+ QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match.";
+ emit succeeded(index_within_job);
+ return;
+ }
+ else
+ {
+ m_expected_md5 = hash;
+ }
+ }
+ if(!ensureFilePathExists(filename))
+ {
+ emit failed(index_within_job);
+ return;
+ }
+
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request ( m_url );
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1());
+ request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Uncached)");
+
+ auto worker = MMC->qnam();
+ QNetworkReply * rep = worker->get ( request );
+
+ m_reply = std::shared_ptr<QNetworkReply> ( rep );
+ connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) );
+ connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) );
+ connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) );
+ connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) );
+}
+
+void FileDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal )
+{
+ emit progress (index_within_job, bytesReceived, bytesTotal );
+}
+
+void FileDownload::downloadError ( QNetworkReply::NetworkError error )
+{
+ // error happened during download.
+ // TODO: log the reason why
+ m_status = Job_Failed;
+}
+
+void FileDownload::downloadFinished()
+{
+ // if the download succeeded
+ if ( m_status != Job_Failed )
+ {
+ // nothing went wrong...
+ m_status = Job_Finished;
+ m_output_file.close();
+
+ m_reply.reset();
+ emit succeeded(index_within_job);
+ return;
+ }
+ // else the download failed
+ else
+ {
+ m_output_file.close();
+ m_reply.reset();
+ emit failed(index_within_job);
+ return;
+ }
+}
+
+void FileDownload::downloadReadyRead()
+{
+ if(!m_opened_for_saving)
+ {
+ if ( !m_output_file.open ( QIODevice::WriteOnly ) )
+ {
+ /*
+ * Can't open the file... the job failed
+ */
+ m_reply->abort();
+ emit failed(index_within_job);
+ return;
+ }
+ m_opened_for_saving = true;
+ }
+ m_output_file.write ( m_reply->readAll() );
+}
diff --git a/logic/net/FileDownload.h b/logic/net/FileDownload.h
new file mode 100644
index 00000000..9abb590d
--- /dev/null
+++ b/logic/net/FileDownload.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Download.h"
+#include <QFile>
+
+class FileDownload : public Download
+{
+ Q_OBJECT
+public:
+ /// if true, check the md5sum against a provided md5sum
+ /// also, if a file exists, perform an md5sum first and don't download only if they don't match
+ bool m_check_md5;
+ /// the expected md5 checksum
+ QString m_expected_md5;
+ /// is the saving file already open?
+ bool m_opened_for_saving;
+ /// if saving to file, use the one specified in this string
+ QString m_target_path;
+ /// this is the output file, if any
+ QFile m_output_file;
+
+public:
+ explicit FileDownload(QUrl url, QString target_path);
+
+protected slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead();
+
+public slots:
+ virtual void start();
+};
+
+typedef std::shared_ptr<FileDownload> FileDownloadPtr;
diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp
new file mode 100644
index 00000000..0e5287d8
--- /dev/null
+++ b/logic/net/ForgeXzDownload.cpp
@@ -0,0 +1,279 @@
+#include "MultiMC.h"
+#include "ForgeXzDownload.h"
+#include <pathutils.h>
+
+#include <QCryptographicHash>
+#include <QFileInfo>
+#include <QDateTime>
+#include <logger/QsLog.h>
+
+ForgeXzDownload::ForgeXzDownload(QUrl url, MetaEntryPtr entry)
+ : Download()
+{
+ QString urlstr = url.toString();
+ urlstr.append(".pack.xz");
+ m_url = QUrl(urlstr);
+ m_entry = entry;
+ m_target_path = entry->getFullPath();
+ m_status = Job_NotStarted;
+ m_opened_for_saving = false;
+}
+
+void ForgeXzDownload::start()
+{
+ if (!m_entry->stale)
+ {
+ emit succeeded(index_within_job);
+ return;
+ }
+ // can we actually create the real, final file?
+ if (!ensureFilePathExists(m_target_path))
+ {
+ emit failed(index_within_job);
+ return;
+ }
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1());
+ request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Cached)");
+
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ emit progress(index_within_job, bytesReceived, bytesTotal);
+}
+
+void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ // TODO: log the reason why
+ m_status = Job_Failed;
+}
+
+void ForgeXzDownload::downloadFinished()
+{
+ // if the download succeeded
+ if (m_status != Job_Failed)
+ {
+ // nothing went wrong...
+ m_status = Job_Finished;
+ if (m_opened_for_saving)
+ {
+ // we actually downloaded something! process and isntall it
+ decompressAndInstall();
+ return;
+ }
+ else
+ {
+ // something bad happened
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ emit failed(index_within_job);
+ return;
+ }
+ }
+ // else the download failed
+ else
+ {
+ m_pack200_xz_file.close();
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ emit failed(index_within_job);
+ return;
+ }
+}
+
+void ForgeXzDownload::downloadReadyRead()
+{
+
+ if (!m_opened_for_saving)
+ {
+ if (!m_pack200_xz_file.open())
+ {
+ /*
+ * Can't open the file... the job failed
+ */
+ m_reply->abort();
+ emit failed(index_within_job);
+ return;
+ }
+ m_opened_for_saving = true;
+ }
+ m_pack200_xz_file.write(m_reply->readAll());
+}
+
+#include "xz.h"
+#include "unpack200.h"
+#include <stdexcept>
+
+const size_t buffer_size = 8196;
+
+void ForgeXzDownload::decompressAndInstall()
+{
+ // rewind the downloaded temp file
+ m_pack200_xz_file.seek(0);
+ // de-xz'd file
+ QTemporaryFile pack200_file;
+ pack200_file.open();
+
+ bool xz_success = false;
+ // first, de-xz
+ {
+ uint8_t in[buffer_size];
+ uint8_t out[buffer_size];
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ xz_crc32_init();
+ xz_crc64_init();
+ s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ if (s == nullptr)
+ {
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+ }
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = 0;
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = buffer_size;
+ while (!xz_success)
+ {
+ if (b.in_pos == b.in_size)
+ {
+ b.in_size = m_pack200_xz_file.read((char*)in, sizeof(in));
+ b.in_pos = 0;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (b.out_pos == sizeof(out))
+ {
+ if (pack200_file.write((char*)out, b.out_pos) != b.out_pos)
+ {
+ // msg = "Write error\n";
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+ }
+
+ b.out_pos = 0;
+ }
+
+ if (ret == XZ_OK)
+ continue;
+
+ if (ret == XZ_UNSUPPORTED_CHECK)
+ {
+ // unsupported check. this is OK, but we should log this
+ continue;
+ }
+
+ if (pack200_file.write((char*)out, b.out_pos) != b.out_pos )
+ {
+ // write error
+ pack200_file.close();
+ xz_dec_end(s);
+ return;
+ }
+
+ switch (ret)
+ {
+ case XZ_STREAM_END:
+ xz_dec_end(s);
+ xz_success = true;
+ break;
+
+ case XZ_MEM_ERROR:
+ QLOG_ERROR() << "Memory allocation failed\n";
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+
+ case XZ_MEMLIMIT_ERROR:
+ QLOG_ERROR() << "Memory usage limit reached\n";
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+
+ case XZ_FORMAT_ERROR:
+ QLOG_ERROR() << "Not a .xz file\n";
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+
+ case XZ_OPTIONS_ERROR:
+ QLOG_ERROR() << "Unsupported options in the .xz headers\n";
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ QLOG_ERROR() << "File is corrupt\n";
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+
+ default:
+ QLOG_ERROR() << "Bug!\n";
+ xz_dec_end(s);
+ emit failed(index_within_job);
+ return;
+ }
+ }
+ }
+
+ // revert pack200
+ pack200_file.close();
+ QString pack_name = pack200_file.fileName();
+ try
+ {
+ unpack_200(pack_name.toStdString(), m_target_path.toStdString());
+ }
+ catch(std::runtime_error & err)
+ {
+ QLOG_ERROR() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what();
+ QFile f(m_target_path);
+ if(f.exists())
+ f.remove();
+ emit failed(index_within_job);
+ return;
+ }
+
+ QFile jar_file(m_target_path);
+
+ if (!jar_file.open(QIODevice::ReadOnly))
+ {
+ jar_file.remove();
+ emit failed(index_within_job);
+ return;
+ }
+ m_entry->md5sum = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5)
+ .toHex()
+ .constData();
+ jar_file.close();
+
+ QFileInfo output_file_info(m_target_path);
+ m_entry->etag = m_reply->rawHeader("ETag").constData();
+ m_entry->local_changed_timestamp =
+ output_file_info.lastModified().toUTC().toMSecsSinceEpoch();
+ m_entry->stale = false;
+ MMC->metacache()->updateEntry(m_entry);
+
+ m_reply.reset();
+ emit succeeded(index_within_job);
+}
diff --git a/logic/net/ForgeXzDownload.h b/logic/net/ForgeXzDownload.h
new file mode 100644
index 00000000..5d677947
--- /dev/null
+++ b/logic/net/ForgeXzDownload.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Download.h"
+#include "HttpMetaCache.h"
+#include <QFile>
+#include <QTemporaryFile>
+
+class ForgeXzDownload : public Download
+{
+ Q_OBJECT
+public:
+ MetaEntryPtr m_entry;
+ /// is the saving file already open?
+ bool m_opened_for_saving;
+ /// if saving to file, use the one specified in this string
+ QString m_target_path;
+ /// this is the output file, if any
+ QTemporaryFile m_pack200_xz_file;
+
+public:
+ explicit ForgeXzDownload(QUrl url, MetaEntryPtr entry);
+
+protected slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead();
+
+public slots:
+ virtual void start();
+private:
+ void decompressAndInstall();
+};
+
+typedef std::shared_ptr<ForgeXzDownload> ForgeXzDownloadPtr;
diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp
index 87741dc9..5ba5b98d 100644
--- a/logic/net/HttpMetaCache.cpp
+++ b/logic/net/HttpMetaCache.cpp
@@ -1,38 +1,130 @@
+#include "MultiMC.h"
#include "HttpMetaCache.h"
#include <pathutils.h>
+
+#include <QFileInfo>
#include <QFile>
-#include <qjsondocument.h>
-#include <qjsonarray.h>
-#include <qjsonobject.h>
-#include <qfileinfo.h>
-#include <qtemporaryfile.h>
-#include <qsavefile.h>
+#include <QTemporaryFile>
+#include <QSaveFile>
+#include <QDateTime>
+#include <QCryptographicHash>
+
+#include <logger/QsLog.h>
+
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QJsonObject>
+
+QString MetaEntry::getFullPath()
+{
+ return PathCombine(MMC->metacache()->getBasePath(base), path);
+}
+
HttpMetaCache::HttpMetaCache(QString path)
+ :QObject()
{
m_index_file = path;
+ saveBatchingTimer.setSingleShot(true);
+ saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer);
+ connect(&saveBatchingTimer,SIGNAL(timeout()),SLOT(SaveNow()));
}
+
HttpMetaCache::~HttpMetaCache()
{
- Save();
+ saveBatchingTimer.stop();
+ SaveNow();
}
-void HttpMetaCache::addEntry ( QString base, QString resource_path, QString etag )
+MetaEntryPtr HttpMetaCache::getEntry ( QString base, QString resource_path )
{
// no base. no base path. can't store
if(!m_entries.contains(base))
- return;
- QString real_path = PathCombine(m_entries[base].base_path, resource_path);
+ {
+ // TODO: log problem
+ return MetaEntryPtr();
+ }
+ EntryMap & map = m_entries[base];
+ if(map.entry_list.contains(resource_path))
+ {
+ return map.entry_list[resource_path];
+ }
+ return MetaEntryPtr();
+}
+
+MetaEntryPtr HttpMetaCache::resolveEntry ( QString base, QString resource_path, QString expected_etag )
+{
+ auto entry = getEntry(base, resource_path);
+ // it's not present? generate a default stale entry
+ if(!entry)
+ {
+ return staleEntry(base, resource_path);
+ }
+
+ auto & selected_base = m_entries[base];
+ QString real_path = PathCombine(selected_base.base_path, resource_path);
QFileInfo finfo(real_path);
- // just ignore it, it's garbage if it's not a proper file
+ // is the file really there? if not -> stale
if(!finfo.isFile() || !finfo.isReadable())
{
- // TODO: log problem
- return;
+ // if the file doesn't exist, we disown the entry
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
+
+ if(!expected_etag.isEmpty() && expected_etag != entry->etag)
+ {
+ // if the etag doesn't match expected, we disown the entry
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
}
- Save();
+ // if the file changed, check md5sum
+ qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch();
+ if(file_last_changed != entry->local_changed_timestamp)
+ {
+ QFile input(real_path);
+ input.open(QIODevice::ReadOnly);
+ QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5).toHex().constData();
+ if(entry->md5sum != md5sum)
+ {
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
+ // md5sums matched... keep entry and save the new state to file
+ entry->local_changed_timestamp = file_last_changed;
+ SaveEventually();
+ }
+
+ // entry passed all the checks we cared about.
+ return entry;
+}
+
+bool HttpMetaCache::updateEntry ( MetaEntryPtr stale_entry )
+{
+ if(!m_entries.contains(stale_entry->base))
+ {
+ QLOG_ERROR() << "Cannot add entry with unknown base: " << stale_entry->base.toLocal8Bit();
+ return false;
+ }
+ if(stale_entry->stale)
+ {
+ QLOG_ERROR() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
+ return false;
+ }
+ m_entries[stale_entry->base].entry_list[stale_entry->path] = stale_entry;
+ SaveEventually();
+ return true;
+}
+
+MetaEntryPtr HttpMetaCache::staleEntry(QString base, QString resource_path)
+{
+ auto foo = new MetaEntry;
+ foo->base = base;
+ foo->path = resource_path;
+ foo->stale = true;
+ return MetaEntryPtr(foo);
}
void HttpMetaCache::addBase ( QString base, QString base_root )
@@ -46,6 +138,16 @@ void HttpMetaCache::addBase ( QString base, QString base_root )
m_entries[base] = foo;
}
+QString HttpMetaCache::getBasePath ( QString base )
+{
+ if(m_entries.contains(base))
+ {
+ return m_entries[base].base_path;
+ }
+ return QString();
+}
+
+
void HttpMetaCache::Load()
{
QFile index(m_index_file);
@@ -65,12 +167,12 @@ void HttpMetaCache::Load()
// read the entry array
auto entries_val =root.value("entries");
- if(!version_val.isArray())
+ if(!entries_val.isArray())
return;
- QJsonArray array = json.array();
+ QJsonArray array = entries_val.toArray();
for(auto element: array)
{
- if(!element.isObject());
+ if(!element.isObject())
return;
auto element_obj = element.toObject();
QString base = element_obj.value("base").toString();
@@ -82,12 +184,22 @@ void HttpMetaCache::Load()
QString path = foo->path = element_obj.value("path").toString();
foo->md5sum = element_obj.value("md5sum").toString();
foo->etag = element_obj.value("etag").toString();
- foo->last_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble();
+ foo->local_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble();
+ foo->remote_changed_timestamp = element_obj.value("remote_changed_timestamp").toString();
+ // presumed innocent until closer examination
+ foo->stale = false;
entrymap.entry_list[path] = MetaEntryPtr( foo );
}
}
-void HttpMetaCache::Save()
+void HttpMetaCache::SaveEventually()
+{
+ // reset the save timer
+ saveBatchingTimer.stop();
+ saveBatchingTimer.start(30000);
+}
+
+void HttpMetaCache::SaveNow()
{
QSaveFile tfile(m_index_file);
if(!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate))
@@ -104,7 +216,9 @@ void HttpMetaCache::Save()
entryObj.insert("path", QJsonValue(entry->path));
entryObj.insert("md5sum", QJsonValue(entry->md5sum));
entryObj.insert("etag", QJsonValue(entry->etag));
- entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->last_changed_timestamp)));
+ entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->local_changed_timestamp)));
+ if(!entry->remote_changed_timestamp.isEmpty())
+ entryObj.insert("remote_changed_timestamp", QJsonValue(entry->remote_changed_timestamp));
entriesArr.append(entryObj);
}
}
@@ -118,14 +232,3 @@ void HttpMetaCache::Save()
return;
tfile.commit();
}
-
-
-MetaEntryPtr HttpMetaCache::getEntryForResource ( QString base, QString resource_path )
-{
- if(!m_entries.contains(base))
- return MetaEntryPtr();
- auto & entrymap = m_entries[base];
- if(!entrymap.entry_list.contains(resource_path))
- return MetaEntryPtr();
- return entrymap.entry_list[resource_path];
-}
diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h
index 161483ad..557d9298 100644
--- a/logic/net/HttpMetaCache.h
+++ b/logic/net/HttpMetaCache.h
@@ -2,6 +2,7 @@
#include <QString>
#include <QSharedPointer>
#include <QMap>
+#include <qtimer.h>
struct MetaEntry
{
@@ -9,23 +10,46 @@ struct MetaEntry
QString path;
QString md5sum;
QString etag;
- quint64 last_changed_timestamp = 0;
+ qint64 local_changed_timestamp = 0;
+ QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time
+ bool stale = true;
+ QString getFullPath();
};
-typedef QSharedPointer<MetaEntry> MetaEntryPtr;
+typedef std::shared_ptr<MetaEntry> MetaEntryPtr;
-class HttpMetaCache
+class HttpMetaCache : public QObject
{
+ Q_OBJECT
public:
// supply path to the cache index file
HttpMetaCache(QString path);
~HttpMetaCache();
- MetaEntryPtr getEntryForResource(QString base, QString resource_path);
- void addEntry(QString base, QString resource_path, QString etag);
+
+ // get the entry solely from the cache
+ // you probably don't want this, unless you have some specific caching needs.
+ MetaEntryPtr getEntry(QString base, QString resource_path);
+
+ // get the entry from cache and verify that it isn't stale (within reason)
+ MetaEntryPtr resolveEntry(QString base, QString resource_path,
+ QString expected_etag = QString());
+
+ // add a previously resolved stale entry
+ bool updateEntry(MetaEntryPtr stale_entry);
+
void addBase(QString base, QString base_root);
-private:
- void Save();
+
+ // (re)start a timer that calls SaveNow later.
+ void SaveEventually();
void Load();
+ QString getBasePath(QString base);
+public
+slots:
+ void SaveNow();
+
+private:
+ // create a new stale entry, given the parameters
+ MetaEntryPtr staleEntry(QString base, QString resource_path);
struct EntryMap
{
QString base_path;
@@ -33,4 +57,5 @@ private:
};
QMap<QString, EntryMap> m_entries;
QString m_index_file;
+ QTimer saveBatchingTimer;
}; \ No newline at end of file
diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp
new file mode 100644
index 00000000..4098783b
--- /dev/null
+++ b/logic/net/LoginTask.cpp
@@ -0,0 +1,285 @@
+/* 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 "LoginTask.h"
+#include "MultiMC.h"
+#include <settingsobject.h>
+
+#include <QStringList>
+
+#include <QNetworkReply>
+#include <QNetworkRequest>
+
+#include <QUrl>
+#include <QUrlQuery>
+#include <QJsonParseError>
+#include <QJsonObject>
+
+LoginTask::LoginTask(const UserInfo &uInfo, QObject *parent) : Task(parent), uInfo(uInfo)
+{
+}
+
+void LoginTask::executeTask()
+{
+ yggdrasilLogin();
+}
+
+void LoginTask::legacyLogin()
+{
+ setStatus(tr("Logging in..."));
+ auto worker = MMC->qnam();
+ connect(worker.get(), SIGNAL(finished(QNetworkReply *)), this,
+ SLOT(processLegacyReply(QNetworkReply *)));
+
+ QUrl loginURL("https://login.minecraft.net/");
+ QNetworkRequest netRequest(loginURL);
+ netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
+ "application/x-www-form-urlencoded");
+
+ QUrlQuery params;
+ params.addQueryItem("user", uInfo.username);
+ params.addQueryItem("password", uInfo.password);
+ params.addQueryItem("version", "13");
+
+ netReply = worker->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8());
+}
+
+void LoginTask::parseLegacyReply(QByteArray data)
+{
+ QString responseStr = QString::fromUtf8(data);
+
+ QStringList strings = responseStr.split(":");
+ if (strings.count() >= 4)
+ {
+ // strings[1] is the download ticket. It isn't used anymore.
+ QString username = strings[2];
+ QString sessionID = strings[3];
+ /*
+ struct LoginResponse
+ {
+ QString username;
+ QString session_id;
+ QString player_name;
+ QString player_id;
+ QString client_id;
+ };
+ */
+ result = {username, sessionID, username, QString()};
+ emitSucceeded();
+ }
+ else
+ {
+ if (responseStr.toLower() == "bad login")
+ emitFailed(tr("Invalid username or password."));
+ else if (responseStr.toLower() == "old version")
+ emitFailed(tr("Launcher outdated, please update."));
+ else
+ emitFailed(tr("Login failed: %1").arg(responseStr));
+ }
+}
+
+void LoginTask::processLegacyReply(QNetworkReply *reply)
+{
+ processReply(reply, &LoginTask::parseLegacyReply, &LoginTask::parseLegacyError);
+}
+
+void LoginTask::processYggdrasilReply(QNetworkReply *reply)
+{
+ processReply(reply, &LoginTask::parseYggdrasilReply, &LoginTask::parseYggdrasilError);
+}
+
+void LoginTask::processReply(QNetworkReply *reply, std::function<void (LoginTask*, QByteArray)> parser, std::function<QString (LoginTask*, QNetworkReply*)> errorHandler)
+{
+ if (netReply != reply)
+ return;
+ // Check for errors.
+ switch (reply->error())
+ {
+ case QNetworkReply::NoError:
+ {
+ // Check the response code.
+ int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ switch (responseCode)
+ {
+ case 200:
+ parser(this, reply->readAll());
+ break;
+
+ default:
+ emitFailed(tr("Login failed: Unknown HTTP code %1 encountered.").arg(responseCode));
+ break;
+ }
+
+ break;
+ }
+
+ case QNetworkReply::OperationCanceledError:
+ emitFailed(tr("Login canceled."));
+ break;
+
+ default:
+ emitFailed(errorHandler(this, reply));
+ break;
+ }
+}
+
+QString LoginTask::parseLegacyError(QNetworkReply *reply)
+{
+ int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ switch (responseCode)
+ {
+ case 403:
+ return tr("Invalid username or password.");
+
+ case 503:
+ return tr("The login servers are currently unavailable. Check "
+ "http://help.mojang.com/ for more info.");
+
+ default:
+ QLOG_DEBUG() << "Login failed with QNetworkReply code:" << reply->error();
+ return tr("Login failed: %1").arg(reply->errorString());
+ }
+}
+
+QString LoginTask::parseYggdrasilError(QNetworkReply *reply)
+{
+ QByteArray data = reply->readAll();
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ // If there are JSON errors fall back to using the legacy error handling using HTTP status codes
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ return parseLegacyError(reply);
+ }
+
+ if (!jsonDoc.isObject())
+ {
+ return parseLegacyError(reply);
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ //QString error = root.value("error").toString();
+ QString errorMessage = root.value("errorMessage").toString();
+
+ if(errorMessage.isEmpty())
+ {
+ return parseLegacyError(reply);
+ }
+
+ return tr("Login failed: ") + errorMessage;
+}
+
+void LoginTask::yggdrasilLogin()
+{
+ setStatus(tr("Logging in..."));
+ auto worker = MMC->qnam();
+ connect(worker.get(), SIGNAL(finished(QNetworkReply *)), this,
+ SLOT(processYggdrasilReply(QNetworkReply *)));
+
+ /*
+ {
+ // agent def. version might be incremented at some point
+ "agent":{"name":"Minecraft","version":1},
+ "username": "mojang account name",
+ "password": "mojang account password",
+ // client token is optional. but we supply one anyway
+ "clientToken": "client identifier"
+ }
+ */
+
+ QUrl loginURL("https://authserver.mojang.com/authenticate");
+ QNetworkRequest netRequest(loginURL);
+ netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ auto settings = MMC->settings();
+ QString clientToken = settings->get("YggdrasilClientToken").toString();
+ // escape the {}
+ clientToken.remove('{');
+ clientToken.remove('}');
+ // create the request
+ QString requestConstent;
+ requestConstent += "{";
+ requestConstent += " \"agent\":{\"name\":\"Minecraft\",\"version\":1},\n";
+ requestConstent += " \"username\":\"" + uInfo.username + "\",\n";
+ requestConstent += " \"password\":\"" + uInfo.password + "\",\n";
+ requestConstent += " \"clientToken\":\"" + clientToken + "\"\n";
+ requestConstent += "}";
+ netReply = worker->post(netRequest, requestConstent.toUtf8());
+}
+
+/*
+{
+ "accessToken": "random access token", // hexadecimal
+ "clientToken": "client identifier", // identical to the one received
+ "availableProfiles": [ // only present if the agent field was received
+ {
+ "id": "profile identifier", // hexadecimal
+ "name": "player name"
+ }
+ ],
+ "selectedProfile": { // only present if the agent field was received
+ "id": "profile identifier",
+ "name": "player name"
+ }
+}
+*/
+void LoginTask::parseYggdrasilReply(QByteArray data)
+{
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ emitFailed(tr("Login failed: %1").arg(jsonError.errorString()));
+ return;
+ }
+
+ if (!jsonDoc.isObject())
+ {
+ emitFailed(tr("Login failed: BAD FORMAT #1"));
+ return;
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ QString accessToken = root.value("accessToken").toString();
+ QString clientToken = root.value("clientToken").toString();
+ QString playerID;
+ QString playerName;
+ auto selectedProfile = root.value("selectedProfile");
+ if(selectedProfile.isObject())
+ {
+ auto selectedProfileO = selectedProfile.toObject();
+ playerID = selectedProfileO.value("id").toString();
+ playerName = selectedProfileO.value("name").toString();
+ }
+ QString sessionID = "token:" + accessToken + ":" + playerID;
+ /*
+ struct LoginResponse
+ {
+ QString username;
+ QString session_id;
+ QString player_name;
+ QString player_id;
+ QString client_id;
+ };
+ */
+
+ result = {uInfo.username, sessionID, playerName, playerID, accessToken};
+ emitSucceeded();
+}
diff --git a/logic/tasks/LoginTask.h b/logic/net/LoginTask.h
index bde672b8..aa925999 100644
--- a/logic/tasks/LoginTask.h
+++ b/logic/net/LoginTask.h
@@ -3,7 +3,7 @@
* 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
@@ -13,10 +13,9 @@
* limitations under the License.
*/
-#ifndef LOGINTASK_H
-#define LOGINTASK_H
+#pragma once
-#include "Task.h"
+#include "logic/tasks/Task.h"
#include <QSharedPointer>
struct UserInfo
@@ -28,8 +27,10 @@ struct UserInfo
struct LoginResponse
{
QString username;
- QString sessionID;
- qint64 latestVersion;
+ QString session_id; // session id is a combination of player id and the access token
+ QString player_name;
+ QString player_id;
+ QString access_token;
};
class QNetworkReply;
@@ -38,21 +39,29 @@ class LoginTask : public Task
{
Q_OBJECT
public:
- explicit LoginTask(const UserInfo& uInfo, QObject *parent = 0);
+ explicit LoginTask(const UserInfo &uInfo, QObject *parent = 0);
LoginResponse getResult()
{
return result;
- };
-
+ }
+
protected slots:
- void processNetReply(QNetworkReply* reply);
-
+ void legacyLogin();
+ void processLegacyReply(QNetworkReply *reply);
+ void parseLegacyReply(QByteArray data);
+ QString parseLegacyError(QNetworkReply *reply);
+
+ void yggdrasilLogin();
+ void processYggdrasilReply(QNetworkReply *reply);
+ void parseYggdrasilReply(QByteArray data);
+ QString parseYggdrasilError(QNetworkReply *reply);
+
+ void processReply(QNetworkReply *reply, std::function<void(LoginTask*, QByteArray)>, std::function<QString(LoginTask*, QNetworkReply*)>);
+
protected:
void executeTask();
-
+
LoginResponse result;
- QNetworkReply* netReply;
+ QNetworkReply *netReply;
UserInfo uInfo;
};
-
-#endif // LOGINTASK_H
diff --git a/logic/tasks/LoginTask.cpp b/logic/tasks/LoginTask.cpp
deleted file mode 100644
index ad9de7f5..00000000
--- a/logic/tasks/LoginTask.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/* 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 "LoginTask.h"
-#include "MultiMC.h"
-
-#include <QStringList>
-
-#include <QNetworkReply>
-#include <QNetworkRequest>
-
-#include <QUrl>
-#include <QUrlQuery>
-
-LoginTask::LoginTask( const UserInfo& uInfo, QObject* parent ) : Task(parent), uInfo(uInfo){}
-
-void LoginTask::executeTask()
-{
- setStatus("Logging in...");
- auto worker = MMC->qnam();
- connect(worker, SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*)));
-
- QUrl loginURL("https://login.minecraft.net/");
- QNetworkRequest netRequest(loginURL);
- netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
-
- QUrlQuery params;
- params.addQueryItem("user", uInfo.username);
- params.addQueryItem("password", uInfo.password);
- params.addQueryItem("version", "13");
-
- netReply = worker->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8());
-}
-
-void LoginTask::processNetReply(QNetworkReply *reply)
-{
- if(netReply != reply)
- return;
- // Check for errors.
- switch (reply->error())
- {
- case QNetworkReply::NoError:
- {
- // Check the response code.
- int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- if (responseCode == 200)
- {
- QString responseStr(reply->readAll());
-
- QStringList strings = responseStr.split(":");
- if (strings.count() >= 4)
- {
- bool parseSuccess;
- qint64 latestVersion = strings[0].toLongLong(&parseSuccess);
- if (parseSuccess)
- {
- // strings[1] is the download ticket. It isn't used anymore.
- QString username = strings[2];
- QString sessionID = strings[3];
-
- result = {username, sessionID, latestVersion};
- emitSucceeded();
- }
- else
- {
- emitFailed("Failed to parse Minecraft version string.");
- }
- }
- else
- {
- if (responseStr.toLower() == "bad login")
- emitFailed("Invalid username or password.");
- else if (responseStr.toLower() == "old version")
- emitFailed("Launcher outdated, please update.");
- else
- emitFailed("Login failed: " + responseStr);
- }
- }
- else if (responseCode == 503)
- {
- emitFailed("The login servers are currently unavailable. Check http://help.mojang.com/ for more info.");
- }
- else
- {
- emitFailed(QString("Login failed: Unknown HTTP error %1 occurred.").arg(QString::number(responseCode)));
- }
- break;
- }
-
- case QNetworkReply::OperationCanceledError:
- emitFailed("Login canceled.");
- break;
-
- default:
- emitFailed("Login failed: " + reply->errorString());
- break;
- }
-}
diff --git a/logic/tasks/ProgressProvider.h b/logic/tasks/ProgressProvider.h
new file mode 100644
index 00000000..e158eb54
--- /dev/null
+++ b/logic/tasks/ProgressProvider.h
@@ -0,0 +1,20 @@
+#pragma once
+#include <QObject>
+class ProgressProvider : public QObject
+{
+ Q_OBJECT
+protected:
+ explicit ProgressProvider(QObject* parent = 0): QObject(parent){}
+signals:
+ void started();
+ void progress(qint64 current, qint64 total);
+ void succeeded();
+ void failed(QString reason);
+ void status(QString status);
+public:
+ virtual QString getStatus() const = 0;
+ virtual void getProgress(qint64 &current, qint64 &total) = 0;
+ virtual bool isRunning() const = 0;
+public slots:
+ virtual void start() = 0;
+};
diff --git a/logic/tasks/Task.cpp b/logic/tasks/Task.cpp
index 7c148591..2f137c55 100644
--- a/logic/tasks/Task.cpp
+++ b/logic/tasks/Task.cpp
@@ -14,72 +14,60 @@
*/
#include "Task.h"
+#include <logger/QsLog.h>
Task::Task(QObject *parent) :
- QObject(parent)
+ ProgressProvider(parent)
{
}
QString Task::getStatus() const
{
- return status;
+ return m_status;
}
-void Task::setStatus(const QString &status)
+void Task::setStatus(const QString &new_status)
{
- this->status = status;
- emitStatusChange(status);
+ m_status = new_status;
+ emit status(new_status);
}
-int Task::getProgress() const
+void Task::setProgress(int new_progress)
{
- return progress;
+ m_progress = new_progress;
+ emit progress(new_progress, 100);
}
-void Task::setProgress(int progress)
+void Task::getProgress(qint64& current, qint64& total)
{
- this->progress = progress;
- emitProgressChange(progress);
+ current = m_progress;
+ total = 100;
}
-void Task::startTask()
-{
- emitStarted();
- executeTask();
-}
-void Task::emitStarted()
+void Task::start()
{
- running = true;
+ m_running = true;
emit started();
+ executeTask();
}
void Task::emitFailed(QString reason)
{
- running = false;
+ m_running = false;
+ QLOG_ERROR() << "Task failed: " << reason;
emit failed(reason);
}
void Task::emitSucceeded()
{
- running = false;
+ m_running = false;
emit succeeded();
}
bool Task::isRunning() const
{
- return running;
-}
-
-
-void Task::emitStatusChange(const QString &status)
-{
- emit statusChanged(status);
-}
-
-void Task::emitProgressChange(int progress)
-{
- emit progressChanged(progress);
+ return m_running;
}
diff --git a/logic/tasks/Task.h b/logic/tasks/Task.h
index 91852b0f..cfe71c51 100644
--- a/logic/tasks/Task.h
+++ b/logic/tasks/Task.h
@@ -13,53 +13,37 @@
* limitations under the License.
*/
-#ifndef TASK_H
-#define TASK_H
+#pragma once
#include <QObject>
#include <QString>
+#include "ProgressProvider.h"
-class Task : public QObject
+class Task : public ProgressProvider
{
Q_OBJECT
public:
explicit Task(QObject *parent = 0);
- QString getStatus() const;
- int getProgress() const;
- bool isRunning() const;
+ virtual QString getStatus() const;
+ virtual void getProgress(qint64& current, qint64& total);
+ virtual bool isRunning() const;
public slots:
- void startTask();
-
-protected slots:
- void setStatus(const QString& status);
- void setProgress(int progress);
-
-signals:
- void started();
- void failed(QString reason);
- void succeeded();
-
- void statusChanged(Task* task, const QString& status);
- void progressChanged(Task* task, int progress);
-
- void statusChanged(const QString& status);
- void progressChanged(int progress);
+ virtual void start();
protected:
virtual void executeTask() = 0;
- virtual void emitStarted();
- virtual void emitFailed(QString reason);
virtual void emitSucceeded();
+ virtual void emitFailed(QString reason);
+
+protected slots:
+ void setStatus(const QString& status);
+ void setProgress(int progress);
- virtual void emitStatusChange(const QString &status);
- virtual void emitProgressChange(int progress);
-
- QString status;
- int progress;
- bool running = false;
+protected:
+ QString m_status;
+ int m_progress = 0;
+ bool m_running = false;
};
-
-#endif // TASK_H