summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew <forkk@forkk.net>2013-03-08 13:56:26 -0600
committerAndrew <forkk@forkk.net>2013-03-08 13:56:26 -0600
commit2d6e785e4ee8a8b837fe84f83972ec23a09564af (patch)
tree6afdac6e06f1ba8ae5c29f62b5a4a9bc1b696b12
parent69040f923b0344de214d6f1fc4553d223df7d2d6 (diff)
downloadMultiMC-2d6e785e4ee8a8b837fe84f83972ec23a09564af.tar
MultiMC-2d6e785e4ee8a8b837fe84f83972ec23a09564af.tar.gz
MultiMC-2d6e785e4ee8a8b837fe84f83972ec23a09564af.tar.lz
MultiMC-2d6e785e4ee8a8b837fe84f83972ec23a09564af.tar.xz
MultiMC-2d6e785e4ee8a8b837fe84f83972ec23a09564af.zip
Implemented version lists.
-rw-r--r--libmultimc/include/instance.h10
-rw-r--r--libmultimc/include/instancetypeinterface.h10
-rw-r--r--libmultimc/include/instversion.h33
-rw-r--r--libmultimc/include/instversionlist.h37
-rw-r--r--libmultimc/include/task.h9
-rw-r--r--libmultimc/src/instversionlist.cpp17
-rw-r--r--libmultimc/src/task.cpp5
-rw-r--r--plugins/stdinstance/CMakeLists.txt7
-rw-r--r--plugins/stdinstance/stdinstance.cpp7
-rw-r--r--plugins/stdinstance/stdinstance.h2
-rw-r--r--plugins/stdinstance/stdinstancetype.cpp6
-rw-r--r--plugins/stdinstance/stdinstancetype.h3
-rw-r--r--plugins/stdinstance/stdinstversion.cpp147
-rw-r--r--plugins/stdinstance/stdinstversion.h82
-rw-r--r--plugins/stdinstance/stdinstversionlist.cpp509
-rw-r--r--plugins/stdinstance/stdinstversionlist.h99
16 files changed, 955 insertions, 28 deletions
diff --git a/libmultimc/include/instance.h b/libmultimc/include/instance.h
index aaefbd6e..41e664ff 100644
--- a/libmultimc/include/instance.h
+++ b/libmultimc/include/instance.h
@@ -23,6 +23,7 @@
#include "inifile.h"
#include "instancetypeinterface.h"
+#include "instversionlist.h"
#include "libmmc_config.h"
@@ -253,6 +254,15 @@ public:
+ //////// LISTS, LISTS, AND MORE LISTS ////////
+ /*!
+ * \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 = 0;
+
+
+
//////// INSTANCE TYPE STUFF ////////
/*!
diff --git a/libmultimc/include/instancetypeinterface.h b/libmultimc/include/instancetypeinterface.h
index 30a12d99..ba13f820 100644
--- a/libmultimc/include/instancetypeinterface.h
+++ b/libmultimc/include/instancetypeinterface.h
@@ -20,6 +20,8 @@
#include "instanceloader.h"
+class InstVersionList;
+
//! The InstanceTypeInterface's interface ID.
#define InstanceTypeInterface_IID "net.forkk.MultiMC.InstanceTypeInterface/0.1"
@@ -56,7 +58,13 @@ public:
* \brief Gets a longer, more detailed description of this instance type.
* \return The instance type's description.
*/
- virtual QString description() const = 0;
+ virtual QString description() const = 0;
+
+ /*!
+ * \brief Gets the version list for this instance type.
+ * \return A pointer to this instance type's version list.
+ */
+ virtual InstVersionList *versionList() const = 0;
protected:
/*!
diff --git a/libmultimc/include/instversion.h b/libmultimc/include/instversion.h
index 3c6b7ac9..7de83966 100644
--- a/libmultimc/include/instversion.h
+++ b/libmultimc/include/instversion.h
@@ -26,27 +26,32 @@ class LIBMULTIMC_EXPORT InstVersion : public QObject
{
Q_OBJECT
public:
- // Constructs a new InstVersion with the given parent. The parent *must*
- // be the InstVersionList that contains this InstVersion. The InstVersion
- // should be added to the list immediately after being created as any calls
- // to id() will likely fail unless the InstVersion is in a list.
+ /*!
+ * \brief Constructs a new InstVersion with the given parent.
+ * The parent *must* be the InstVersionList that contains this InstVersion.
+ * The InstVersion should be added to the list immediately after being created.
+ */
explicit InstVersion(InstVersionList *parent = 0);
- // Returns this InstVersion's ID. This is usually just the InstVersion's index
- // within its InstVersionList, but not always.
- // If this InstVersion is not in an InstVersionList, returns -1.
- virtual int id() const = 0;
+ //! Gets the string used to identify this version in config files.
+ virtual QString descriptor() const = 0;
+
+ /*!
+ * \breif Returns this InstVersion's name.
+ * This is displayed to the user in the GUI and is usually just the version number ("1.4.7"), for example.
+ */
- // Returns this InstVersion's name. This is displayed to the user in the GUI
- // and is usually just the version number ("1.4.7"), for example.
virtual QString name() const = 0;
- // Returns this InstVersion's name. This is usually displayed to the user
- // in the GUI and specifies what kind of version this is. For example: it
- // could be "Snapshot", "Latest Version", "MCNostalgia", etc.
+ /*!
+ * \brief Returns this InstVersion's name.
+ * This is usually displayed to the user in the GUI and specifies what
+ * kind of version this is. For example: it could be "Snapshot",
+ * "Latest Version", "MCNostalgia", etc.
+ */
virtual QString type() const = 0;
- // Returns the version list that this InstVersion is a part of.
+ //! Returns the version list that this InstVersion is a part of.
virtual InstVersionList *versionList() const;
};
diff --git a/libmultimc/include/instversionlist.h b/libmultimc/include/instversionlist.h
index d64a286f..4345aaaa 100644
--- a/libmultimc/include/instversionlist.h
+++ b/libmultimc/include/instversionlist.h
@@ -21,25 +21,44 @@
#include "libmmc_config.h"
class InstVersion;
+class Task;
-// Class that each instance type's version list derives from. Version lists are
-// the lists that keep track of the available game versions for that instance.
-// This list will not be loaded on startup. It will be loaded when the list's
-// load function is called.
+/*!
+ * \brief Class that each instance type's version list derives from.
+ * Version lists are the lists that keep track of the available game versions
+ * for that instance. This list will not be loaded on startup. It will be loaded
+ * when the list's load function is called. Before using the version list, you
+ * should check to see if it has been loaded yet and if not, load the list.
+ */
class LIBMULTIMC_EXPORT InstVersionList : public QObject
{
Q_OBJECT
public:
- explicit InstVersionList();
+ explicit InstVersionList(QObject *parent = 0);
+
+ /*!
+ * \brief Gets a task that will reload the version list.
+ * Simply execute the task to load the list.
+ * \return A pointer to a task that reloads the version list.
+ */
+ virtual Task *getLoadTask() = 0;
- // Reloads the version list.
- virtual void loadVersionList() = 0;
+ //! Checks whether or not the list is loaded. If this returns false, the list should be loaded.
+ virtual bool isLoaded() = 0;
- // Gets the version at the given index.
+ //! Gets the version at the given index.
virtual const InstVersion *at(int i) const = 0;
- // Returns the number of versions in the list.
+ //! Returns the number of versions in the list.
virtual int count() const = 0;
+
+ /*!
+ * \brief Finds a version by its descriptor.
+ * \param The descriptor of the version to find.
+ * \return A const pointer to the version with the given descriptor. NULL if
+ * one doesn't exist.
+ */
+ virtual const InstVersion *findVersion(const QString &descriptor);
};
#endif // INSTVERSIONLIST_H
diff --git a/libmultimc/include/task.h b/libmultimc/include/task.h
index b1a3052d..fc5b1d25 100644
--- a/libmultimc/include/task.h
+++ b/libmultimc/include/task.h
@@ -34,6 +34,15 @@ public:
QString getStatus() const;
int getProgress() const;
+ /*!
+ * \brief Calculates and sets the task's progress based on the number of parts completed out of the total number to complete.
+ * This is essentially just shorthand for setProgress((parts / whole) * 100);
+ * \param parts The parts out of the whole completed. This parameter should
+ * be less than whole. If it is greater than whole, progress is set to 100.
+ * \param whole The total number of things that need to be completed.
+ */
+ void calcProgress(int parts, int whole);
+
public slots:
void setStatus(const QString& status);
void setProgress(int progress);
diff --git a/libmultimc/src/instversionlist.cpp b/libmultimc/src/instversionlist.cpp
index e171cfa5..301b9969 100644
--- a/libmultimc/src/instversionlist.cpp
+++ b/libmultimc/src/instversionlist.cpp
@@ -13,9 +13,20 @@
* limitations under the License.
*/
-#include "include/instversionlist.h"
+#include "instversionlist.h"
+#include "instversion.h"
-InstVersionList::InstVersionList() :
- QObject(NULL)
+InstVersionList::InstVersionList(QObject *parent) :
+ QObject(parent)
{
}
+
+const InstVersion *InstVersionList::findVersion(const QString &descriptor)
+{
+ for (int i = 0; i < count(); i++)
+ {
+ if (at(i)->descriptor() == descriptor)
+ return at(i);
+ }
+ return NULL;
+}
diff --git a/libmultimc/src/task.cpp b/libmultimc/src/task.cpp
index d581a1dd..3e30827b 100644
--- a/libmultimc/src/task.cpp
+++ b/libmultimc/src/task.cpp
@@ -37,6 +37,11 @@ int Task::getProgress() const
return progress;
}
+void Task::calcProgress(int parts, int whole)
+{
+ setProgress((int)((((float)parts) / ((float)whole))*100)); // Not sure if C++ or LISP...
+}
+
void Task::setProgress(int progress)
{
this->progress = progress;
diff --git a/plugins/stdinstance/CMakeLists.txt b/plugins/stdinstance/CMakeLists.txt
index 7b1ff192..deebf812 100644
--- a/plugins/stdinstance/CMakeLists.txt
+++ b/plugins/stdinstance/CMakeLists.txt
@@ -5,6 +5,7 @@ ADD_DEFINITIONS(-DQT_PLUGIN)
# Find Qt
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
+find_package(Qt5Xml REQUIRED)
# Include Qt headers.
include_directories(${Qt5Base_INCLUDE_DIRS})
@@ -25,11 +26,15 @@ include_directories(${CMAKE_SOURCE_DIR}libinstance/include)
SET(STDINST_HEADERS
stdinstancetype.h
stdinstance.h
+stdinstversionlist.h
+stdinstversion.h
)
SET(STDINST_SOURCES
stdinstancetype.cpp
stdinstance.cpp
+stdinstversionlist.cpp
+stdinstversion.cpp
)
add_library(stdinstance SHARED ${STDINST_SOURCES} ${STDINST_HEADERS})
@@ -41,7 +46,7 @@ IF(UNIX)
set_target_properties(stdinstance PROPERTIES LIBRARY_OUTPUT_DIRECTORY "..")
ENDIF()
-qt5_use_modules(stdinstance Core Network)
+qt5_use_modules(stdinstance Core Network Xml)
target_link_libraries(stdinstance
quazip
patchlib
diff --git a/plugins/stdinstance/stdinstance.cpp b/plugins/stdinstance/stdinstance.cpp
index 077b6b15..e5d87d23 100644
--- a/plugins/stdinstance/stdinstance.cpp
+++ b/plugins/stdinstance/stdinstance.cpp
@@ -21,6 +21,8 @@
#include <javautils.h>
+#include "stdinstversionlist.h"
+
StdInstance::StdInstance(const QString &rootDir, const InstanceTypeInterface *iType, QObject *parent) :
Instance(rootDir, parent)
{
@@ -61,3 +63,8 @@ const InstanceTypeInterface *StdInstance::instanceType() const
{
return m_instType;
}
+
+InstVersionList *StdInstance::versionList() const
+{
+ return &vList;
+}
diff --git a/plugins/stdinstance/stdinstance.h b/plugins/stdinstance/stdinstance.h
index 9c4b6fd6..2058453b 100644
--- a/plugins/stdinstance/stdinstance.h
+++ b/plugins/stdinstance/stdinstance.h
@@ -30,6 +30,8 @@ public:
virtual const InstanceTypeInterface *instanceType() const;
+ virtual InstVersionList *versionList() const;
+
////// TIMESTAMPS //////
virtual qint64 lastVersionUpdate() { return settings().get("lastVersionUpdate").value<qint64>(); }
virtual void setLastVersionUpdate(qint64 val) { settings().set("lastVersionUpdate", val); }
diff --git a/plugins/stdinstance/stdinstancetype.cpp b/plugins/stdinstance/stdinstancetype.cpp
index 93c5fd16..8ad7fd40 100644
--- a/plugins/stdinstance/stdinstancetype.cpp
+++ b/plugins/stdinstance/stdinstancetype.cpp
@@ -19,6 +19,7 @@
#include <QFileInfo>
#include "stdinstance.h"
+#include "stdinstversionlist.h"
StdInstanceType::StdInstanceType(QObject *parent) :
QObject(parent)
@@ -26,6 +27,11 @@ StdInstanceType::StdInstanceType(QObject *parent) :
}
+InstVersionList *StdInstanceType::versionList() const
+{
+ return &vList;
+}
+
InstanceLoader::InstTypeError StdInstanceType::createInstance(Instance *&inst,
const QString &instDir) const
{
diff --git a/plugins/stdinstance/stdinstancetype.h b/plugins/stdinstance/stdinstancetype.h
index b8382a97..4840ab51 100644
--- a/plugins/stdinstance/stdinstancetype.h
+++ b/plugins/stdinstance/stdinstancetype.h
@@ -34,6 +34,9 @@ public:
virtual QString description() const { return "A standard Minecraft instance."; }
+ virtual InstVersionList *versionList() const;
+
+protected:
virtual InstanceLoader::InstTypeError createInstance(Instance *&inst, const QString &instDir) const;
virtual InstanceLoader::InstTypeError loadInstance(Instance *&inst, const QString &instDir) const;
diff --git a/plugins/stdinstance/stdinstversion.cpp b/plugins/stdinstance/stdinstversion.cpp
new file mode 100644
index 00000000..0e582ffc
--- /dev/null
+++ b/plugins/stdinstance/stdinstversion.cpp
@@ -0,0 +1,147 @@
+/* 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 "stdinstversion.h"
+
+StdInstVersion::StdInstVersion(QString descriptor,
+ QString name,
+ qint64 timestamp,
+ QString dlUrl,
+ bool hasLWJGL,
+ QString etag,
+ InstVersionList *parent) :
+ InstVersion(parent), m_descriptor(descriptor), m_name(name), m_timestamp(timestamp),
+ m_dlUrl(dlUrl), m_hasLWJGL(hasLWJGL), m_etag(etag)
+{
+ m_linkedVersion = NULL;
+}
+
+StdInstVersion::StdInstVersion(StdInstVersion *linkedVersion)
+{
+ m_linkedVersion = linkedVersion;
+}
+
+StdInstVersion::StdInstVersion()
+{
+ m_timestamp = 0;
+ m_hasLWJGL = false;
+ m_linkedVersion = NULL;
+}
+
+StdInstVersion *StdInstVersion::mcnVersion(QString rawName, QString niceName)
+{
+ StdInstVersion *version = new StdInstVersion;
+ version->m_descriptor = rawName;
+ version->m_name = niceName;
+ version->setVersionType(MCNostalgia);
+ return version;
+}
+
+QString StdInstVersion::descriptor() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->descriptor();
+ return m_descriptor;
+}
+
+QString StdInstVersion::name() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->name();
+ return m_name;
+}
+
+QString StdInstVersion::type() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->type();
+
+ switch (versionType())
+ {
+ case OldSnapshot:
+ return "Old Snapshot";
+
+ case Stable:
+ return "Stable";
+
+ case CurrentStable:
+ return "Current Stable";
+
+ case Snapshot:
+ return "Snapshot";
+
+ case MCNostalgia:
+ return "MCNostalgia";
+
+ case MetaCustom:
+ // Not really sure what this does, but it was in the code for v4,
+ // so it must be important... Right?
+ return "Custom Meta Version";
+
+ case MetaLatestSnapshot:
+ return "Latest Snapshot";
+
+ case MetaLatestStable:
+ return "Latest Stable";
+
+ default:
+ return QString("Unknown Type %1").arg(versionType());
+ }
+}
+
+qint64 StdInstVersion::timestamp() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->timestamp();
+ return m_timestamp;
+}
+
+QString StdInstVersion::downloadURL() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->downloadURL();
+ return m_dlUrl;
+}
+
+bool StdInstVersion::hasLWJGL() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->hasLWJGL();
+ return m_hasLWJGL;
+}
+
+QString StdInstVersion::etag() const
+{
+ if (m_linkedVersion)
+ return m_linkedVersion->etag();
+ return m_etag;
+}
+
+StdInstVersion::VersionType StdInstVersion::versionType() const
+{
+ return m_type;
+}
+
+void StdInstVersion::setVersionType(StdInstVersion::VersionType type)
+{
+ m_type = type;
+}
+
+bool StdInstVersion::isMeta() const
+{
+ return versionType() == MetaCustom ||
+ versionType() == MetaLatestSnapshot ||
+ versionType() == MetaLatestStable;
+}
diff --git a/plugins/stdinstance/stdinstversion.h b/plugins/stdinstance/stdinstversion.h
new file mode 100644
index 00000000..3f03ae83
--- /dev/null
+++ b/plugins/stdinstance/stdinstversion.h
@@ -0,0 +1,82 @@
+/* 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.
+ */
+
+#ifndef STDINSTVERSION_H
+#define STDINSTVERSION_H
+
+#include "instversion.h"
+
+/*!
+ * \brief Implements a standard Minecraft instance version.
+ * This class also supports meta versions (i.e. versions that are essentially
+ * aliases of other versions).
+ */
+class StdInstVersion : public InstVersion
+{
+ Q_OBJECT
+public:
+ explicit StdInstVersion(QString descriptor,
+ QString name,
+ qint64 timestamp,
+ QString dlUrl,
+ bool hasLWJGL,
+ QString etag,
+ InstVersionList *parent);
+
+ explicit StdInstVersion(StdInstVersion *linkedVersion);
+
+ StdInstVersion();
+
+ static StdInstVersion *mcnVersion(QString rawName, QString niceName);
+
+ enum VersionType
+ {
+ OldSnapshot,
+ Stable,
+ CurrentStable,
+ Snapshot,
+ MCNostalgia,
+ MetaCustom,
+ MetaLatestSnapshot,
+ MetaLatestStable
+ };
+
+ virtual QString descriptor() const;
+ virtual QString name() const;
+ virtual QString type() const;
+ virtual qint64 timestamp() const;
+ virtual QString downloadURL() const;
+ virtual bool hasLWJGL() const;
+ virtual QString etag() const;
+
+ virtual VersionType versionType() const;
+ virtual void setVersionType(VersionType type);
+
+ virtual bool isMeta() const;
+
+
+protected:
+ QString m_descriptor;
+ QString m_name;
+ qint64 m_timestamp;
+ QString m_dlUrl;
+ bool m_hasLWJGL;
+ QString m_etag;
+ VersionType m_type;
+
+ StdInstVersion *m_linkedVersion;
+};
+
+#endif // STDINSTVERSION_H
diff --git a/plugins/stdinstance/stdinstversionlist.cpp b/plugins/stdinstance/stdinstversionlist.cpp
new file mode 100644
index 00000000..4ad4c52f
--- /dev/null
+++ b/plugins/stdinstance/stdinstversionlist.cpp
@@ -0,0 +1,509 @@
+/* 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 "stdinstversionlist.h"
+
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+
+#include <QtXml/QDomDocument>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonParseError>
+
+#include <QDateTime>
+#include <QMap>
+#include <QMapIterator>
+#include <QStringList>
+#include <QUrl>
+
+#include <QRegExp>
+
+#include <QDebug>
+
+#include <instversion.h>
+
+#include "stdinstversion.h"
+
+#define MCDL_URLBASE "http://assets.minecraft.net/"
+#define ASSETS_URLBASE "http://s3.amazonaws.com/MinecraftDownload/"
+#define MCN_URLBASE "http://sonicrules.org/mcnweb.py"
+
+// When this is defined, prints the entire version list to qDebug() after loading.
+#define PRINT_VERSIONS
+
+
+StdInstVersionList vList;
+
+StdInstVersionList::StdInstVersionList(QObject *parent) :
+ InstVersionList(parent)
+{
+ loaded = false;
+}
+
+Task *StdInstVersionList::getLoadTask()
+{
+ return new StdInstVListLoadTask(this);
+}
+
+bool StdInstVersionList::isLoaded()
+{
+ return loaded;
+}
+
+const InstVersion *StdInstVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+int StdInstVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+void StdInstVersionList::printToStdOut()
+{
+ qDebug() << "---------------- Version List ----------------";
+
+ for (int i = 0; i < m_vlist.count(); i++)
+ {
+ StdInstVersion *version = qobject_cast<StdInstVersion *>(m_vlist.at(i));
+
+ if (!version)
+ continue;
+
+ qDebug() << "Version " << version->name();
+ qDebug() << "\tDownload: " << version->downloadURL();
+ qDebug() << "\tTimestamp: " << version->timestamp();
+ qDebug() << "\tType: " << version->type();
+ qDebug() << "----------------------------------------------";
+ }
+}
+
+
+StdInstVListLoadTask::StdInstVListLoadTask(StdInstVersionList *vlist) :
+ Task(vlist)
+{
+ m_list = vlist;
+ processedMCDLReply = false;
+ processedAssetsReply = false;
+ processedMCNReply = false;
+
+ currentStable = NULL;
+ foundCurrentInAssets = false;
+}
+
+void StdInstVListLoadTask::executeTask()
+{
+ setSubStatus();
+
+ // Initialize the network access manager.
+ QNetworkAccessManager netMgr;
+
+ mcdlReply = netMgr.get(QNetworkRequest(QUrl(ASSETS_URLBASE)));
+ assetsReply = netMgr.get(QNetworkRequest(QUrl(MCDL_URLBASE)));
+ mcnReply = netMgr.get(QNetworkRequest(QUrl(QString(MCN_URLBASE) + "?pversion=1&list=True")));
+
+ connect(mcdlReply, SIGNAL(finished()),
+ SLOT(processMCDLReply()));
+ connect(mcnReply, SIGNAL(finished()),
+ SLOT(processMCNReply()));
+
+ exec();
+ finalize();
+}
+
+void StdInstVListLoadTask::finalize()
+{
+ // First, we need to do some cleanup. We loaded MCNostalgia versions into
+ // mcnList and all the others into tempList. MCNostalgia provides some versions
+ // that are on assets.minecraft.net and we want to ignore those, so we remove
+ // and delete them from mcnList.
+
+ // To start, we get a list of the descriptors in tmpList.
+ QStringList tlistDescriptors;
+ for (int i = 0; i < tempList.count(); i++)
+ tlistDescriptors.append(tempList.at(i)->descriptor());
+
+ // Now, we go through our MCNostalgia version list and remove anything with
+ // a descriptor that matches one we already have in tempList.
+ // We'll need a list of items we're going to remove.
+ for (int i = 0; i < mcnList.count(); i++)
+ if (tlistDescriptors.contains(mcnList.at(i)->descriptor()))
+ delete mcnList.takeAt(i--); // We need to decrement here because we're removing an item.
+
+ // Now that the duplicates are gone, we need to merge the two lists. This is
+ // simple enough.
+ tempList.append(mcnList);
+
+ // We're done with mcnList now, but the items have been moved over to
+ // tempList, so we don't need to delete them.
+
+ // Now we swap the list we loaded into the actual version list.
+ // This applies our changes to the version list immediately and still gives us
+ // access to the old list so that we can delete the objects in it and free their memory.
+ // By doing this, we cause the version list to update immediately.
+ m_list->m_vlist.swap(tempList);
+
+ // We called swap, so all the data that was in the version list previously is now in
+ // tempList (and vice-versa). Now we just free the memory.
+ while (!tempList.isEmpty())
+ delete tempList.takeFirst();
+
+#ifdef PRINT_VERSIONS
+ m_list->printToStdOut();
+#endif
+}
+
+inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
+{
+ QDomNodeList elementList = parent.elementsByTagName(tagname);
+ if (elementList.count())
+ return elementList.at(0).toElement();
+ else
+ return QDomElement();
+}
+
+inline QDateTime timeFromS3Time(QString str)
+{
+ const QString fmt("yyyy-MM-dd'T'HH:mm:ss'.000Z'");
+ return QDateTime::fromString(str, fmt);
+}
+
+void StdInstVListLoadTask::processMCDLReply()
+{
+ switch (mcdlReply->error())
+ {
+ case QNetworkReply::NoError:
+ {
+ // Get the XML string.
+ QString xmlString = mcdlReply->readAll();
+
+ QString xmlErrorMsg;
+
+ QDomDocument doc;
+ if (!doc.setContent(xmlString, false, &xmlErrorMsg))
+ {
+ // TODO: Display error message to the user.
+ qDebug(QString("Failed to process Minecraft download site. XML error: %s").
+ arg(xmlErrorMsg).toUtf8());
+ }
+
+ QDomNodeList contents = doc.elementsByTagName("Contents");
+
+ for (int i = 0; i < contents.length(); i++)
+ {
+ QDomElement element = contents.at(i).toElement();
+
+ if (element.isNull())
+ continue;
+
+ QDomElement keyElement = getDomElementByTagName(element, "Key");
+ QDomElement lastmodElement = getDomElementByTagName(element, "LastModified");
+ QDomElement etagElement = getDomElementByTagName(element, "ETag");
+
+ if (keyElement.isNull() || lastmodElement.isNull() || etagElement.isNull())
+ continue;
+
+ QString key = keyElement.text();
+ QString lastModStr = lastmodElement.text();
+ QString etagStr = etagElement.text();
+ QString dlUrl = "http://s3.amazonaws.com/MinecraftDownload/";
+
+ if (key != "minecraft.jar")
+ continue;
+
+ QDateTime versionTimestamp = timeFromS3Time(lastModStr);
+ if (!versionTimestamp.isValid())
+ {
+ qDebug(QString("Failed to parse timestamp for current stable version %1").
+ arg(lastModStr).toUtf8());
+ versionTimestamp = QDateTime::currentDateTime();
+ }
+
+ currentStable = new StdInstVersion("LatestStable", "Current",
+ versionTimestamp.toMSecsSinceEpoch(),
+ "http://s3.amazonaws.com/MinecraftDownload/",
+ true, etagStr, m_list);
+
+ setSubStatus("Loaded latest version info.");
+ }
+ break;
+ }
+
+ default:
+ // TODO: Network error handling.
+ break;
+ }
+
+ if (!currentStable)
+ qDebug("Failed to get current stable version.");
+
+
+ processedMCDLReply = true;
+ updateStuff();
+
+ // If the assets request isn't finished yet, connect the slot to allow it
+ // to process when the request is done. Otherwise, simply call the
+ // processAssetsReply slot directly.
+ if (!assetsReply->isFinished())
+ connect(assetsReply, SIGNAL(finished()),
+ SLOT(processAssetsReply()));
+ else if (!processedAssetsReply)
+ processAssetsReply();
+}
+
+void StdInstVListLoadTask::processAssetsReply()
+{
+ switch (assetsReply->error())
+ {
+ case QNetworkReply::NoError:
+ {
+ // Get the XML string.
+ QString xmlString = assetsReply->readAll();
+
+ QString xmlErrorMsg;
+
+ QDomDocument doc;
+ if (!doc.setContent(xmlString, false, &xmlErrorMsg))
+ {
+ // TODO: Display error message to the user.
+ qDebug(QString("Failed to process assets.minecraft.net. XML error: %s").
+ arg(xmlErrorMsg).toUtf8());
+ }
+
+ QDomNodeList contents = doc.elementsByTagName("Contents");
+
+ QRegExp mcRegex("/minecraft.jar$");
+ QRegExp snapshotRegex("[0-9][0-9]w[0-9][0-9][a-z]|pre|rc");
+
+ for (int i = 0; i < contents.length(); i++)
+ {
+ QDomElement element = contents.at(i).toElement();
+
+ if (element.isNull())
+ continue;
+
+ QDomElement keyElement = getDomElementByTagName(element, "Key");
+ QDomElement lastmodElement = getDomElementByTagName(element, "LastModified");
+ QDomElement etagElement = getDomElementByTagName(element, "ETag");
+
+ if (keyElement.isNull() || lastmodElement.isNull() || etagElement.isNull())
+ continue;
+
+ QString key = keyElement.text();
+ QString lastModStr = lastmodElement.text();
+ QString etagStr = etagElement.text();
+
+ if (!key.contains(mcRegex))
+ continue;
+
+ QString versionDirName = key.left(key.length() - 14);
+ QString dlUrl = QString("http://assets.minecraft.net/%1/").arg(versionDirName);
+
+ QString versionName = versionDirName.replace("_", ".");
+
+ QDateTime versionTimestamp = timeFromS3Time(lastModStr);
+ if (!versionTimestamp.isValid())
+ {
+ qDebug(QString("Failed to parse timestamp for version %1 %2").
+ arg(versionName, lastModStr).toUtf8());
+ versionTimestamp = QDateTime::currentDateTime();
+ }
+
+ if (currentStable)
+ {
+ if (etagStr == currentStable->etag())
+ {
+ StdInstVersion *version = new StdInstVersion(
+ versionName, versionName,
+ versionTimestamp.toMSecsSinceEpoch(),
+ currentStable->downloadURL(), true, etagStr, m_list);
+ version->setVersionType(StdInstVersion::CurrentStable);
+ tempList.push_back(version);
+ foundCurrentInAssets = true;
+ }
+ else
+ {
+ bool older = versionTimestamp.toMSecsSinceEpoch() < currentStable->timestamp();
+ bool newer = versionTimestamp.toMSecsSinceEpoch() > currentStable->timestamp();
+ bool isSnapshot = versionName.contains(snapshotRegex);
+
+ StdInstVersion *version = new StdInstVersion(
+ versionName, versionName,
+ versionTimestamp.toMSecsSinceEpoch(),
+ dlUrl, false, etagStr, m_list);
+
+ if (newer)
+ {
+ version->setVersionType(StdInstVersion::Snapshot);
+ }
+ else if (older && isSnapshot)
+ {
+ version->setVersionType(StdInstVersion::OldSnapshot);
+ }
+ else if (older)
+ {
+ version->setVersionType(StdInstVersion::Stable);
+ }
+ else
+ {
+ // Shouldn't happen, but just in case...
+ version->setVersionType(StdInstVersion::CurrentStable);
+ }
+
+ tempList.push_back(version);
+ }
+ }
+ else // If there isn't a current stable version.
+ {
+ bool isSnapshot = versionName.contains(snapshotRegex);
+
+ StdInstVersion *version = new StdInstVersion(
+ versionName, versionName,
+ versionTimestamp.toMSecsSinceEpoch(),
+ dlUrl, false, etagStr, m_list);
+ version->setVersionType(isSnapshot? StdInstVersion::Snapshot :
+ StdInstVersion::Stable);
+ tempList.push_back(version);
+ }
+ }
+
+ setSubStatus("Loaded assets.minecraft.net");
+ break;
+ }
+
+ default:
+ // TODO: Network error handling.
+ break;
+ }
+
+ processedAssetsReply = true;
+ updateStuff();
+}
+
+
+QString mcnToAssetsVersion(QString mcnVersion);
+
+void StdInstVListLoadTask::processMCNReply()
+{
+ switch (assetsReply->error())
+ {
+ case QNetworkReply::NoError:
+ {
+ QJsonParseError pError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(mcnReply->readAll(), &pError);
+
+ if (pError.error != QJsonParseError::NoError)
+ {
+ // Handle errors.
+ qDebug() << "Failed to parse MCNostalgia response. JSON parser error: " <<
+ pError.errorString();
+ break;
+ }
+
+
+ // Load data.
+ QRegExp indevRegex("in(f)?dev");
+ QJsonArray vlistArray = jsonDoc.object().value("order").toArray();
+
+ for (int i = 0; i < vlistArray.size(); i++)
+ {
+ QString rawVersion = vlistArray.at(i).toString();
+ if (rawVersion.isEmpty() || rawVersion.contains(indevRegex))
+ continue;
+
+ QString niceVersion = mcnToAssetsVersion(rawVersion);
+ if (niceVersion.isEmpty())
+ continue;
+
+ StdInstVersion *version = StdInstVersion::mcnVersion(rawVersion, niceVersion);
+ mcnList.prepend(version);
+ }
+
+ setSubStatus("Loaded MCNostalgia");
+ break;
+ }
+
+ default:
+ // TODO: Network error handling.
+ break;
+ }
+
+
+ processedMCNReply = true;
+ updateStuff();
+}
+
+void StdInstVListLoadTask::setSubStatus(const QString &msg)
+{
+ if (msg.isEmpty())
+ setStatus("Loading instance version list...");
+ else
+ setStatus("Loading instance version list: " + msg);
+}
+
+void StdInstVListLoadTask::updateStuff()
+{
+ const int totalReqs = 3;
+ int reqsComplete = 0;
+
+ if (processedMCDLReply)
+ reqsComplete++;
+ if (processedAssetsReply)
+ reqsComplete++;
+ if (processedMCNReply)
+ reqsComplete++;
+
+ calcProgress(reqsComplete, totalReqs);
+
+ if (reqsComplete >= totalReqs)
+ {
+ quit();
+ }
+}
+
+class MCNostalgiaVNameMap
+{
+public:
+ QMap <QString, QString> mapping;
+ MCNostalgiaVNameMap()
+ {
+ // An empty string means that it should be ignored
+ mapping["1.4.6_pre"] = "";
+ mapping["1.4.5_pre"] = "";
+ mapping["1.4.3_pre"] = "1.4.3";
+ mapping["1.4.2_pre"] = "";
+ mapping["1.4.1_pre"] = "1.4.1";
+ mapping["1.4_pre"] = "1.4";
+ mapping["1.3.2_pre"] = "";
+ mapping["1.3.1_pre"] = "";
+ mapping["1.3_pre"] = "";
+ mapping["1.2_pre"] = "1.2";
+ }
+} mcnVNMap;
+
+QString mcnToAssetsVersion(QString mcnVersion)
+{
+ QMap<QString, QString>::iterator iter = mcnVNMap.mapping.find(mcnVersion);
+ if (iter != mcnVNMap.mapping.end())
+ {
+ return iter.value();
+ }
+ return mcnVersion;
+}
diff --git a/plugins/stdinstance/stdinstversionlist.h b/plugins/stdinstance/stdinstversionlist.h
new file mode 100644
index 00000000..1fad48c2
--- /dev/null
+++ b/plugins/stdinstance/stdinstversionlist.h
@@ -0,0 +1,99 @@
+/* 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.
+ */
+
+#ifndef STDINSTVERSIONLIST_H
+#define STDINSTVERSIONLIST_H
+
+#include <instversionlist.h>
+
+#include <task.h>
+
+class QNetworkReply;
+
+class StdInstVListLoadTask;
+class StdInstVersion;
+
+class StdInstVersionList : public InstVersionList
+{
+ Q_OBJECT
+public:
+ friend class StdInstVListLoadTask;
+
+ explicit StdInstVersionList(QObject *parent = 0);
+
+ virtual Task *getLoadTask();
+
+ virtual bool isLoaded();
+
+ virtual const InstVersion *at(int i) const;
+
+ virtual int count() const;
+
+ //! Prints the list to stdout. This is mainly for debugging.
+ virtual void printToStdOut();
+
+protected:
+ QList<InstVersion *> m_vlist;
+
+ bool loaded;
+};
+
+class StdInstVListLoadTask : public Task
+{
+Q_OBJECT
+public:
+ StdInstVListLoadTask(StdInstVersionList *vlist);
+
+ virtual void executeTask();
+
+ //! Performs some final processing when the task is finished.
+ virtual void finalize();
+
+protected slots:
+ //! Slot connected to the finished signal for mcdlReply.
+ virtual void processMCDLReply();
+
+ //! Slot connected to the finished signal for assetsReply.
+ virtual void processAssetsReply();
+
+ //! Slot connected to the finished signal for mcnReply.
+ virtual void processMCNReply();
+
+protected:
+ void setSubStatus(const QString &msg = "");
+
+ StdInstVersionList *m_list;
+ QList<InstVersion *> tempList; //! < List of loaded versions.
+ QList<InstVersion *> mcnList; //! < List of MCNostalgia versions.
+
+ QNetworkReply *assetsReply; //! < The reply from assets.minecraft.net
+ QNetworkReply *mcdlReply; //! < The reply from s3.amazonaws.com/MinecraftDownload
+ QNetworkReply *mcnReply; //! < The reply from MCNostalgia.
+
+ bool processedAssetsReply;
+ bool processedMCDLReply;
+ bool processedMCNReply;
+
+ //! Checks if the task is finished processing replies and, if so, exits the task's event loop.
+ void updateStuff();
+
+ StdInstVersion *currentStable;
+
+ bool foundCurrentInAssets;
+};
+
+extern StdInstVersionList vList;
+
+#endif // STDINSTVERSIONLIST_H