summaryrefslogtreecommitdiffstats
path: root/libmultimc/src/minecraftversionlist.cpp
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 /libmultimc/src/minecraftversionlist.cpp
parent8808a8b108b82916eaf30f9aca50cd3ab16af230 (diff)
downloadMultiMC-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.tar
MultiMC-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.tar.gz
MultiMC-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.tar.lz
MultiMC-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.tar.xz
MultiMC-2e0cbf393a5320dbf5448ca44a9b5905314b0be8.zip
Massive renaming in the backend folder, all around restructure in the same.
Diffstat (limited to 'libmultimc/src/minecraftversionlist.cpp')
-rw-r--r--libmultimc/src/minecraftversionlist.cpp528
1 files changed, 0 insertions, 528 deletions
diff --git a/libmultimc/src/minecraftversionlist.cpp b/libmultimc/src/minecraftversionlist.cpp
deleted file mode 100644
index 17737b1e..00000000
--- a/libmultimc/src/minecraftversionlist.cpp
+++ /dev/null
@@ -1,528 +0,0 @@
-/* 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 "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.";
- }
- if (!loadFromAssets())
- {
- qDebug() << "Failed to load assets version list.";
- }
- if (!loadMCNostalgia())
- {
- qDebug() << "Failed to load MCNostalgia version list.";
- }
- finalize();
-}
-
-void MCVListLoadTask::setSubStatus(const QString msg)
-{
- if (msg.isEmpty())
- setStatus("Loading instance version list...");
- else
- setStatus("Loading instance version list: " + msg);
-}
-
-// FIXME: we should have a local cache of the version list and a local cache of version data
-bool MCVListLoadTask::loadFromVList()
-{
- QNetworkReply *vlistReply = netMgr->get(QNetworkRequest(QUrl(QString(MCVLIST_URLBASE) +
- "versions.json")));
- NetUtils::waitForNetRequest(vlistReply);
-
- switch (vlistReply->error())
- {
- case QNetworkReply::NoError:
- {
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
-
- if (jsonError.error == QJsonParseError::NoError)
- {
- Q_ASSERT_X(jsonDoc.isObject(), "loadFromVList", "jsonDoc is not an object");
-
- QJsonObject root = jsonDoc.object();
-
- // Get the ID of the latest release and the latest snapshot.
- Q_ASSERT_X(root.value("latest").isObject(), "loadFromVList",
- "version list is missing 'latest' object");
- QJsonObject latest = root.value("latest").toObject();
-
- QString latestReleaseID = latest.value("release").toString("");
- QString latestSnapshotID = latest.value("snapshot").toString("");
- Q_ASSERT_X(!latestReleaseID.isEmpty(), "loadFromVList", "latest release field is missing");
- Q_ASSERT_X(!latestSnapshotID.isEmpty(), "loadFromVList", "latest snapshot field is missing");
-
- // Now, get the array of versions.
- Q_ASSERT_X(root.value("versions").isArray(), "loadFromVList",
- "version list object is missing 'versions' array");
- QJsonArray versions = root.value("versions").toArray();
-
- for (int i = 0; i < versions.count(); i++)
- {
- // Load the version info.
- Q_ASSERT_X(versions[i].isObject(), "loadFromVList",
- QString("in versions array, index %1 is not an object").
- arg(i).toUtf8());
- QJsonObject version = versions[i].toObject();
-
- QString versionID = version.value("id").toString("");
- QString versionTimeStr = version.value("releaseTime").toString("");
- QString versionTypeStr = version.value("type").toString("");
-
- Q_ASSERT_X(!versionID.isEmpty(), "loadFromVList",
- QString("in versions array, index %1's \"id\" field is not a valid string").
- arg(i).toUtf8());
- Q_ASSERT_X(!versionTimeStr.isEmpty(), "loadFromVList",
- QString("in versions array, index %1's \"time\" field is not a valid string").
- arg(i).toUtf8());
- Q_ASSERT_X(!versionTypeStr.isEmpty(), "loadFromVList",
- QString("in versions array, index %1's \"type\" field is not a valid string").
- arg(i).toUtf8());
-
-
- // Now, process that info and add the version to the list.
-
- // Parse the timestamp.
- QDateTime versionTime = timeFromS3Time(versionTimeStr);
-
- Q_ASSERT_X(versionTime.isValid(), "loadFromVList",
- QString("in versions array, index %1's timestamp failed to parse").
- arg(i).toUtf8());
-
- // Parse the type.
- MinecraftVersion::VersionType versionType;
- if (versionTypeStr == "release")
- {
- // Check if this version is the current stable version.
- if (versionID == latestReleaseID)
- versionType = MinecraftVersion::CurrentStable;
- else
- versionType = MinecraftVersion::Stable;
- }
- else if(versionTypeStr == "snapshot")
- {
- versionType = MinecraftVersion::Snapshot;
- }
- else
- {
- // we don't know what to do with this...
- continue;
- }
-
- // Get the download URL.
- QString dlUrl = QString(MCVLIST_URLBASE) + versionID + "/";
-
-
- // Now, we construct the version object and add it to the list.
- MinecraftVersion *mcVersion = new MinecraftVersion(
- versionID, versionID, versionTime.toMSecsSinceEpoch(),
- dlUrl, "");
- mcVersion->setLauncherVersion(MinecraftVersion::Launcher16);
- mcVersion->setVersionType(versionType);
- tempList.append(mcVersion);
- }
- }
- else
- {
- qDebug() << "Error parsing version list JSON:" << jsonError.errorString();
- }
-
- break;
- }
-
- default:
- // TODO: Network error handling.
- qDebug() << "Failed to load Minecraft main version list" << vlistReply->errorString();
- break;
- }
-
- return true;
-}
-
-bool MCVListLoadTask::loadFromAssets()
-{
- setSubStatus("Loading versions from assets.minecraft.net...");
-
- bool succeeded = false;
-
- QNetworkReply *assetsReply = netMgr->get(QNetworkRequest(QUrl(ASSETS_URLBASE)));
- NetUtils::waitForNetRequest(assetsReply);
-
- 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() << "Failed to process assets.minecraft.net. XML error:" <<
- xmlErrorMsg << xmlString;
- }
-
- 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 (m_currentStable)
- {
- {
- bool older = versionTimestamp.toMSecsSinceEpoch() < m_currentStable->timestamp();
- bool newer = versionTimestamp.toMSecsSinceEpoch() > m_currentStable->timestamp();
- bool isSnapshot = versionName.contains(snapshotRegex);
-
- MinecraftVersion *version = new MinecraftVersion(
- versionName, versionName,
- versionTimestamp.toMSecsSinceEpoch(),
- dlUrl, etagStr);
-
- if (newer)
- {
- version->setVersionType(MinecraftVersion::Snapshot);
- }
- else if (older && isSnapshot)
- {
- version->setVersionType(MinecraftVersion::OldSnapshot);
- }
- else if (older)
- {
- version->setVersionType(MinecraftVersion::Stable);
- }
- else
- {
- // Shouldn't happen, but just in case...
- version->setVersionType(MinecraftVersion::CurrentStable);
- }
-
- assetsList.push_back(version);
- }
- }
- else // If there isn't a current stable version.
- {
- bool isSnapshot = versionName.contains(snapshotRegex);
-
- MinecraftVersion *version = new MinecraftVersion(
- versionName, versionName,
- versionTimestamp.toMSecsSinceEpoch(),
- dlUrl, etagStr);
- version->setVersionType(isSnapshot? MinecraftVersion::Snapshot :
- MinecraftVersion::Stable);
- assetsList.push_back(version);
- }
- }
-
- setSubStatus("Loaded assets.minecraft.net");
- succeeded = true;
- break;
- }
-
- default:
- // TODO: Network error handling.
- qDebug() << "Failed to load assets.minecraft.net" << assetsReply->errorString();
- break;
- }
-
- processedAssetsReply = true;
- updateStuff();
- return succeeded;
-}
-
-bool MCVListLoadTask::loadMCNostalgia()
-{
- QNetworkReply *mcnReply = netMgr->get(QNetworkRequest(QUrl(QString(MCN_URLBASE) + "?pversion=1&list=True")));
- NetUtils::waitForNetRequest(mcnReply);
- processedMCNReply = true;
- updateStuff();
- return true;
-}
-
-bool MCVListLoadTask::finalize()
-{
- // First, we need to do some cleanup. We loaded assets versions into assetsList,
- // 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. assets.minecraft.net also provides
- // versions that are on Mojang's version list and we want to ignore those as well.
-
- // 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 assets version list and remove anything with
- // a descriptor that matches one we already have in tempList.
- for (int i = 0; i < assetsList.count(); i++)
- if (tlistDescriptors.contains(assetsList.at(i)->descriptor()))
- delete assetsList.takeAt(i--); // We need to decrement here because we're removing an item.
-
- // We also need to rebuild the list of descriptors.
- tlistDescriptors.clear();
- for (int i = 0; i < tempList.count(); i++)
- tlistDescriptors.append(tempList.at(i)->descriptor());
-
- // Next, we go through our MCNostalgia version list and do the same thing.
- 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 lists. This is
- // simple enough.
- tempList.append(assetsList);
- tempList.append(mcnList);
-
- // We're done with these lists now, but the items have been moved over to
- // tempList, so we don't need to delete them yet.
-
- // Now, we invoke the updateListData slot on the GUI thread. This will copy all
- // the versions we loaded and set their parents to the version list.
- // Then, it will swap the new list with the old one and free the old list's memory.
- QMetaObject::invokeMethod(m_list, "updateListData", Qt::BlockingQueuedConnection,
- Q_ARG(QList<InstVersion*>, tempList));
-
- // Once that's finished, we can delete the versions in our temp list.
- while (!tempList.isEmpty())
- delete tempList.takeFirst();
-
-#ifdef PRINT_VERSIONS
- m_list->printToStdOut();
-#endif
- return true;
-}
-
-void MCVListLoadTask::updateStuff()
-{
- const int totalReqs = 3;
- int reqsComplete = 0;
-
- if (processedMCVListReply)
- reqsComplete++;
- if (processedAssetsReply)
- reqsComplete++;
- if (processedMCNReply)
- reqsComplete++;
-
- calcProgress(reqsComplete, totalReqs);
-
- if (reqsComplete >= totalReqs)
- {
- quit();
- }
-}