aboutsummaryrefslogtreecommitdiff
path: root/backend/lists
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2013-07-29 00:59:35 +0200
committerPetr Mrázek <peterix@gmail.com>2013-07-29 00:59:35 +0200
commit2e0cbf393a5320dbf5448ca44a9b5905314b0be8 (patch)
tree4baac9cf015ca7b15d83de33c705e0d8d4497d30 /backend/lists
parent8808a8b108b82916eaf30f9aca50cd3ab16af230 (diff)
downloadPrismLauncher-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.tar.gz
PrismLauncher-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.tar.bz2
PrismLauncher-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.zip
Massive renaming in the backend folder, all around restructure in the same.
Diffstat (limited to 'backend/lists')
-rw-r--r--backend/lists/InstVersionList.cpp129
-rw-r--r--backend/lists/InstVersionList.h121
-rw-r--r--backend/lists/InstanceList.cpp232
-rw-r--r--backend/lists/InstanceList.h92
-rw-r--r--backend/lists/LwjglVersionList.cpp204
-rw-r--r--backend/lists/LwjglVersionList.h132
-rw-r--r--backend/lists/MinecraftVersionList.cpp528
-rw-r--r--backend/lists/MinecraftVersionList.h106
8 files changed, 1544 insertions, 0 deletions
diff --git a/backend/lists/InstVersionList.cpp b/backend/lists/InstVersionList.cpp
new file mode 100644
index 00000000..c65770a9
--- /dev/null
+++ b/backend/lists/InstVersionList.cpp
@@ -0,0 +1,129 @@
+/* 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 "InstVersionList.h"
+#include "InstanceVersion.h"
+
+InstVersionList::InstVersionList(QObject *parent) :
+ QAbstractListModel(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;
+}
+
+const InstVersion *InstVersionList::getLatestStable() const
+{
+ if (count() <= 0)
+ return NULL;
+ else
+ return at(0);
+}
+
+QVariant InstVersionList::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+
+ const InstVersion *version = at(index.row());
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case NameColumn:
+ return version->name();
+
+ case TypeColumn:
+ return version->typeName();
+
+ case TimeColumn:
+ return version->timestamp();
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return version->descriptor();
+
+ case VersionPointerRole:
+ return qVariantFromValue((void *) version);
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant InstVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case NameColumn:
+ return "Name";
+
+ case TypeColumn:
+ return "Type";
+
+ case TimeColumn:
+ return "Time";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case NameColumn:
+ return "The name of the version.";
+
+ case TypeColumn:
+ return "The version's type.";
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+int InstVersionList::rowCount(const QModelIndex &parent) const
+{
+ // Return count
+ return count();
+}
+
+int InstVersionList::columnCount(const QModelIndex &parent) const
+{
+ return 2;
+}
diff --git a/backend/lists/InstVersionList.h b/backend/lists/InstVersionList.h
new file mode 100644
index 00000000..97e00383
--- /dev/null
+++ b/backend/lists/InstVersionList.h
@@ -0,0 +1,121 @@
+/* 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 <QVariant>
+#include <QAbstractListModel>
+
+#include "libmmc_config.h"
+
+class InstVersion;
+class Task;
+
+/*!
+ * \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.
+ *
+ * Note that this class also inherits from QAbstractListModel. Methods from that
+ * class determine how this version list shows up in a list view. Said methods
+ * all have a default implementation, but they can be overridden by plugins to
+ * change the behavior of the list.
+ */
+class LIBMULTIMC_EXPORT InstVersionList : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum ModelRoles
+ {
+ VersionPointerRole = 0x34B1CB48
+ };
+
+ enum VListColumns
+ {
+ // First column - Name
+ NameColumn = 0,
+
+ // Second column - Type
+ TypeColumn,
+
+ // Third column - Timestamp
+ TimeColumn
+ };
+
+ explicit InstVersionList(QObject *parent = 0);
+
+ /*!
+ * \brief Gets a task that will reload the version islt.
+ * Simply execute the task to load the list.
+ * The task returned by this function should reset the model when it's done.
+ * \return A pointer to a task that reloads the version list.
+ */
+ virtual Task *getLoadTask() = 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.
+ virtual const InstVersion *at(int i) const = 0;
+
+ //! Returns the number of versions in the list.
+ virtual int count() const = 0;
+
+
+ //////// List Model Functions ////////
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int rowCount(const QModelIndex &parent) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+
+
+ /*!
+ * \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);
+
+ /*!
+ * \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 const InstVersion *getLatestStable() const;
+
+ /*!
+ * Sorts the version list.
+ */
+ virtual void sort() = 0;
+
+protected slots:
+ /*!
+ * Updates this list with the given list of versions.
+ * This is done by copying each version in the given list and inserting it
+ * into this one.
+ * We need to do this so that we can set the parents of the versions are set to this
+ * version list. This can't be done in the load task, because the versions the load
+ * task creates are on the load task's thread and Qt won't allow their parents
+ * to be set to something created on another thread.
+ * To get around that problem, we invoke this method on the GUI thread, which
+ * then copies the versions and sets their parents correctly.
+ * \param versions List of versions whose parents should be set.
+ */
+ virtual void updateListData(QList<InstVersion *> versions) = 0;
+};
diff --git a/backend/lists/InstanceList.cpp b/backend/lists/InstanceList.cpp
new file mode 100644
index 00000000..101d52c5
--- /dev/null
+++ b/backend/lists/InstanceList.cpp
@@ -0,0 +1,232 @@
+/* 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 <QDir>
+#include <QFile>
+#include <QDirIterator>
+#include <QThread>
+#include <QTextStream>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "lists/InstanceList.h"
+#include "BaseInstance.h"
+#include "InstanceFactory.h"
+
+#include "pathutils.h"
+
+const static int GROUP_FILE_FORMAT_VERSION = 1;
+
+InstanceList::InstanceList(const QString &instDir, QObject *parent) :
+ QObject(parent), m_instDir("instances")
+{
+
+}
+
+void InstanceList::loadGroupList(QMap<QString, QString> & groupMap)
+{
+ QString groupFileName = m_instDir + "/instgroups.json";
+
+ // if there's no group file, fail
+ 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.");
+ 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());
+ 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());
+ 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());
+ 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++)
+ {
+ groupMap[(*iter2).toString()] = groupName;
+ }
+ }
+}
+
+InstanceList::InstListError InstanceList::loadList()
+{
+ // load the instance groups
+ QMap<QString, QString> groupMap;
+ loadGroupList(groupMap);
+
+ m_instances.clear();
+ QDir dir(m_instDir);
+ QDirIterator iter(dir);
+ while (iter.hasNext())
+ {
+ 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)
+ {
+ case InstanceFactory::NoLoadError:
+ break;
+ case InstanceFactory::NotAnInstance:
+ break;
+ }
+
+ if (error != InstanceFactory::NoLoadError &&
+ error != InstanceFactory::NotAnInstance)
+ {
+ 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);
+ break;
+ }
+ qDebug(errorMsg.toUtf8());
+ }
+ else if (!instPtr)
+ {
+ qDebug(QString("Error loading instance %1. Instance loader returned null.").
+ arg(QFileInfo(subDir).baseName()).toUtf8());
+ }
+ else
+ {
+ QSharedPointer<BaseInstance> inst(instPtr);
+ auto iter = groupMap.find(inst->id());
+ if(iter != groupMap.end())
+ {
+ inst->setGroup((*iter));
+ }
+ qDebug(QString("Loaded instance %1").arg(inst->name()).toUtf8());
+ inst->setParent(this);
+ m_instances.append(inst);
+ connect(instPtr, SIGNAL(propertiesChanged(BaseInstance*)),this, SLOT(propertiesChanged(BaseInstance*)));
+ }
+ }
+ emit invalidated();
+ return NoError;
+}
+
+/// Clear all instances. Triggers notifications.
+void InstanceList::clear()
+{
+ m_instances.clear();
+ emit invalidated();
+};
+
+/// Add an instance. Triggers notifications, returns the new index
+int InstanceList::add(InstancePtr t)
+{
+ m_instances.append(t);
+ emit instanceAdded(count() - 1);
+ return count() - 1;
+}
+
+InstancePtr InstanceList::getInstanceById(QString instId)
+{
+ QListIterator<InstancePtr> iter(m_instances);
+ InstancePtr inst;
+ while(iter.hasNext())
+ {
+ inst = iter.next();
+ if (inst->id() == instId)
+ break;
+ }
+ if (inst->id() != instId)
+ return InstancePtr();
+ else
+ return iter.peekPrevious();
+}
+
+void InstanceList::propertiesChanged(BaseInstance * inst)
+{
+ for(int i = 0; i < m_instances.count(); i++)
+ {
+ if(inst == m_instances[i].data())
+ {
+ emit instanceChanged(i);
+ break;
+ }
+ }
+}
diff --git a/backend/lists/InstanceList.h b/backend/lists/InstanceList.h
new file mode 100644
index 00000000..8c9965e5
--- /dev/null
+++ b/backend/lists/InstanceList.h
@@ -0,0 +1,92 @@
+/* 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 <QSharedPointer>
+
+#include "BaseInstance.h"
+#include "libmmc_config.h"
+
+class BaseInstance;
+
+class LIBMULTIMC_EXPORT InstanceList : public QObject
+{
+ Q_OBJECT
+private:
+ /*!
+ * \brief Get the instance groups
+ */
+ void loadGroupList(QMap<QString, QString> & groupList);
+
+public:
+ explicit InstanceList(const QString &instDir, QObject *parent = 0);
+
+ /*!
+ * \brief Error codes returned by functions in the InstanceList class.
+ * NoError Indicates that no error occurred.
+ * UnknownError indicates that an unspecified error occurred.
+ */
+ enum InstListError
+ {
+ NoError = 0,
+ UnknownError
+ };
+
+ QString instDir() const { return m_instDir; }
+
+ /*!
+ * \brief Loads the instance list. Triggers notifications.
+ */
+ InstListError loadList();
+
+ /*!
+ * \brief Get the instance at index
+ */
+ InstancePtr at(int i) const
+ {
+ return m_instances.at(i);
+ };
+
+ /*!
+ * \brief Get the count of loaded instances
+ */
+ int count() const
+ {
+ return m_instances.count();
+ };
+
+ /// Clear all instances. Triggers notifications.
+ void clear();
+
+ /// Add an instance. Triggers notifications, returns the new index
+ int add(InstancePtr t);
+
+ /// Get an instance by ID
+ InstancePtr getInstanceById (QString id);
+
+signals:
+ void instanceAdded(int index);
+ void instanceChanged(int index);
+ void invalidated();
+
+private slots:
+ void propertiesChanged(BaseInstance * inst);
+
+protected:
+ QString m_instDir;
+ QList< InstancePtr > m_instances;
+};
diff --git a/backend/lists/LwjglVersionList.cpp b/backend/lists/LwjglVersionList.cpp
new file mode 100644
index 00000000..824d0906
--- /dev/null
+++ b/backend/lists/LwjglVersionList.cpp
@@ -0,0 +1,204 @@
+/* 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 "LwjglVersionList.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;
+}
+
+
+LWJGLVersionList::LWJGLVersionList(QObject *parent) :
+ QAbstractListModel(parent)
+{
+ setLoading(false);
+}
+
+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().toString();
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant LWJGLVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return "Version";
+
+ case Qt::ToolTipRole:
+ return "LWJGL version name.";
+
+ default:
+ return QVariant();
+ }
+}
+
+int LWJGLVersionList::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+bool LWJGLVersionList::isLoading() const
+{
+ return m_loading;
+}
+
+void LWJGLVersionList::loadList()
+{
+ Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)");
+
+ setLoading(true);
+ reply = netMgr.get(QNetworkRequest(QUrl(RSS_URL)));
+ connect(reply, SIGNAL(finished()), SLOT(netRequestComplete()));
+}
+
+inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
+{
+ QDomNodeList elementList = parent.elementsByTagName(tagname);
+ if (elementList.count())
+ return elementList.at(0).toElement();
+ else
+ return QDomElement();
+}
+
+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");
+
+ 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));
+ 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));
+ // Subtract 4 here to remove the .zip file extension.
+ name = name.left(lwjglRegex.matchedLength() - 4);
+
+ 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.");
+ finished();
+ }
+ else
+ {
+ failed("Failed to load LWJGL list. Network error: " + reply->errorString());
+ }
+
+ setLoading(false);
+ reply->deleteLater();
+}
+
+const PtrLWJGLVersion LWJGLVersionList::getVersion(const QString &versionName)
+{
+ for (int i = 0; i < count(); i++)
+ {
+ if (at(i)->name() == versionName)
+ return at(i);
+ }
+ return PtrLWJGLVersion();
+}
+
+
+void LWJGLVersionList::failed(QString msg)
+{
+ qWarning() << msg;
+ emit loadListFailed(msg);
+}
+
+void LWJGLVersionList::finished()
+{
+ emit loadListFinished();
+}
+
+void LWJGLVersionList::setLoading(bool loading)
+{
+ m_loading = loading;
+ emit loadingStateUpdated(m_loading);
+}
diff --git a/backend/lists/LwjglVersionList.h b/backend/lists/LwjglVersionList.h
new file mode 100644
index 00000000..f3e7799a
--- /dev/null
+++ b/backend/lists/LwjglVersionList.h
@@ -0,0 +1,132 @@
+/* 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 <QNetworkAccessManager>
+#include <QNetworkReply>
+
+#include "libmmc_config.h"
+
+class LWJGLVersion;
+typedef QSharedPointer<LWJGLVersion> PtrLWJGLVersion;
+
+class LIBMULTIMC_EXPORT LWJGLVersion : public QObject
+{
+ Q_OBJECT
+
+ /*!
+ * The name of the LWJGL version.
+ */
+ Q_PROPERTY(QString name READ name)
+
+ /*!
+ * The URL for this version of LWJGL.
+ */
+ Q_PROPERTY(QUrl url READ url)
+
+ LWJGLVersion(const QString &name, const QUrl &url, QObject *parent = 0) :
+ QObject(parent), m_name(name), m_url(url) { }
+public:
+
+ static PtrLWJGLVersion Create(const QString &name, const QUrl &url, QObject *parent = 0)
+ {
+ return PtrLWJGLVersion(new LWJGLVersion(name, url, parent));
+ };
+
+ QString name() const { return m_name; }
+
+ QUrl url() const { return m_url; }
+
+protected:
+ QString m_name;
+ QUrl m_url;
+};
+
+class LIBMULTIMC_EXPORT LWJGLVersionList : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit LWJGLVersionList(QObject *parent = 0);
+
+ static LWJGLVersionList &get();
+
+ bool isLoaded() { return m_vlist.length() > 0; }
+
+ const PtrLWJGLVersion getVersion(const QString &versionName);
+ PtrLWJGLVersion at(int index) { return m_vlist[index]; }
+ const PtrLWJGLVersion at(int index) const { return m_vlist[index]; }
+
+ int count() const { return m_vlist.length(); }
+
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int rowCount(const QModelIndex &parent) const { return count(); }
+ virtual int columnCount(const QModelIndex &parent) const;
+
+ virtual bool isLoading() const;
+ virtual bool errored() const { return m_errored; }
+
+ virtual QString lastErrorMsg() const { return m_lastErrorMsg; }
+
+public slots:
+ /*!
+ * Loads the version list.
+ * This is done asynchronously. On success, the loadListFinished() signal will
+ * be emitted. The list model will be reset as well, resulting in the modelReset()
+ * signal being emitted. Note that the model will be reset before loadListFinished() is emitted.
+ * If loading the list failed, the loadListFailed(QString msg),
+ * signal will be emitted.
+ */
+ virtual void loadList();
+
+signals:
+ /*!
+ * Emitted when the list either starts or finishes loading.
+ * \param loading Whether or not the list is loading.
+ */
+ void loadingStateUpdated(bool loading);
+
+ void loadListFinished();
+
+ void loadListFailed(QString msg);
+
+private:
+ QList<PtrLWJGLVersion> m_vlist;
+
+ QNetworkReply *m_netReply;
+
+ QNetworkAccessManager netMgr;
+ QNetworkReply *reply;
+
+ bool m_loading;
+ bool m_errored;
+ QString m_lastErrorMsg;
+
+ void failed(QString msg);
+
+ void finished();
+
+ void setLoading(bool loading);
+
+private slots:
+ virtual void netRequestComplete();
+};
+
diff --git a/backend/lists/MinecraftVersionList.cpp b/backend/lists/MinecraftVersionList.cpp
new file mode 100644
index 00000000..d576397f
--- /dev/null
+++ b/backend/lists/MinecraftVersionList.cpp
@@ -0,0 +1,528 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 "MinecraftVersionList.h"
+
+#include <QDebug>
+
+#include <QtXml>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <QJsonParseError>
+
+#include <QtAlgorithms>
+
+#include <QtNetwork>
+
+#include "netutils.h"
+
+#define MCVLIST_URLBASE "http://s3.amazonaws.com/Minecraft.Download/versions/"
+#define ASSETS_URLBASE "http://assets.minecraft.net/"
+#define MCN_URLBASE "http://sonicrules.org/mcnweb.py"
+
+MinecraftVersionList mcVList;
+
+MinecraftVersionList::MinecraftVersionList(QObject *parent) :
+ InstVersionList(parent)
+{
+
+}
+
+Task *MinecraftVersionList::getLoadTask()
+{
+ return new MCVListLoadTask(this);
+}
+
+bool MinecraftVersionList::isLoaded()
+{
+ return m_loaded;
+}
+
+const InstVersion *MinecraftVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+int MinecraftVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+void MinecraftVersionList::printToStdOut() const
+{
+ qDebug() << "---------------- Version List ----------------";
+
+ for (int i = 0; i < m_vlist.count(); i++)
+ {
+ MinecraftVersion *version = qobject_cast<MinecraftVersion *>(m_vlist.at(i));
+
+ if (!version)
+ continue;
+
+ qDebug() << "Version " << version->name();
+ qDebug() << "\tDownload: " << version->downloadURL();
+ qDebug() << "\tTimestamp: " << version->timestamp();
+ qDebug() << "\tType: " << version->typeName();
+ qDebug() << "----------------------------------------------";
+ }
+}
+
+bool cmpVersions(const InstVersion *first, const InstVersion *second)
+{
+ return !first->isLessThan(*second);
+}
+
+void MinecraftVersionList::sort()
+{
+ beginResetModel();
+ qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
+ endResetModel();
+}
+
+InstVersion *MinecraftVersionList::getLatestStable() const
+{
+ for (int i = 0; i < m_vlist.length(); i++)
+ {
+ if (((MinecraftVersion *)m_vlist.at(i))->versionType() == MinecraftVersion::CurrentStable)
+ {
+ return m_vlist.at(i);
+ }
+ }
+ return NULL;
+}
+
+MinecraftVersionList &MinecraftVersionList::getMainList()
+{
+ return mcVList;
+}
+
+void MinecraftVersionList::updateListData(QList<InstVersion *> versions)
+{
+ // First, we populate a temporary list with the copies of the versions.
+ QList<InstVersion *> tempList;
+ for (int i = 0; i < versions.length(); i++)
+ {
+ InstVersion *version = versions[i]->copyVersion(this);
+ Q_ASSERT(version != NULL);
+ tempList.append(version);
+ }
+
+ // Now we swap the temporary list into the actual version list.
+ // This applies our changes to the version list immediately and still gives us
+ // access to the old version list so that we can delete the objects in it and
+ // free their memory. By doing this, we cause the version list to update as
+ // quickly as possible.
+ beginResetModel();
+ m_vlist.swap(tempList);
+ m_loaded = true;
+ endResetModel();
+
+ // 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();
+
+ // NOW SORT!!
+ sort();
+}
+
+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)
+{
+ return QDateTime::fromString(str, Qt::ISODate);
+}
+
+
+MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
+{
+ m_list = vlist;
+ m_currentStable = NULL;
+ processedAssetsReply = false;
+ processedMCNReply = false;
+ processedMCVListReply = false;
+}
+
+MCVListLoadTask::~MCVListLoadTask()
+{
+// delete netMgr;
+}
+
+void MCVListLoadTask::executeTask()
+{
+ setSubStatus();
+
+ QNetworkAccessManager networkMgr;
+ netMgr = &networkMgr;
+
+ if (!loadFromVList())
+ {
+ qDebug() << "Failed to load from Mojang version list.