From a02e62f17f7b51c489e209ab6937ad717fbcfb07 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 14 Dec 2013 16:02:51 +0100 Subject: Tests for parsing of channel lists in UpdateChecker --- logic/updater/UpdateChecker.cpp | 2 +- logic/updater/UpdateChecker.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp index 5ff1898e..af56288c 100644 --- a/logic/updater/UpdateChecker.cpp +++ b/logic/updater/UpdateChecker.cpp @@ -44,7 +44,7 @@ QList UpdateChecker::getChannelList() const bool UpdateChecker::hasChannels() const { - return m_channels.isEmpty(); + return !m_channels.isEmpty(); } void UpdateChecker::checkForUpdate() diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h index 59fb8e47..131f49a2 100644 --- a/logic/updater/UpdateChecker.h +++ b/logic/updater/UpdateChecker.h @@ -27,6 +27,9 @@ public: UpdateChecker(); void checkForUpdate(); + void setCurrentChannel(const QString &channel) { m_currentChannel = channel; } + void setChannelListUrl(const QString &url) { m_channelListUrl = url; } + /*! * Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake). * If this isn't called before checkForUpdate(), it will automatically be called. -- cgit v1.2.3 From f273334212274b1f1c7da376ef186314de8c4428 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 14 Dec 2013 19:19:14 +0100 Subject: More tests for the UpdateChecker class. It should be done for now. --- logic/updater/UpdateChecker.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'logic') diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h index 131f49a2..5b7efc05 100644 --- a/logic/updater/UpdateChecker.h +++ b/logic/updater/UpdateChecker.h @@ -73,6 +73,8 @@ private slots: void chanListDownloadFailed(); private: + friend class UpdateCheckerTest; + NetJobPtr indexJob; NetJobPtr chanListJob; -- cgit v1.2.3 From 3e8bcc1cf6f3400fff9aa361ddc109bafe16d646 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sun, 15 Dec 2013 12:18:42 +0100 Subject: Unit tests for the DownloadUpdateTask class --- logic/updater/DownloadUpdateTask.cpp | 112 +++++++++++++++++++---------------- logic/updater/DownloadUpdateTask.h | 22 ++++++- 2 files changed, 80 insertions(+), 54 deletions(-) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index d9aab826..ed5bfd02 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -45,55 +45,54 @@ void DownloadUpdateTask::executeTask() findCurrentVersionInfo(); } -void DownloadUpdateTask::findCurrentVersionInfo() +void DownloadUpdateTask::processChannels() { - setStatus(tr("Finding information about the current version.")); - auto checker = MMC->updateChecker(); - // This runs after we've tried loading the channel list. - // If the channel list doesn't need to be loaded, this will be called immediately. - // If the channel list does need to be loaded, this will be called when it's done. - auto processFunc = [this, &checker] () -> void + // Now, check the channel list again. + if (!checker->hasChannels()) { - // Now, check the channel list again. - if (checker->hasChannels()) - { - // We still couldn't load the channel list. Give up. Call loadVersionInfo and return. - QLOG_INFO() << "Reloading the channel list didn't work. Giving up."; - loadVersionInfo(); - return; - } + // We still couldn't load the channel list. Give up. Call loadVersionInfo and return. + QLOG_INFO() << "Reloading the channel list didn't work. Giving up."; + loadVersionInfo(); + return; + } - QList channels = checker->getChannelList(); - QString channelId = MMC->version().channel; + QList channels = checker->getChannelList(); + QString channelId = MMC->version().channel; - // Search through the channel list for a channel with the correct ID. - for (auto channel : channels) + // Search through the channel list for a channel with the correct ID. + for (auto channel : channels) + { + if (channel.id == channelId) { - if (channel.id == channelId) - { - QLOG_INFO() << "Found matching channel."; - m_cRepoUrl = channel.url; - break; - } + QLOG_INFO() << "Found matching channel."; + m_cRepoUrl = preparePath(channel.url); + break; } + } - // Now that we've done that, load version info. - loadVersionInfo(); - }; + // Now that we've done that, load version info. + loadVersionInfo(); +} + +void DownloadUpdateTask::findCurrentVersionInfo() +{ + setStatus(tr("Finding information about the current version.")); + + auto checker = MMC->updateChecker(); - if (checker->hasChannels()) + if (!checker->hasChannels()) { // Load the channel list and wait for it to finish loading. QLOG_INFO() << "No channel list entries found. Will try reloading it."; - QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, processFunc); + QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this, &DownloadUpdateTask::processChannels); checker->updateChanList(); } else { - processFunc(); + processChannels(); } } @@ -152,12 +151,24 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() { setStatus(tr("Reading file lists.")); - parseVersionInfo(NEW_VERSION, &m_nVersionFileList); + setStatus(tr("Reading file list for new version.")); + QLOG_DEBUG() << "Reading file list for new version."; + QString error; + if (!parseVersionInfo(std::dynamic_pointer_cast( + m_vinfoNetJob->first())->m_data, &m_nVersionFileList, &error)) + { + emitFailed(error); + return; + } // If there is a second entry in the network job's list, load it as the current version's info. if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed) { - parseVersionInfo(CURRENT_VERSION, &m_cVersionFileList); + setStatus(tr("Reading file list for current version.")); + QLOG_DEBUG() << "Reading file list for current version."; + QString error; + parseVersionInfo(std::dynamic_pointer_cast( + m_vinfoNetJob->operator[](1))->m_data, &m_cVersionFileList, &error); } // We don't need this any more. @@ -167,26 +178,15 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() processFileLists(); } -void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list) +bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error) { - if (vfile == CURRENT_VERSION) setStatus(tr("Reading file list for current version.")); - else if (vfile == NEW_VERSION) setStatus(tr("Reading file list for new version.")); - - QLOG_DEBUG() << "Reading file list for" << (vfile == NEW_VERSION ? "new" : "current") << "version."; - - QByteArray data; - { - ByteArrayDownloadPtr dl = std::dynamic_pointer_cast( - vfile == NEW_VERSION ? m_vinfoNetJob->first() : m_vinfoNetJob->operator[](1)); - data = dl->m_data; - } - QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError) { - QLOG_ERROR() << "Failed to parse version info JSON:" << jsonError.errorString() << "at" << jsonError.offset; - return; + *error = QString("Failed to parse version info JSON: %1 at %2").arg(jsonError.errorString()).arg(jsonError.offset); + QLOG_ERROR() << error; + return false; } QJsonObject json = jsonDoc.object(); @@ -213,11 +213,11 @@ void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFile QString type = sourceObj.value("SourceType").toString(); if (type == "http") { - file.sources.append(FileSource("http", sourceObj.value("Url").toString())); + file.sources.append(FileSource("http", preparePath(sourceObj.value("Url").toString()))); } else if (type == "httpc") { - file.sources.append(FileSource("httpc", sourceObj.value("Url").toString(), sourceObj.value("CompressionType").toString())); + file.sources.append(FileSource("httpc", preparePath(sourceObj.value("Url").toString()), sourceObj.value("CompressionType").toString())); } else { @@ -229,6 +229,8 @@ void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFile list->append(file); } + + return true; } void DownloadUpdateTask::processFileLists() @@ -312,7 +314,7 @@ void DownloadUpdateTask::processFileLists() writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); } -void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) +bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) { // Build the base structure of the XML document. QDomDocument doc; @@ -377,7 +379,15 @@ void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QStrin else { emitFailed(tr("Failed to write update script file.")); + return false; } + + return true; +} + +QString DownloadUpdateTask::preparePath(const QString &path) +{ + return QString(path).replace("$PWD", qApp->applicationDirPath()); } void DownloadUpdateTask::fileDownloadFinished() diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index f5b23d12..1d1fc7bf 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -34,7 +34,8 @@ public: */ QString updateFilesDir(); -protected: +public: + // TODO: We should probably put these data structures into a separate header... /*! @@ -59,6 +60,7 @@ protected: /*! * Structure that describes an entry in a GoUpdate version's `Files` list. */ + struct VersionFileEntry { QString path; @@ -69,6 +71,8 @@ protected: typedef QList VersionFileList; +protected: + friend class DownloadUpdateTaskTest; /*! * Structure that describes an operation to perform when installing updates. @@ -119,6 +123,13 @@ protected: */ virtual void findCurrentVersionInfo(); + /*! + * This runs after we've tried loading the channel list. + * If the channel list doesn't need to be loaded, this will be called immediately. + * If the channel list does need to be loaded, this will be called when it's done. + */ + void processChannels(); + /*! * Downloads the version info files from the repository. * The files for both the current build, and the build that we're updating to need to be downloaded. @@ -142,7 +153,7 @@ protected: /*! * Loads the file list from the given version info JSON object into the given list. */ - virtual void parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list); + virtual bool parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error); /*! * Takes a list of file entries for the current version's files and the new version's files @@ -153,7 +164,7 @@ protected: /*! * Takes the operations list and writes an install script for the updater to the update files directory. */ - virtual void writeInstallScript(UpdateOperationList& opsList, QString scriptFile); + virtual bool writeInstallScript(UpdateOperationList& opsList, QString scriptFile); VersionFileList m_downloadList; UpdateOperationList m_operationList; @@ -181,6 +192,11 @@ protected: */ QTemporaryDir m_updateFilesDir; + /*! + * Substitutes $PWD for the application directory + */ + static QString preparePath(const QString &path); + protected slots: void vinfoDownloadFinished(); void vinfoDownloadFailed(); -- cgit v1.2.3 From 7f884a18a85eca8c1a395ab0e9d421f17a98f142 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sun, 15 Dec 2013 18:50:56 +0100 Subject: Finish unit tests for the DownloadUpdateTask class --- logic/updater/DownloadUpdateTask.cpp | 60 ++++++++++++++++++++---------------- logic/updater/DownloadUpdateTask.h | 18 ++++++----- 2 files changed, 44 insertions(+), 34 deletions(-) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index ed5bfd02..d72cfcf6 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -234,15 +234,36 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis } void DownloadUpdateTask::processFileLists() +{ + // Create a network job for downloading files. + NetJob* netJob = new NetJob("Update Files"); + + processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList); + + // Add listeners to wait for the downloads to finish. + QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished); + QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged); + QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed); + + // Now start the download. + setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size()))); + QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path(); + m_filesNetJob.reset(netJob); + netJob->start(); + + writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); +} + +void DownloadUpdateTask::processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, DownloadUpdateTask::UpdateOperationList &ops) { setStatus(tr("Processing file lists. Figuring out how to install the update.")); // First, if we've loaded the current version's file list, we need to iterate through it and // delete anything in the current one version's list that isn't in the new version's list. - for (VersionFileEntry entry : m_cVersionFileList) + for (VersionFileEntry entry : currentVersion) { bool keep = false; - for (VersionFileEntry newEntry : m_nVersionFileList) + for (VersionFileEntry newEntry : newVersion) { if (newEntry.path == entry.path) { @@ -253,14 +274,11 @@ void DownloadUpdateTask::processFileLists() } // If the loop reaches the end and we didn't find a match, delete the file. if(!keep) - m_operationList.append(UpdateOperation::DeleteOp(entry.path)); + ops.append(UpdateOperation::DeleteOp(entry.path)); } - // Create a network job for downloading files. - NetJob* netJob = new NetJob("Update Files"); - // Next, check each file in MultiMC's folder and see if we need to update them. - for (VersionFileEntry entry : m_nVersionFileList) + for (VersionFileEntry entry : newVersion) { // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a way to do this in the background. QString fileMD5; @@ -287,31 +305,21 @@ void DownloadUpdateTask::processFileLists() // Download it to updatedir/- where filepath is the file's path with slashes replaced by underscores. QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); - // We need to download the file to the updatefiles folder and add a task to copy it to its install path. - auto download = MD5EtagDownload::make(source.url, dlPath); - download->m_check_md5 = true; - download->m_expected_md5 = entry.md5; - netJob->addNetAction(download); + if (job) + { + // We need to download the file to the updatefiles folder and add a task to copy it to its install path. + auto download = MD5EtagDownload::make(source.url, dlPath); + download->m_check_md5 = true; + download->m_expected_md5 = entry.md5; + job->addNetAction(download); + } // Now add a copy operation to our operations list to install the file. - m_operationList.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); } } } } - - // Add listeners to wait for the downloads to finish. - QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished); - QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged); - QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed); - - // Now start the download. - setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size()))); - QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path(); - m_filesNetJob.reset(netJob); - netJob->start(); - - writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); } bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 1d1fc7bf..8530be77 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -54,13 +54,11 @@ public: QString url; QString compressionType; }; - typedef QList FileSourceList; /*! * Structure that describes an entry in a GoUpdate version's `Files` list. */ - struct VersionFileEntry { QString path; @@ -68,12 +66,8 @@ public: FileSourceList sources; QString md5; }; - typedef QList VersionFileList; -protected: - friend class DownloadUpdateTaskTest; - /*! * Structure that describes an operation to perform when installing updates. */ @@ -104,9 +98,12 @@ protected: // Yeah yeah, polymorphism blah blah inheritance, blah blah object oriented. I'm lazy, OK? }; - typedef QList UpdateOperationList; +protected: + friend class DownloadUpdateTaskTest; + + /*! * Used for arguments to parseVersionInfo and friends to specify which version info file to parse. */ @@ -159,6 +156,12 @@ protected: * Takes a list of file entries for the current version's files and the new version's files * and populates the downloadList and operationList with information about how to download and install the update. */ + virtual void processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, UpdateOperationList &ops); + + /*! + * Calls \see processFileLists to populate the \see m_operationList and a NetJob, and then executes + * the NetJob to fetch all needed files + */ virtual void processFileLists(); /*! @@ -166,7 +169,6 @@ protected: */ virtual bool writeInstallScript(UpdateOperationList& opsList, QString scriptFile); - VersionFileList m_downloadList; UpdateOperationList m_operationList; VersionFileList m_nVersionFileList; -- cgit v1.2.3 From be8dba9ee2573062dc689041874bade27504f045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 16 Dec 2013 22:30:42 +0100 Subject: Remove obsolete auto login option. --- logic/BaseInstance.cpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'logic') diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index 6f8222b7..bc82fee1 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -85,11 +85,6 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, settings().registerSetting( new OverrideSetting("PermGen", globalSettings->getSetting("PermGen"))); - // Auto login - settings().registerSetting(new Setting("OverrideLogin", false)); - settings().registerSetting( - new OverrideSetting("AutoLogin", globalSettings->getSetting("AutoLogin"))); - // Console settings().registerSetting(new Setting("OverrideConsole", false)); settings().registerSetting( -- cgit v1.2.3 From d6c71488b34a2854461feee3296c11568542ecbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 17 Dec 2013 02:09:58 +0100 Subject: Some test madness --- logic/net/ForgeXzDownload.cpp | 5 +- logic/updater/DownloadUpdateTask.cpp | 279 +++++++++++++++++++++++------------ logic/updater/DownloadUpdateTask.h | 5 +- 3 files changed, 193 insertions(+), 96 deletions(-) (limited to 'logic') diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index 1771d304..83cbabd0 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "logger/QsLog.h" ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction() @@ -312,9 +313,11 @@ void ForgeXzDownload::decompressAndInstall() // revert pack200 pack200_file.close(); QString pack_name = pack200_file.fileName(); + QString source_native = QDir::toNativeSeparators(pack_name); + QString target_native = QDir::toNativeSeparators(m_target_path); try { - unpack_200(pack_name.toStdString(), m_target_path.toStdString()); + unpack_200(source_native.toStdString(), target_native.toStdString()); } catch (std::runtime_error &err) { diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index d72cfcf6..cc06104a 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -26,9 +26,8 @@ #include - -DownloadUpdateTask::DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent) : - Task(parent) +DownloadUpdateTask::DownloadUpdateTask(QString repoUrl, int versionId, QObject *parent) + : Task(parent) { m_cVersionId = MMC->version().build; @@ -87,7 +86,8 @@ void DownloadUpdateTask::findCurrentVersionInfo() // Load the channel list and wait for it to finish loading. QLOG_INFO() << "No channel list entries found. Will try reloading it."; - QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this, &DownloadUpdateTask::processChannels); + QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this, + &DownloadUpdateTask::processChannels); checker->updateChanList(); } else @@ -101,11 +101,12 @@ void DownloadUpdateTask::loadVersionInfo() setStatus(tr("Loading version information.")); // Create the net job for loading version info. - NetJob* netJob = new NetJob("Version Info"); - + NetJob *netJob = new NetJob("Version Info"); + // Find the index URL. QUrl newIndexUrl = QUrl(m_nRepoUrl).resolved(QString::number(m_nVersionId) + ".json"); - + QLOG_DEBUG() << m_nRepoUrl << " turns into " << newIndexUrl; + // Add a net action to download the version info for the version we're updating to. netJob->addNetAction(ByteArrayDownload::make(newIndexUrl)); @@ -114,10 +115,12 @@ void DownloadUpdateTask::loadVersionInfo() { QUrl cIndexUrl = QUrl(m_cRepoUrl).resolved(QString::number(m_cVersionId) + ".json"); netJob->addNetAction(ByteArrayDownload::make(cIndexUrl)); + QLOG_DEBUG() << m_cRepoUrl << " turns into " << cIndexUrl; } // Connect slots so we know when it's done. - QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::vinfoDownloadFinished); + QObject::connect(netJob, &NetJob::succeeded, this, + &DownloadUpdateTask::vinfoDownloadFinished); QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::vinfoDownloadFailed); // Store the NetJob in a class member. We don't want to lose it! @@ -135,7 +138,8 @@ void DownloadUpdateTask::vinfoDownloadFinished() void DownloadUpdateTask::vinfoDownloadFailed() { - // Something failed. We really need the second download (current version info), so parse downloads anyways as long as the first one succeeded. + // Something failed. We really need the second download (current version info), so parse + // downloads anyways as long as the first one succeeded. if (m_vinfoNetJob->first()->m_status != Job_Failed) { parseDownloadedVersionInfo(); @@ -154,43 +158,51 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() setStatus(tr("Reading file list for new version.")); QLOG_DEBUG() << "Reading file list for new version."; QString error; - if (!parseVersionInfo(std::dynamic_pointer_cast( - m_vinfoNetJob->first())->m_data, &m_nVersionFileList, &error)) + if (!parseVersionInfo( + std::dynamic_pointer_cast(m_vinfoNetJob->first())->m_data, + &m_nVersionFileList, &error)) { emitFailed(error); return; } - // If there is a second entry in the network job's list, load it as the current version's info. + // If there is a second entry in the network job's list, load it as the current version's + // info. if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed) { setStatus(tr("Reading file list for current version.")); QLOG_DEBUG() << "Reading file list for current version."; QString error; - parseVersionInfo(std::dynamic_pointer_cast( - m_vinfoNetJob->operator[](1))->m_data, &m_cVersionFileList, &error); + parseVersionInfo( + std::dynamic_pointer_cast(m_vinfoNetJob->operator[](1))->m_data, + &m_cVersionFileList, &error); } // We don't need this any more. m_vinfoNetJob.reset(); - // Now that we're done loading version info, we can move on to the next step. Process file lists and download files. + // Now that we're done loading version info, we can move on to the next step. Process file + // lists and download files. processFileLists(); } -bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error) +bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList *list, + QString *error) { QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError) { - *error = QString("Failed to parse version info JSON: %1 at %2").arg(jsonError.errorString()).arg(jsonError.offset); + *error = QString("Failed to parse version info JSON: %1 at %2") + .arg(jsonError.errorString()) + .arg(jsonError.offset); QLOG_ERROR() << error; return false; } QJsonObject json = jsonDoc.object(); + QLOG_DEBUG() << data; QLOG_DEBUG() << "Loading version info from JSON."; QJsonArray filesArray = json.value("Files").toArray(); for (QJsonValue fileValue : filesArray) @@ -198,13 +210,10 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis QJsonObject fileObj = fileValue.toObject(); VersionFileEntry file{ - fileObj.value("Path").toString(), - fileObj.value("Perms").toVariant().toInt(), - FileSourceList(), - fileObj.value("MD5").toString(), - }; + fileObj.value("Path").toString(), fileObj.value("Perms").toVariant().toInt(), + FileSourceList(), fileObj.value("MD5").toString(), }; QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode; - + QJsonArray sourceArray = fileObj.value("Sources").toArray(); for (QJsonValue val : sourceArray) { @@ -213,11 +222,14 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis QString type = sourceObj.value("SourceType").toString(); if (type == "http") { - file.sources.append(FileSource("http", preparePath(sourceObj.value("Url").toString()))); + file.sources.append( + FileSource("http", preparePath(sourceObj.value("Url").toString()))); } else if (type == "httpc") { - file.sources.append(FileSource("httpc", preparePath(sourceObj.value("Url").toString()), sourceObj.value("CompressionType").toString())); + file.sources.append(FileSource("httpc", + preparePath(sourceObj.value("Url").toString()), + sourceObj.value("CompressionType").toString())); } else { @@ -236,13 +248,19 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis void DownloadUpdateTask::processFileLists() { // Create a network job for downloading files. - NetJob* netJob = new NetJob("Update Files"); + NetJob *netJob = new NetJob("Update Files"); - processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList); + if (!processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList)) + { + emitFailed(tr("Failed to process update lists...")); + return; + } // Add listeners to wait for the downloads to finish. - QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished); - QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged); + QObject::connect(netJob, &NetJob::succeeded, this, + &DownloadUpdateTask::fileDownloadFinished); + QObject::connect(netJob, &NetJob::progress, this, + &DownloadUpdateTask::fileDownloadProgressChanged); QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed); // Now start the download. @@ -254,75 +272,144 @@ void DownloadUpdateTask::processFileLists() writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); } -void DownloadUpdateTask::processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, DownloadUpdateTask::UpdateOperationList &ops) +bool +DownloadUpdateTask::processFileLists(NetJob *job, + const DownloadUpdateTask::VersionFileList ¤tVersion, + const DownloadUpdateTask::VersionFileList &newVersion, + DownloadUpdateTask::UpdateOperationList &ops) { setStatus(tr("Processing file lists. Figuring out how to install the update.")); - // First, if we've loaded the current version's file list, we need to iterate through it and + // First, if we've loaded the current version's file list, we need to iterate through it and // delete anything in the current one version's list that isn't in the new version's list. for (VersionFileEntry entry : currentVersion) { + QFileInfo toDelete(entry.path); + if (!toDelete.exists()) + { + QLOG_ERROR() << "Expected file " << toDelete.absoluteFilePath() + << " doesn't exist!"; + QLOG_ERROR() << "CWD: " << QDir::currentPath(); + } bool keep = false; + + // for (VersionFileEntry newEntry : newVersion) { if (newEntry.path == entry.path) { - QLOG_DEBUG() << "Not deleting" << entry.path << "because it is still present in the new version."; + QLOG_DEBUG() << "Not deleting" << entry.path + << "because it is still present in the new version."; keep = true; break; } } + // If the loop reaches the end and we didn't find a match, delete the file. - if(!keep) - ops.append(UpdateOperation::DeleteOp(entry.path)); + if (!keep) + { + QFileInfo toDelete(entry.path); + if (toDelete.exists()) + ops.append(UpdateOperation::DeleteOp(entry.path)); + } } // Next, check each file in MultiMC's folder and see if we need to update them. for (VersionFileEntry entry : newVersion) { - // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a way to do this in the background. + // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a + // way to do this in the background. QString fileMD5; QFile entryFile(entry.path); - if (entryFile.open(QFile::ReadOnly)) + QFileInfo entryInfo(entry.path); + + bool needs_upgrade = false; + if (!entryFile.exists()) + { + needs_upgrade = true; + } + else { - QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(entryFile.readAll()); - fileMD5 = hash.result().toHex(); + bool pass = true; + if (!entryInfo.isReadable()) + { + QLOG_ERROR() << "File " << entry.path << " is not readable."; + pass = false; + } + if (!entryInfo.isWritable()) + { + QLOG_ERROR() << "File " << entry.path << " is not writable."; + pass = false; + } + if (!entryFile.open(QFile::ReadOnly)) + { + QLOG_ERROR() << "File " << entry.path << " cannot be opened for reading."; + pass = false; + } + if (!pass) + { + QLOG_ERROR() << "CWD: " << QDir::currentPath(); + ops.clear(); + return false; + } } - if (!entryFile.exists() || fileMD5.isEmpty() || fileMD5 != entry.md5) + QCryptographicHash hash(QCryptographicHash::Md5); + auto foo = entryFile.readAll(); + + hash.addData(foo); + fileMD5 = hash.result().toHex(); + if ((fileMD5 != entry.md5)) + { + QLOG_DEBUG() << "MD5Sum does not match!"; + QLOG_DEBUG() << "Expected:'" << entry.md5 << "'"; + QLOG_DEBUG() << "Got: '" << fileMD5 << "'"; + needs_upgrade = true; + } + + // skip file. it doesn't need an upgrade. + if (!needs_upgrade) { - QLOG_DEBUG() << "Found file" << entry.path << "that needs updating."; + QLOG_DEBUG() << "File" << entry.path << " does not need updating."; + continue; + } + + // yep. this file actually needs an upgrade. PROCEED. + QLOG_DEBUG() << "Found file" << entry.path << " that needs updating."; - // Go through the sources list and find one to use. - // TODO: Make a NetAction that takes a source list and tries each of them until one works. For now, we'll just use the first http one. - for (FileSource source : entry.sources) + // Go through the sources list and find one to use. + // TODO: Make a NetAction that takes a source list and tries each of them until one + // works. For now, we'll just use the first http one. + for (FileSource source : entry.sources) + { + if (source.type == "http") { - if (source.type == "http") + QLOG_DEBUG() << "Will download" << entry.path << "from" << source.url; + + // Download it to updatedir/- where filepath is the file's + // path with slashes replaced by underscores. + QString dlPath = + PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); + + if (job) { - QLOG_DEBUG() << "Will download" << entry.path << "from" << source.url; - - // Download it to updatedir/- where filepath is the file's path with slashes replaced by underscores. - QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); - - if (job) - { - // We need to download the file to the updatefiles folder and add a task to copy it to its install path. - auto download = MD5EtagDownload::make(source.url, dlPath); - download->m_check_md5 = true; - download->m_expected_md5 = entry.md5; - job->addNetAction(download); - } - - // Now add a copy operation to our operations list to install the file. - ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + // We need to download the file to the updatefiles folder and add a task + // to copy it to its install path. + auto download = MD5EtagDownload::make(source.url, dlPath); + download->m_check_md5 = true; + download->m_expected_md5 = entry.md5; + job->addNetAction(download); } + + // Now add a copy operation to our operations list to install the file. + ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); } } } + return true; } -bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) +bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QString scriptFile) { // Build the base structure of the XML document. QDomDocument doc; @@ -342,38 +429,43 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QStrin { QDomElement file = doc.createElement("file"); + QString native_file = QDir::toNativeSeparators(op.file); + QString native_dest = QDir::toNativeSeparators(op.dest); + switch (op.type) { - case UpdateOperation::OP_COPY: - { - // Install the file. - QDomElement name = doc.createElement("source"); - QDomElement path = doc.createElement("dest"); - QDomElement mode = doc.createElement("mode"); - name.appendChild(doc.createTextNode(op.file)); - path.appendChild(doc.createTextNode(op.dest)); - // We need to add a 0 at the beginning here, because Qt doesn't convert to octal correctly. - mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8))); - file.appendChild(name); - file.appendChild(path); - file.appendChild(mode); - installFiles.appendChild(file); - QLOG_DEBUG() << "Will install file" << op.file; - } - break; + case UpdateOperation::OP_COPY: + { + // Install the file. + QDomElement name = doc.createElement("source"); + QDomElement path = doc.createElement("dest"); + QDomElement mode = doc.createElement("mode"); + name.appendChild(doc.createTextNode(native_file)); + path.appendChild(doc.createTextNode(native_dest)); + // We need to add a 0 at the beginning here, because Qt doesn't convert to octal + // correctly. + mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8))); + file.appendChild(name); + file.appendChild(path); + file.appendChild(mode); + installFiles.appendChild(file); + QLOG_DEBUG() << "Will install file" << native_file; + } + break; - case UpdateOperation::OP_DELETE: - { - // Delete the file. - file.appendChild(doc.createTextNode(op.file)); - removeFiles.appendChild(file); - QLOG_DEBUG() << "Will remove file" << op.file; - } - break; + case UpdateOperation::OP_DELETE: + { + // Delete the file. + file.appendChild(doc.createTextNode(native_file)); + removeFiles.appendChild(file); + QLOG_DEBUG() << "Will remove file" << native_file; + } + break; - default: - QLOG_WARN() << "Can't write update operation of type" << op.type << "to file. Not implemented."; - continue; + default: + QLOG_WARN() << "Can't write update operation of type" << op.type + << "to file. Not implemented."; + continue; } } @@ -395,7 +487,9 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QStrin QString DownloadUpdateTask::preparePath(const QString &path) { - return QString(path).replace("$PWD", qApp->applicationDirPath()); + QString foo = path; + foo.replace("$PWD", qApp->applicationDirPath()); + return QUrl::fromLocalFile(foo).toString(QUrl::FullyEncoded); } void DownloadUpdateTask::fileDownloadFinished() @@ -412,11 +506,10 @@ void DownloadUpdateTask::fileDownloadFailed() void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 total) { - setProgress((int)(((float)current / (float)total)*100)); + setProgress((int)(((float)current / (float)total) * 100)); } QString DownloadUpdateTask::updateFilesDir() { return m_updateFilesDir.path(); } - diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 8530be77..1fc14049 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -156,7 +156,7 @@ protected: * Takes a list of file entries for the current version's files and the new version's files * and populates the downloadList and operationList with information about how to download and install the update. */ - virtual void processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, UpdateOperationList &ops); + virtual bool processFileLists(NetJob *job, const VersionFileList ¤tVersion, const VersionFileList &newVersion, UpdateOperationList &ops); /*! * Calls \see processFileLists to populate the \see m_operationList and a NetJob, and then executes @@ -195,7 +195,8 @@ protected: QTemporaryDir m_updateFilesDir; /*! - * Substitutes $PWD for the application directory + * Filters paths + * Path of the format $PWD/path, it is converted to a file:///$PWD/ URL */ static QString preparePath(const QString &path); -- cgit v1.2.3 From 01dbebdfc81abab00048f48e68a4e04d391bc50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 21 Dec 2013 14:25:16 +0100 Subject: Fix issues with the updater * Bad URLs used for downloading update files * MD5ETagDownload resetting the expected ETag after failure to the failed file MD5 checksum * Delete MD5ETagDownload downloaded files if the download fails. --- logic/net/MD5EtagDownload.cpp | 38 +++++++++++++++++++++++++----------- logic/net/MD5EtagDownload.h | 8 +++----- logic/updater/DownloadUpdateTask.cpp | 11 +++++++---- 3 files changed, 37 insertions(+), 20 deletions(-) (limited to 'logic') diff --git a/logic/net/MD5EtagDownload.cpp b/logic/net/MD5EtagDownload.cpp index 435e854e..63583e8d 100644 --- a/logic/net/MD5EtagDownload.cpp +++ b/logic/net/MD5EtagDownload.cpp @@ -23,7 +23,6 @@ MD5EtagDownload::MD5EtagDownload(QUrl url, QString target_path) : NetAction() { m_url = url; m_target_path = target_path; - m_check_md5 = false; m_status = Job_NotStarted; } @@ -34,22 +33,26 @@ void MD5EtagDownload::start() // 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 = + // get the md5 of the local file. + m_local_md5 = 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) + // if we are expecting some md5sum, compare it with the local one + if (!m_expected_md5.isEmpty()) { - QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match."; - emit succeeded(m_index_within_job); - return; + // skip if they match + if(m_local_md5 == m_expected_md5) + { + QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match."; + emit succeeded(m_index_within_job); + return; + } } else { - m_expected_md5 = hash; + // no expected md5. we use the local md5sum as an ETag } } if (!ensureFilePathExists(filename)) @@ -58,9 +61,18 @@ void MD5EtagDownload::start() return; } - QLOG_INFO() << "Downloading " << m_url.toString() << " expecting " << m_expected_md5; QNetworkRequest request(m_url); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); + + QLOG_INFO() << "Downloading " << m_url.toString() << " got " << m_local_md5; + + if(!m_local_md5.isEmpty()) + { + QLOG_INFO() << "Got " << m_local_md5; + request.setRawHeader(QString("If-None-Match").toLatin1(), m_local_md5.toLatin1()); + } + if(!m_expected_md5.isEmpty()) + QLOG_INFO() << "Expecting " << m_expected_md5; + request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); // Go ahead and try to open the file. @@ -107,7 +119,10 @@ void MD5EtagDownload::downloadFinished() m_status = Job_Finished; m_output_file.close(); + // FIXME: compare with the real written data md5sum + // this is just an ETag QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData(); + m_reply.reset(); emit succeeded(m_index_within_job); return; @@ -116,6 +131,7 @@ void MD5EtagDownload::downloadFinished() else { m_output_file.close(); + m_output_file.remove(); m_reply.reset(); emit failed(m_index_within_job); return; diff --git a/logic/net/MD5EtagDownload.h b/logic/net/MD5EtagDownload.h index 416ab9de..d5aed0ca 100644 --- a/logic/net/MD5EtagDownload.h +++ b/logic/net/MD5EtagDownload.h @@ -23,12 +23,10 @@ class MD5EtagDownload : public NetAction { 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 + /// the expected md5 checksum. Only set from outside QString m_expected_md5; + /// the md5 checksum of a file that already exists. + QString m_local_md5; /// if saving to file, use the one specified in this string QString m_target_path; /// this is the output file, if any diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index cc06104a..b017afeb 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -396,7 +396,6 @@ DownloadUpdateTask::processFileLists(NetJob *job, // We need to download the file to the updatefiles folder and add a task // to copy it to its install path. auto download = MD5EtagDownload::make(source.url, dlPath); - download->m_check_md5 = true; download->m_expected_md5 = entry.md5; job->addNetAction(download); } @@ -487,9 +486,13 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin QString DownloadUpdateTask::preparePath(const QString &path) { - QString foo = path; - foo.replace("$PWD", qApp->applicationDirPath()); - return QUrl::fromLocalFile(foo).toString(QUrl::FullyEncoded); + if(path.startsWith("$PWD")) + { + QString foo = path; + foo.replace("$PWD", qApp->applicationDirPath()); + return QUrl::fromLocalFile(foo).toString(QUrl::FullyEncoded); + } + return path; } void DownloadUpdateTask::fileDownloadFinished() -- cgit v1.2.3 From 82c87aa06f793b9f38e6cb42d284f00695f4bac5 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Fri, 20 Dec 2013 14:47:26 +0100 Subject: Initial FTB support. Allows "tracking" of FTB instances. --- logic/InstanceFactory.cpp | 86 ++++++++++++++++------ logic/InstanceFactory.h | 11 ++- logic/LegacyFTBInstance.cpp | 16 +++++ logic/LegacyFTBInstance.h | 13 ++++ logic/OneSixFTBInstance.cpp | 111 ++++++++++++++++++++++++++++ logic/OneSixFTBInstance.h | 20 ++++++ logic/lists/ForgeVersionList.cpp | 1 + logic/lists/InstanceList.cpp | 151 +++++++++++++++++++++++++++++---------- logic/lists/InstanceList.h | 14 ++-- logic/tasks/SequentialTask.cpp | 77 ++++++++++++++++++++ logic/tasks/SequentialTask.h | 32 +++++++++ 11 files changed, 465 insertions(+), 67 deletions(-) create mode 100644 logic/LegacyFTBInstance.cpp create mode 100644 logic/LegacyFTBInstance.h create mode 100644 logic/OneSixFTBInstance.cpp create mode 100644 logic/OneSixFTBInstance.h create mode 100644 logic/tasks/SequentialTask.cpp create mode 100644 logic/tasks/SequentialTask.h (limited to 'logic') diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 66b271d0..31a287dd 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -20,7 +20,9 @@ #include "BaseInstance.h" #include "LegacyInstance.h" +#include "LegacyFTBInstance.h" #include "OneSixInstance.h" +#include "OneSixFTBInstance.h" #include "NostalgiaInstance.h" #include "BaseVersion.h" #include "MinecraftVersion.h" @@ -60,6 +62,14 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new NostalgiaInstance(instDir, m_settings, this); } + else if (inst_type == "LegacyFTB") + { + inst = new LegacyFTBInstance(instDir, m_settings, this); + } + else if (inst_type == "OneSixFTB") + { + inst = new OneSixFTBInstance(instDir, m_settings, this); + } else { return InstanceFactory::UnknownLoadError; @@ -69,7 +79,8 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&inst, BaseVersionPtr version, - const QString &instDir) + const QString &instDir, + const InstType type) { QDir rootDir(instDir); @@ -85,32 +96,63 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg")); m_settings->registerSetting(new Setting("InstanceType", "Legacy")); - switch (mcVer->type) + if (type == NormalInst) { - case MinecraftVersion::Legacy: - m_settings->set("InstanceType", "Legacy"); - inst = new LegacyInstance(instDir, m_settings, this); - 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->setShouldUseCustomBaseJar(false); - break; - case MinecraftVersion::Nostalgia: - m_settings->set("InstanceType", "Nostalgia"); - inst = new NostalgiaInstance(instDir, m_settings, this); - inst->setIntendedVersionId(version->descriptor()); - inst->setShouldUseCustomBaseJar(false); - break; - default: + switch (mcVer->type) + { + case MinecraftVersion::Legacy: + m_settings->set("InstanceType", "Legacy"); + inst = new LegacyInstance(instDir, m_settings, this); + 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->setShouldUseCustomBaseJar(false); + break; + case MinecraftVersion::Nostalgia: + m_settings->set("InstanceType", "Nostalgia"); + inst = new NostalgiaInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + default: + { + delete m_settings; + return InstanceFactory::NoSuchVersion; + } + } + } + else if (type == FTBInstance) + { + switch (mcVer->type) + { + case MinecraftVersion::Legacy: + m_settings->set("InstanceType", "LegacyFTB"); + inst = new LegacyFTBInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + case MinecraftVersion::OneSix: + m_settings->set("InstanceType", "OneSixFTB"); + inst = new OneSixFTBInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + default: + { + delete m_settings; + return InstanceFactory::NoSuchVersion; + } + } + } + else { delete m_settings; return InstanceFactory::NoSuchVersion; } - } // FIXME: really, how do you even know? return InstanceFactory::NoCreateError; diff --git a/logic/InstanceFactory.h b/logic/InstanceFactory.h index 01e5af7e..5ff4c7ec 100644 --- a/logic/InstanceFactory.h +++ b/logic/InstanceFactory.h @@ -55,18 +55,25 @@ public: CantCreateDir }; + enum InstType + { + NormalInst, + FTBInstance + }; + /*! * \brief Creates a stub instance * * \param inst Pointer to store the created instance in. - * \param inst Game version to use for the instance + * \param version Game version to use for the instance * \param instDir The new instance's directory. + * \param type The type of instance to create * \return An InstCreateError error code. * - InstExists if the given instance directory is already an instance. * - CantCreateDir if the given instance directory cannot be created. */ InstCreateError createInstance(BaseInstance *&inst, BaseVersionPtr version, - const QString &instDir); + const QString &instDir, const InstType type = NormalInst); /*! * \brief Creates a copy of an existing instance with a new name diff --git a/logic/LegacyFTBInstance.cpp b/logic/LegacyFTBInstance.cpp new file mode 100644 index 00000000..84d5a900 --- /dev/null +++ b/logic/LegacyFTBInstance.cpp @@ -0,0 +1,16 @@ +#include "LegacyFTBInstance.h" + +LegacyFTBInstance::LegacyFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : + LegacyInstance(rootDir, settings, parent) +{ +} + +QString LegacyFTBInstance::getStatusbarDescription() +{ + return "Legacy FTB: " + intendedVersionId(); +} + +bool LegacyFTBInstance::menuActionEnabled(QString action_name) const +{ + return false; +} diff --git a/logic/LegacyFTBInstance.h b/logic/LegacyFTBInstance.h new file mode 100644 index 00000000..2ae72797 --- /dev/null +++ b/logic/LegacyFTBInstance.h @@ -0,0 +1,13 @@ +#pragma once + +#include "LegacyInstance.h" + +class LegacyFTBInstance : public LegacyInstance +{ + Q_OBJECT +public: + explicit LegacyFTBInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + virtual QString getStatusbarDescription(); + virtual bool menuActionEnabled(QString action_name) const; +}; diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp new file mode 100644 index 00000000..567004b9 --- /dev/null +++ b/logic/OneSixFTBInstance.cpp @@ -0,0 +1,111 @@ +#include "OneSixFTBInstance.h" + +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "tasks/SequentialTask.h" +#include "ForgeInstaller.h" +#include "lists/ForgeVersionList.h" +#include "MultiMC.h" + +class OneSixFTBInstanceForge : public Task +{ + Q_OBJECT +public: + explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) : + Task(parent), instance(inst), version("Forge " + version) + { + } + + void executeTask() + { + for (int i = 0; i < MMC->forgelist()->count(); ++i) + { + if (MMC->forgelist()->at(i)->name() == version) + { + forgeVersion = std::dynamic_pointer_cast(MMC->forgelist()->at(i)); + break; + } + } + if (!forgeVersion) + return; + entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename); + if (entry->stale) + { + setStatus(tr("Downloading Forge...")); + fjob = new NetJob("Forge download"); + fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry)); + connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);}); + connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge); + connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); }); + fjob->start(); + } + else + { + installForge(); + } + } + +private +slots: + void installForge() + { + setStatus(tr("Installing Forge...")); + QString forgePath = entry->getFullPath(); + ForgeInstaller forge(forgePath, forgeVersion->universal_url); + if (!instance->reloadFullVersion()) + { + emitFailed(tr("Couldn't load the version config")); + return; + } + if (!forge.apply(instance->getFullVersion())) + { + emitFailed(tr("Couldn't install Forge")); + return; + } + emitSucceeded(); + } + +private: + OneSixFTBInstance *instance; + QString version; + ForgeVersionPtr forgeVersion; + MetaEntryPtr entry; + NetJob *fjob; +}; + +OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : + OneSixInstance(rootDir, settings, parent) +{ + QFile f(QDir(minecraftRoot()).absoluteFilePath("pack.json")); + if (f.open(QFile::ReadOnly)) + { + QString data = QString::fromUtf8(f.readAll()); + QRegularExpressionMatch match = QRegularExpression("net.minecraftforge:minecraftforge:[\\.\\d]*").match(data); + m_forge.reset(new OneSixLibrary(match.captured())); + m_forge->finalize(); + } +} + +QString OneSixFTBInstance::getStatusbarDescription() +{ + return "OneSix FTB: " + intendedVersionId(); +} +bool OneSixFTBInstance::menuActionEnabled(QString action_name) const +{ + return false; +} + +std::shared_ptr OneSixFTBInstance::doUpdate(bool only_prepare) +{ + std::shared_ptr task; + task.reset(new SequentialTask(this)); + if (!MMC->forgelist()->isLoaded()) + { + task->addTask(std::shared_ptr(MMC->forgelist()->getLoadTask())); + } + task->addTask(OneSixInstance::doUpdate(only_prepare)); + task->addTask(std::shared_ptr(new OneSixFTBInstanceForge(m_forge->version(), this, this))); + return task; +} + +#include "OneSixFTBInstance.moc" diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h new file mode 100644 index 00000000..7600090c --- /dev/null +++ b/logic/OneSixFTBInstance.h @@ -0,0 +1,20 @@ +#pragma once + +#include "OneSixInstance.h" + +class OneSixLibrary; + +class OneSixFTBInstance : public OneSixInstance +{ + Q_OBJECT +public: + explicit OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + virtual QString getStatusbarDescription(); + virtual bool menuActionEnabled(QString action_name) const; + + virtual std::shared_ptr doUpdate(bool only_prepare) override; + +private: + std::shared_ptr m_forge; +}; diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index b5e421af..d6d353da 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -159,6 +159,7 @@ ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task() void ForgeListLoadTask::executeTask() { + setStatus(tr("Fetching Forge version list")); auto job = new NetJob("Version index"); // we do not care if the version is stale or not. auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 15fd10ba..539413d8 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -22,11 +22,14 @@ #include #include #include +#include +#include #include #include "MultiMC.h" #include "logic/lists/InstanceList.h" #include "logic/lists/IconList.h" +#include "logic/lists/MinecraftVersionList.h" #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logger/QsLog.h" @@ -42,6 +45,8 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent) { QDir::current().mkpath(m_instDir); } + + connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this, &InstanceList::loadList); } InstanceList::~InstanceList() @@ -285,57 +290,87 @@ InstanceList::InstListError InstanceList::loadList() beginResetModel(); m_instances.clear(); - QDir dir(m_instDir); - QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, - QDirIterator::FollowSymlinks); - while (iter.hasNext()) + { - QString subDir = iter.next(); - if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists()) - continue; + QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, + QDirIterator::FollowSymlinks); + 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); + BaseInstance *instPtr = NULL; + auto error = InstanceFactory::get().loadInstance(instPtr, subDir); + continueProcessInstance(instPtr, error, subDir, groupMap); + } + } - if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance) + if (MMC->settings()->get("TrackFTBInstances").toBool() && MMC->minecraftlist()->isLoaded()) + { + QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString()); + QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString()); + if (!dir.exists()) { - 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; - } - QLOG_ERROR() << errorMsg.toUtf8(); + QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your settings."; } - else if (!instPtr) + else if (!dataDir.exists()) { - QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.") - .arg(QFileInfo(subDir).baseName()) - .toUtf8(); + QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings"; } else { - std::shared_ptr inst(instPtr); - auto iter = groupMap.find(inst->id()); - if (iter != groupMap.end()) + dir.cd("ModPacks"); + QFile f(dir.absoluteFilePath("modpacks.xml")); + if (f.open(QFile::ReadOnly)) { - inst->setGroupInitial((*iter)); + QXmlStreamReader reader(&f); + while (!reader.atEnd()) + { + switch (reader.readNext()) + { + case QXmlStreamReader::StartElement: + { + if (reader.name() == "modpack") + { + QXmlStreamAttributes attrs = reader.attributes(); + const QDir instanceDir = QDir(dataDir.absoluteFilePath(attrs.value("dir").toString())); + if (instanceDir.exists()) + { + const QString name = attrs.value("name").toString(); + const QString iconKey = attrs.value("logo").toString().remove(QRegularExpression("\\..*")); + const QString mcVersion = attrs.value("mcVersion").toString(); + const QString notes = attrs.value("description").toString(); + QLOG_DEBUG() << dir.absoluteFilePath(attrs.value("logo").toString()); + MMC->icons()->addIcon(iconKey, iconKey, dir.absoluteFilePath(attrs.value("dir").toString() + QDir::separator() + attrs.value("logo").toString()), true); + + BaseInstance *instPtr = NULL; + auto error = InstanceFactory::get().createInstance(instPtr, MMC->minecraftlist()->findVersion(mcVersion), instanceDir.absolutePath(), InstanceFactory::FTBInstance); + if (instPtr && error == InstanceFactory::NoCreateError) + { + instPtr->setGroupInitial("FTB"); + instPtr->setName(name); + instPtr->setIconKey(iconKey); + instPtr->setIntendedVersionId(mcVersion); + instPtr->setNotes(notes); + } + continueProcessInstance(instPtr, error, instanceDir, groupMap); + } + } + break; + } + case QXmlStreamReader::EndElement: + break; + case QXmlStreamReader::Characters: + break; + default: + break; + } + } } - 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 *))); } } + endResetModel(); emit dataIsInvalid(); return NoError; @@ -409,6 +444,46 @@ int InstanceList::getInstIndex(BaseInstance *inst) const return -1; } +void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap) +{ + if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance) + { + QString errorMsg = QString("Failed to load instance %1: ") + .arg(QFileInfo(dir.absolutePath()).baseName()) + .toUtf8(); + + switch (error) + { + default: + errorMsg += QString("Unknown instance loader error %1").arg(error); + break; + } + QLOG_ERROR() << errorMsg.toUtf8(); + } + else if (!instPtr) + { + QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.") + .arg(QFileInfo(dir.absolutePath()).baseName()) + .toUtf8(); + } + else + { + auto iter = groupMap.find(instPtr->id()); + if (iter != groupMap.end()) + { + instPtr->setGroupInitial((*iter)); + } + QLOG_INFO() << "Loaded instance " << instPtr->name(); + instPtr->setParent(this); + m_instances.append(std::shared_ptr(instPtr)); + 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 *))); + } +} + void InstanceList::instanceNuked(BaseInstance *inst) { int i = getInstIndex(inst); diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h index f23b7763..b3ee6cfe 100644 --- a/logic/lists/InstanceList.h +++ b/logic/lists/InstanceList.h @@ -25,6 +25,8 @@ class BaseInstance; +class QDir; + class InstanceList : public QAbstractListModel { Q_OBJECT @@ -65,11 +67,6 @@ public: return m_instDir; } - /*! - * \brief Loads the instance list. Triggers notifications. - */ - InstListError loadList(); - /*! * \brief Get the instance at index */ @@ -108,6 +105,11 @@ public slots: void on_InstFolderChanged(const Setting &setting, QVariant value); + /*! + * \brief Loads the instance list. Triggers notifications. + */ + InstListError loadList(); + private slots: void propertiesChanged(BaseInstance *inst); @@ -117,6 +119,8 @@ slots: private: int getInstIndex(BaseInstance *inst) const; + void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap); + protected: QString m_instDir; QList m_instances; diff --git a/logic/tasks/SequentialTask.cpp b/logic/tasks/SequentialTask.cpp new file mode 100644 index 00000000..63025eee --- /dev/null +++ b/logic/tasks/SequentialTask.cpp @@ -0,0 +1,77 @@ +#include "SequentialTask.h" + +SequentialTask::SequentialTask(QObject *parent) : + Task(parent), m_currentIndex(-1) +{ + +} + +QString SequentialTask::getStatus() const +{ + if (m_queue.isEmpty() || m_currentIndex >= m_queue.size()) + { + return QString(); + } + return m_queue.at(m_currentIndex)->getStatus(); +} + +void SequentialTask::getProgress(qint64 ¤t, qint64 &total) +{ + current = 0; + total = 0; + for (int i = 0; i < m_queue.size(); ++i) + { + qint64 subCurrent, subTotal; + m_queue.at(i)->getProgress(subCurrent, subTotal); + current += subCurrent; + total += subTotal; + } +} + +void SequentialTask::addTask(std::shared_ptr task) +{ + m_queue.append(task); +} + +void SequentialTask::executeTask() +{ + m_currentIndex = -1; + startNext(); +} + +void SequentialTask::startNext() +{ + if (m_currentIndex != -1) + { + std::shared_ptr previous = m_queue[m_currentIndex]; + disconnect(previous.get(), 0, this, 0); + } + m_currentIndex++; + if (m_queue.isEmpty() || m_currentIndex >= m_queue.size()) + { + emitSucceeded(); + return; + } + std::shared_ptr next = m_queue[m_currentIndex]; + connect(next.get(), SIGNAL(failed(QString)), this, SLOT(subTaskFailed(QString))); + connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString))); + connect(next.get(), SIGNAL(progress(qint64,qint64)), this, SLOT(subTaskProgress())); + connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext())); + next->start(); + emit status(getStatus()); +} + +void SequentialTask::subTaskFailed(const QString &msg) +{ + emitFailed(msg); +} +void SequentialTask::subTaskStatus(const QString &msg) +{ + setStatus(msg); +} +void SequentialTask::subTaskProgress() +{ + qint64 current, total; + getProgress(current, total); + setProgress(100 * current / total); +} diff --git a/logic/tasks/SequentialTask.h b/logic/tasks/SequentialTask.h new file mode 100644 index 00000000..7f046928 --- /dev/null +++ b/logic/tasks/SequentialTask.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Task.h" + +#include +#include + +class SequentialTask : public Task +{ + Q_OBJECT +public: + explicit SequentialTask(QObject *parent = 0); + + virtual QString getStatus() const; + virtual void getProgress(qint64 ¤t, qint64 &total); + + void addTask(std::shared_ptr task); + +protected: + void executeTask(); + +private +slots: + void startNext(); + void subTaskFailed(const QString &msg); + void subTaskStatus(const QString &msg); + void subTaskProgress(); + +private: + QQueue > m_queue; + int m_currentIndex; +}; -- cgit v1.2.3 From 74b5b5f535ec8d98ba93c629804c75fda9e32475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 22 Dec 2013 04:31:30 +0100 Subject: Make FTB instances behave better * Do not re-create on every reload * Use the version.json/custom.json logic properly * Should be offline-friendly * FTB instances can be copied, turn into normal instances --- logic/InstanceFactory.cpp | 10 +++ logic/OneSixFTBInstance.cpp | 5 +- logic/lists/InstanceList.cpp | 196 ++++++++++++++++++++++++++++--------------- logic/lists/InstanceList.h | 4 +- 4 files changed, 145 insertions(+), 70 deletions(-) (limited to 'logic') diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 31a287dd..7c778035 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -170,7 +170,17 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne rootDir.removeRecursively(); return InstanceFactory::CantCreateDir; } + auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg")); + m_settings->registerSetting(new Setting("InstanceType", "Legacy")); + QString inst_type = m_settings->get("InstanceType").toString(); + + if(inst_type == "OneSixFTB") + m_settings->set("InstanceType", "OneSix"); + if(inst_type == "LegacyFTB") + m_settings->set("InstanceType", "Legacy"); + auto error = loadInstance(newInstance, instDir); + switch (error) { case NoLoadError: diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 567004b9..0090c1d8 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -57,7 +57,10 @@ slots: emitFailed(tr("Couldn't load the version config")); return; } - if (!forge.apply(instance->getFullVersion())) + instance->revertCustomVersion(); + instance->customizeVersion(); + auto version = instance->getFullVersion(); + if (!forge.apply(version)) { emitFailed(tr("Couldn't install Forge")); return; diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 539413d8..0ecb387d 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -46,7 +46,8 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent) QDir::current().mkpath(m_instDir); } - connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this, &InstanceList::loadList); + connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this, + &InstanceList::loadList); } InstanceList::~InstanceList() @@ -281,6 +282,124 @@ void InstanceList::loadGroupList(QMap &groupMap) } } +struct FTBRecord +{ + QString dir; + QString name; + QString logo; + QString mcVersion; + QString description; +}; + +void InstanceList::loadForgeInstances(QMap groupMap) +{ + QList records; + QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString()); + QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString()); + if (!dir.exists()) + { + QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your " + "settings."; + return; + } + else if (!dataDir.exists()) + { + QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings"; + return; + } + + dir.cd("ModPacks"); + QFile f(dir.absoluteFilePath("modpacks.xml")); + if (!f.open(QFile::ReadOnly)) + return; + + // read the FTB packs XML. + QXmlStreamReader reader(&f); + while (!reader.atEnd()) + { + switch (reader.readNext()) + { + case QXmlStreamReader::StartElement: + { + if (reader.name() == "modpack") + { + QXmlStreamAttributes attrs = reader.attributes(); + FTBRecord record; + record.dir = attrs.value("dir").toString(); + record.name = attrs.value("name").toString(); + record.logo = attrs.value("logo").toString(); + record.mcVersion = attrs.value("mcVersion").toString(); + record.description = attrs.value("description").toString(); + records.append(record); + } + break; + } + case QXmlStreamReader::EndElement: + break; + case QXmlStreamReader::Characters: + break; + default: + break; + } + } + f.close(); + + // process the records we acquired. + for (auto record : records) + { + auto instanceDir = dataDir.absoluteFilePath(record.dir); + auto templateDir = dir.absoluteFilePath(record.dir); + if (!QFileInfo(instanceDir).exists()) + { + continue; + } + + QString iconKey = record.logo; + iconKey.remove(QRegularExpression("\\..*")); + MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo), true); + + if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists()) + { + BaseInstance *instPtr = NULL; + auto & factory = InstanceFactory::get(); + auto version = MMC->minecraftlist()->findVersion(record.mcVersion); + if (!version) + { + QLOG_ERROR() << "Can't load instance " << instanceDir + << " because minecraft version " << record.mcVersion + << " can't be resolved."; + continue; + } + auto error = factory.createInstance(instPtr, version, instanceDir, + InstanceFactory::FTBInstance); + + if (!instPtr || error != InstanceFactory::NoCreateError) + continue; + + instPtr->setGroupInitial("FTB"); + instPtr->setName(record.name); + instPtr->setIconKey(iconKey); + instPtr->setIntendedVersionId(record.mcVersion); + instPtr->setNotes(record.description); + continueProcessInstance(instPtr, error, instanceDir, groupMap); + } + else + { + BaseInstance *instPtr = NULL; + auto error = InstanceFactory::get().loadInstance(instPtr, instanceDir); + if (!instPtr || error != InstanceFactory::NoCreateError) + continue; + instPtr->setGroupInitial("FTB"); + instPtr->setName(record.name); + instPtr->setIconKey(iconKey); + if (instPtr->intendedVersionId() != record.mcVersion) + instPtr->setIntendedVersionId(record.mcVersion); + instPtr->setNotes(record.description); + continueProcessInstance(instPtr, error, instanceDir, groupMap); + } + } +} + InstanceList::InstListError InstanceList::loadList() { // load the instance groups @@ -306,69 +425,9 @@ InstanceList::InstListError InstanceList::loadList() } } - if (MMC->settings()->get("TrackFTBInstances").toBool() && MMC->minecraftlist()->isLoaded()) + if (MMC->settings()->get("TrackFTBInstances").toBool()) { - QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString()); - QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString()); - if (!dir.exists()) - { - QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your settings."; - } - else if (!dataDir.exists()) - { - QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings"; - } - else - { - dir.cd("ModPacks"); - QFile f(dir.absoluteFilePath("modpacks.xml")); - if (f.open(QFile::ReadOnly)) - { - QXmlStreamReader reader(&f); - while (!reader.atEnd()) - { - switch (reader.readNext()) - { - case QXmlStreamReader::StartElement: - { - if (reader.name() == "modpack") - { - QXmlStreamAttributes attrs = reader.attributes(); - const QDir instanceDir = QDir(dataDir.absoluteFilePath(attrs.value("dir").toString())); - if (instanceDir.exists()) - { - const QString name = attrs.value("name").toString(); - const QString iconKey = attrs.value("logo").toString().remove(QRegularExpression("\\..*")); - const QString mcVersion = attrs.value("mcVersion").toString(); - const QString notes = attrs.value("description").toString(); - QLOG_DEBUG() << dir.absoluteFilePath(attrs.value("logo").toString()); - MMC->icons()->addIcon(iconKey, iconKey, dir.absoluteFilePath(attrs.value("dir").toString() + QDir::separator() + attrs.value("logo").toString()), true); - - BaseInstance *instPtr = NULL; - auto error = InstanceFactory::get().createInstance(instPtr, MMC->minecraftlist()->findVersion(mcVersion), instanceDir.absolutePath(), InstanceFactory::FTBInstance); - if (instPtr && error == InstanceFactory::NoCreateError) - { - instPtr->setGroupInitial("FTB"); - instPtr->setName(name); - instPtr->setIconKey(iconKey); - instPtr->setIntendedVersionId(mcVersion); - instPtr->setNotes(notes); - } - continueProcessInstance(instPtr, error, instanceDir, groupMap); - } - } - break; - } - case QXmlStreamReader::EndElement: - break; - case QXmlStreamReader::Characters: - break; - default: - break; - } - } - } - } + loadForgeInstances(groupMap); } endResetModel(); @@ -444,13 +503,14 @@ int InstanceList::getInstIndex(BaseInstance *inst) const return -1; } -void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap) +void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error, + const QDir &dir, QMap &groupMap) { if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance) { QString errorMsg = QString("Failed to load instance %1: ") - .arg(QFileInfo(dir.absolutePath()).baseName()) - .toUtf8(); + .arg(QFileInfo(dir.absolutePath()).baseName()) + .toUtf8(); switch (error) { @@ -463,8 +523,8 @@ void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int erro else if (!instPtr) { QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.") - .arg(QFileInfo(dir.absolutePath()).baseName()) - .toUtf8(); + .arg(QFileInfo(dir.absolutePath()).baseName()) + .toUtf8(); } else { diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h index b3ee6cfe..0ce808e5 100644 --- a/logic/lists/InstanceList.h +++ b/logic/lists/InstanceList.h @@ -109,6 +109,7 @@ slots: * \brief Loads the instance list. Triggers notifications. */ InstListError loadList(); + void loadForgeInstances(QMap groupMap); private slots: @@ -119,7 +120,8 @@ slots: private: int getInstIndex(BaseInstance *inst) const; - void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap); + void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, + QMap &groupMap); protected: QString m_instDir; -- cgit v1.2.3 From 245d441a6eee558557a01d61a726b7feaeb5dc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 22 Dec 2013 05:15:26 +0100 Subject: Fail if we fail to find the right forge version --- logic/OneSixFTBInstance.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'logic') diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 0090c1d8..924fc790 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -27,7 +27,10 @@ public: } } if (!forgeVersion) + { + emitFailed(QString("Couldn't find forge version ") + version ); return; + } entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename); if (entry->stale) { -- cgit v1.2.3 From 7a07ed79407edcb2a543aa0dc80745a0b8c2e234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 22 Dec 2013 05:47:10 +0100 Subject: FTB fixage * Corrected an uninitialized variable that prevented forge list loadinf on Windows * Run the update step twice for FTB instances to ensure forge libs get downloaded --- logic/OneSixFTBInstance.cpp | 3 +++ logic/lists/ForgeVersionList.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 924fc790..4bb5cf42 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -111,6 +111,9 @@ std::shared_ptr OneSixFTBInstance::doUpdate(bool only_prepare) } task->addTask(OneSixInstance::doUpdate(only_prepare)); task->addTask(std::shared_ptr(new OneSixFTBInstanceForge(m_forge->version(), this, this))); + //FIXME: yes. this may appear dumb. but the previous step can change the list, so we do it all again. + //TODO: Add a graph task. Construct graphs of tasks so we may capture the logic properly. + task->addTask(OneSixInstance::doUpdate(only_prepare)); return task; } diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h index bf9e87b2..f32975ed 100644 --- a/logic/lists/ForgeVersionList.h +++ b/logic/lists/ForgeVersionList.h @@ -80,7 +80,7 @@ public: protected: QList m_vlist; - bool m_loaded; + bool m_loaded = false; protected slots: -- cgit v1.2.3 From 3051d0d3283656baafe6021e5036fdca9db0c4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 22 Dec 2013 18:49:52 +0100 Subject: Make pack200 use QFile by proxy, eliminating some unicode issues. --- logic/net/ForgeXzDownload.cpp | 45 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'logic') diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index 83cbabd0..359ad858 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -311,18 +311,51 @@ void ForgeXzDownload::decompressAndInstall() m_pack200_xz_file.remove(); // revert pack200 - pack200_file.close(); - QString pack_name = pack200_file.fileName(); - QString source_native = QDir::toNativeSeparators(pack_name); - QString target_native = QDir::toNativeSeparators(m_target_path); + pack200_file.seek(0); + int handle_in = pack200_file.handle(); + // FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects. + if(handle_in == -1) + { + QLOG_ERROR() << "Error reopening " << pack200_file.fileName(); + failAndTryNextMirror(); + return; + } + FILE * file_in = fdopen(handle_in,"r"); + if(!file_in) + { + QLOG_ERROR() << "Error reopening " << pack200_file.fileName(); + failAndTryNextMirror(); + return; + } + QFile qfile_out(m_target_path); + if(!qfile_out.open(QIODevice::WriteOnly)) + { + QLOG_ERROR() << "Error opening " << qfile_out.fileName(); + failAndTryNextMirror(); + return; + } + int handle_out = qfile_out.handle(); + if(handle_out == -1) + { + QLOG_ERROR() << "Error opening " << qfile_out.fileName(); + failAndTryNextMirror(); + return; + } + FILE * file_out = fdopen(handle_out,"w"); + if(!file_out) + { + QLOG_ERROR() << "Error opening " << qfile_out.fileName(); + failAndTryNextMirror(); + return; + } try { - unpack_200(source_native.toStdString(), target_native.toStdString()); + unpack_200(file_in, file_out); } catch (std::runtime_error &err) { m_status = Job_Failed; - QLOG_ERROR() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what(); + QLOG_ERROR() << "Error unpacking " << pack200_file.fileName() << " : " << err.what(); QFile f(m_target_path); if (f.exists()) f.remove(); -- cgit v1.2.3 From 77ddf8b5d79caf0176ac2647e539272ee3e07d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 22 Dec 2013 19:47:58 +0100 Subject: Show errors when logging in in the account dialog. --- logic/auth/MojangAccount.cpp | 26 ++++++++++++++------------ logic/auth/MojangAccount.h | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'logic') diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index bc6af98f..a462eda5 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -32,7 +32,8 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) // The JSON object must at least have a username for it to be valid. if (!object.value("username").isString()) { - QLOG_ERROR() << "Can't load Mojang account info from JSON object. Username field is missing or of the wrong type."; + QLOG_ERROR() << "Can't load Mojang account info from JSON object. Username field is " + "missing or of the wrong type."; return nullptr; } @@ -43,7 +44,8 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) QJsonArray profileArray = object.value("profiles").toArray(); if (profileArray.size() < 1) { - QLOG_ERROR() << "Can't load Mojang account with username \"" << username << "\". No profiles found."; + QLOG_ERROR() << "Can't load Mojang account with username \"" << username + << "\". No profiles found."; return nullptr; } @@ -63,7 +65,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) } MojangAccountPtr account(new MojangAccount()); - if(object.value("user").isObject()) + if (object.value("user").isObject()) { User u; QJsonObject userStructure = object.value("user").toObject(); @@ -92,7 +94,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) return account; } -MojangAccountPtr MojangAccount::createFromUsername(const QString& username) +MojangAccountPtr MojangAccount::createFromUsername(const QString &username) { MojangAccountPtr account(new MojangAccount()); account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); @@ -152,27 +154,27 @@ bool MojangAccount::setCurrentProfile(const QString &profileId) return false; } -const AccountProfile* MojangAccount::currentProfile() const +const AccountProfile *MojangAccount::currentProfile() const { - if(m_currentProfile == -1) + if (m_currentProfile == -1) return nullptr; return &m_profiles[m_currentProfile]; } AccountStatus MojangAccount::accountStatus() const { - if(m_accessToken.isEmpty()) + if (m_accessToken.isEmpty()) return NotVerified; - if(!m_online) + if (!m_online) return Verified; return Online; } -std::shared_ptr MojangAccount::login(QString password) +std::shared_ptr MojangAccount::login(QString password) { - if(m_currentTask) + if (m_currentTask) return m_currentTask; - if(password.isEmpty()) + if (password.isEmpty()) { m_currentTask.reset(new RefreshTask(this)); } @@ -196,7 +198,7 @@ void MojangAccount::authFailed(QString reason) { // This is emitted when the yggdrasil tasks time out or are cancelled. // -> we treat the error as no-op - if(reason != "Yggdrasil task cancelled.") + if (reason != "Yggdrasil task cancelled.") { m_online = false; m_accessToken = QString(); diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index 325aa826..dd5d54ae 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -95,7 +95,7 @@ public: /* manipulation */ * Attempt to login. Empty password means we use the token. * If the attempt fails because we already are performing some task, it returns false. */ - std::shared_ptr login(QString password = QString()); + std::shared_ptr login(QString password = QString()); void downgrade() { -- cgit v1.2.3 From 3841260ef1f4e6d7e30e68d1ccae09bfbf176ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 22 Dec 2013 23:05:18 +0100 Subject: Fix Java checker leaving behind temporary jar files --- logic/JavaChecker.cpp | 2 +- logic/lists/JavaVersionList.cpp | 11 ++++++----- logic/lists/JavaVersionList.h | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'logic') diff --git a/logic/JavaChecker.cpp b/logic/JavaChecker.cpp index 2b94fbb6..113974ff 100644 --- a/logic/JavaChecker.cpp +++ b/logic/JavaChecker.cpp @@ -99,6 +99,7 @@ void JavaChecker::error(QProcess::ProcessError err) if(err == QProcess::FailedToStart) { killTimer.stop(); + checkerJar.remove(); JavaCheckResult result; { @@ -116,6 +117,5 @@ void JavaChecker::timeout() if(process) { process->kill(); - process.reset(); } } diff --git a/logic/lists/JavaVersionList.cpp b/logic/lists/JavaVersionList.cpp index d2f0972c..c2886c67 100644 --- a/logic/lists/JavaVersionList.cpp +++ b/logic/lists/JavaVersionList.cpp @@ -177,9 +177,9 @@ void JavaListLoadTask::executeTask() JavaUtils ju; QList candidate_paths = ju.FindJavaPaths(); - auto job = new JavaCheckerJob("Java detection"); - connect(job, SIGNAL(finished(QList)), this, SLOT(javaCheckerFinished(QList))); - connect(job, SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int))); + m_job = std::shared_ptr(new JavaCheckerJob("Java detection")); + connect(m_job.get(), SIGNAL(finished(QList)), this, SLOT(javaCheckerFinished(QList))); + connect(m_job.get(), SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int))); QLOG_DEBUG() << "Probing the following Java paths: "; for(QString candidate : candidate_paths) @@ -188,10 +188,10 @@ void JavaListLoadTask::executeTask() auto candidate_checker = new JavaChecker(); candidate_checker->path = candidate; - job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); + m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); } - job->start(); + m_job->start(); } void JavaListLoadTask::checkerProgress(int current, int total) @@ -203,6 +203,7 @@ void JavaListLoadTask::checkerProgress(int current, int total) void JavaListLoadTask::javaCheckerFinished(QList results) { QList candidates; + m_job.reset(); QLOG_DEBUG() << "Found the following valid Java installations:"; for(JavaCheckResult result : results) diff --git a/logic/lists/JavaVersionList.h b/logic/lists/JavaVersionList.h index 879b2480..e6cc8e5f 100644 --- a/logic/lists/JavaVersionList.h +++ b/logic/lists/JavaVersionList.h @@ -90,6 +90,7 @@ public slots: void checkerProgress(int current, int total); protected: + std::shared_ptr m_job; JavaVersionList *m_list; JavaVersion *m_currentRecommended; }; -- cgit v1.2.3 From 9e645f4a37e73857b1414a67700e9f3c4cf61d56 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 23 Dec 2013 00:12:03 +0100 Subject: Support for the new forge gradle repo --- logic/lists/ForgeVersionList.cpp | 201 +++++++++++++++++++++++++++++++++------ logic/lists/ForgeVersionList.h | 12 ++- 2 files changed, 184 insertions(+), 29 deletions(-) (limited to 'logic') diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index d6d353da..b6e06e77 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -24,6 +24,7 @@ #include "logger/QsLog.h" #define JSON_URL "http://files.minecraftforge.net/minecraftforge/json" +#define GRADLE_JSON_URL "http://files.minecraftforge.net/maven/net/minecraftforge/forge/json" ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent) { @@ -163,41 +164,37 @@ void ForgeListLoadTask::executeTask() auto job = new NetJob("Version index"); // we do not care if the version is stale or not. auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); + auto gradleForgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "json"); // verify by poking the server. forgeListEntry->stale = true; + gradleForgeListEntry->stale = true; + + job->addNetAction(listDownload = CacheDownload::make(QUrl(JSON_URL), forgeListEntry)); + job->addNetAction(gradleListDownload = CacheDownload::make(QUrl(GRADLE_JSON_URL), gradleForgeListEntry)); + + connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed())); + connect(gradleListDownload.get(), SIGNAL(failed(int)), SLOT(gradleListFailed())); - job->addNetAction(CacheDownload::make(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(succeeded()), SLOT(listDownloaded())); 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() +bool ForgeListLoadTask::parseForgeList(QList &out) { QByteArray data; { - auto DlJob = listJob->first(); - auto filename = std::dynamic_pointer_cast(DlJob)->m_target_path; + auto dlJob = listDownload; + auto filename = std::dynamic_pointer_cast(dlJob)->m_target_path; QFile listFile(filename); if (!listFile.open(QIODevice::ReadOnly)) - return; + { + return false; + } data = listFile.readAll(); - DlJob.reset(); + dlJob.reset(); } QJsonParseError jsonError; @@ -206,13 +203,13 @@ void ForgeListLoadTask::list_downloaded() if (jsonError.error != QJsonParseError::NoError) { emitFailed("Error parsing version list JSON:" + jsonError.errorString()); - return; + return false; } if (!jsonDoc.isObject()) { - emitFailed("Error parsing version list JSON: jsonDoc is not an object"); - return; + emitFailed("Error parsing version list JSON: JSON root is not an object"); + return false; } QJsonObject root = jsonDoc.object(); @@ -222,11 +219,10 @@ void ForgeListLoadTask::list_downloaded() { emitFailed( "Error parsing version list JSON: version list object is missing 'builds' array"); - return; + return false; } QJsonArray builds = root.value("builds").toArray(); - QList tempList; for (int i = 0; i < builds.count(); i++) { // Load the version info. @@ -247,7 +243,9 @@ void ForgeListLoadTask::list_downloaded() 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) @@ -263,7 +261,9 @@ void ForgeListLoadTask::list_downloaded() { QString ext = file.value("ext").toString(); if (ext.isEmpty()) + { continue; + } changelog_url = file.value("url").toString(); } else if (buildtype == "installer") @@ -283,15 +283,162 @@ void ForgeListLoadTask::list_downloaded() 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); + out.append(fVersion); + } + } + + return true; +} + +bool ForgeListLoadTask::parseForgeGradleList(QList &out) +{ + QByteArray data; + { + auto dlJob = gradleListDownload; + auto filename = std::dynamic_pointer_cast(dlJob)->m_target_path; + QFile listFile(filename); + if (!listFile.open(QIODevice::ReadOnly)) + { + return false; + } + data = listFile.readAll(); + dlJob.reset(); + } + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + + if (jsonError.error != QJsonParseError::NoError) + { + emitFailed("Error parsing gradle version list JSON:" + jsonError.errorString()); + return false; + } + + if (!jsonDoc.isObject()) + { + emitFailed("Error parsing gradle version list JSON: JSON root is not an object"); + return false; + } + + QJsonObject root = jsonDoc.object(); + + // we probably could hard code these, but it might still be worth doing it this way + const QString webpath = root.value("webpath").toString(); + const QString artifact = root.value("artifact").toString(); + + QJsonObject numbers = root.value("number").toObject(); + for (auto it = numbers.begin(); it != numbers.end(); ++it) + { + QJsonObject number = it.value().toObject(); + std::shared_ptr fVersion(new ForgeVersion()); + fVersion->m_buildnr = number.value("build").toDouble(); + fVersion->jobbuildver = number.value("version").toString(); + fVersion->mcver = number.value("mcversion").toString(); + fVersion->filename = ""; + QString filename, installer_filename; + QJsonArray files = number.value("files").toArray(); + for (auto fIt = files.begin(); fIt != files.end(); ++fIt) + { + // TODO with gradle we also get checksums, use them + QJsonArray file = (*fIt).toArray(); + if (file.size() < 3) + { + continue; + } + if (file.at(1).toString() == "installer") + { + fVersion->installer_url = + QString("%1/%2-%3/%4-%2-%3-installer.%5") + .arg(webpath, fVersion->mcver, fVersion->jobbuildver, artifact, file.at(0).toString()); + installer_filename = QString("%1-%2-%3-installer.%4") + .arg(artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString()); + } + else if (file.at(1).toString() == "universal") + { + fVersion->universal_url = + QString("%1/%2-%3/%4-%2-%3-universal.%5") + .arg(webpath, fVersion->mcver, fVersion->jobbuildver, artifact, file.at(0).toString()); + filename = QString("%1-%2-%3-universal.%4") + .arg(artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString()); + } + else if (file.at(1).toString() == "changelog") + { + fVersion->changelog_url = + QString("%1/%2-%3/%4-%2-%3-changelog.%5") + .arg(webpath, fVersion->mcver, fVersion->jobbuildver, artifact, file.at(0).toString()); + } } + if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty()) + { + continue; + } + fVersion->filename = fVersion->installer_url.isEmpty() ? + filename : installer_filename; + out.append(fVersion); + } + + return true; +} + +void ForgeListLoadTask::listDownloaded() +{ + QList list; + bool ret = true; + if (!parseForgeList(list)) + { + ret = false; + } + if (!parseForgeGradleList(list)) + { + ret = false; + } + + if (!ret) + { + return; } - m_list->updateListData(tempList); + + qSort(list.begin(), list.end(), + [](const BaseVersionPtr &p1, const BaseVersionPtr &p2) { + // TODO better comparison (takes major/minor/build number into account) + return p1->name() > p2->name(); + }); + + m_list->updateListData(list); emitSucceeded(); return; } + +void ForgeListLoadTask::listFailed() +{ + auto reply = listDownload->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::gradleListFailed() +{ + auto reply = gradleListDownload->m_reply; + if (reply) + { + QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString(); + } + else + { + QLOG_ERROR() << "Getting forge version list failed for reasons unknown."; + } +} diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h index f32975ed..924084ae 100644 --- a/logic/lists/ForgeVersionList.h +++ b/logic/lists/ForgeVersionList.h @@ -98,10 +98,18 @@ public: protected slots: - void list_downloaded(); - void list_failed(); + void listDownloaded(); + void listFailed(); + void gradleListFailed(); protected: NetJobPtr listJob; ForgeVersionList *m_list; + + CacheDownloadPtr listDownload; + CacheDownloadPtr gradleListDownload; + +private: + bool parseForgeList(QList &out); + bool parseForgeGradleList(QList &out); }; -- cgit v1.2.3 From f402001453c4053d1c8c56b63896796d63df0388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 23 Dec 2013 00:43:29 +0100 Subject: Use the central URL list for forge URLs --- logic/lists/ForgeVersionList.cpp | 43 ++++++++++++++++++++-------------------- logic/net/URLConstants.h | 2 ++ 2 files changed, 23 insertions(+), 22 deletions(-) (limited to 'logic') diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index b6e06e77..78cb0de0 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -15,6 +15,7 @@ #include "ForgeVersionList.h" #include +#include #include "MultiMC.h" #include @@ -23,9 +24,6 @@ #include "logger/QsLog.h" -#define JSON_URL "http://files.minecraftforge.net/minecraftforge/json" -#define GRADLE_JSON_URL "http://files.minecraftforge.net/maven/net/minecraftforge/forge/json" - ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent) { } @@ -170,8 +168,10 @@ void ForgeListLoadTask::executeTask() forgeListEntry->stale = true; gradleForgeListEntry->stale = true; - job->addNetAction(listDownload = CacheDownload::make(QUrl(JSON_URL), forgeListEntry)); - job->addNetAction(gradleListDownload = CacheDownload::make(QUrl(GRADLE_JSON_URL), gradleForgeListEntry)); + job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::FORGE_LEGACY_URL), + forgeListEntry)); + job->addNetAction(gradleListDownload = CacheDownload::make( + QUrl(URLConstants::FORGE_GRADLE_URL), gradleForgeListEntry)); connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed())); connect(gradleListDownload.get(), SIGNAL(failed(int)), SLOT(gradleListFailed())); @@ -355,33 +355,32 @@ bool ForgeListLoadTask::parseForgeGradleList(QList &out) } if (file.at(1).toString() == "installer") { - fVersion->installer_url = - QString("%1/%2-%3/%4-%2-%3-installer.%5") - .arg(webpath, fVersion->mcver, fVersion->jobbuildver, artifact, file.at(0).toString()); - installer_filename = QString("%1-%2-%3-installer.%4") - .arg(artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString()); + fVersion->installer_url = QString("%1/%2-%3/%4-%2-%3-installer.%5").arg( + webpath, fVersion->mcver, fVersion->jobbuildver, artifact, + file.at(0).toString()); + installer_filename = QString("%1-%2-%3-installer.%4").arg( + artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString()); } else if (file.at(1).toString() == "universal") { - fVersion->universal_url = - QString("%1/%2-%3/%4-%2-%3-universal.%5") - .arg(webpath, fVersion->mcver, fVersion->jobbuildver, artifact, file.at(0).toString()); - filename = QString("%1-%2-%3-universal.%4") - .arg(artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString()); + fVersion->universal_url = QString("%1/%2-%3/%4-%2-%3-universal.%5").arg( + webpath, fVersion->mcver, fVersion->jobbuildver, artifact, + file.at(0).toString()); + filename = QString("%1-%2-%3-universal.%4").arg( + artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString()); } else if (file.at(1).toString() == "changelog") { - fVersion->changelog_url = - QString("%1/%2-%3/%4-%2-%3-changelog.%5") - .arg(webpath, fVersion->mcver, fVersion->jobbuildver, artifact, file.at(0).toString()); + fVersion->changelog_url = QString("%1/%2-%3/%4-%2-%3-changelog.%5").arg( + webpath, fVersion->mcver, fVersion->jobbuildver, artifact, + file.at(0).toString()); } } if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty()) { continue; } - fVersion->filename = fVersion->installer_url.isEmpty() ? - filename : installer_filename; + fVersion->filename = fVersion->installer_url.isEmpty() ? filename : installer_filename; out.append(fVersion); } @@ -406,8 +405,8 @@ void ForgeListLoadTask::listDownloaded() return; } - qSort(list.begin(), list.end(), - [](const BaseVersionPtr &p1, const BaseVersionPtr &p2) { + qSort(list.begin(), list.end(), [](const BaseVersionPtr & p1, const BaseVersionPtr & p2) + { // TODO better comparison (takes major/minor/build number into account) return p1->name() > p2->name(); }); diff --git a/logic/net/URLConstants.h b/logic/net/URLConstants.h index dcd5c2b1..9579198d 100644 --- a/logic/net/URLConstants.h +++ b/logic/net/URLConstants.h @@ -29,4 +29,6 @@ const QString RESOURCE_BASE("resources.download.minecraft.net/"); const QString LIBRARY_BASE("libraries.minecraft.net/"); const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/"); const QString AUTH_BASE("authserver.mojang.com/"); +const QString FORGE_LEGACY_URL("http://files.minecraftforge.net/minecraftforge/json"); +const QString FORGE_GRADLE_URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/json"); } -- cgit v1.2.3 From 00822fa0f9d3cd93e460c992aac77693ac00a741 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 23 Dec 2013 10:34:43 +0100 Subject: Treat the updater separately --- logic/updater/DownloadUpdateTask.cpp | 31 +++++++++++++++++++++++++++++-- logic/updater/DownloadUpdateTask.h | 2 ++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index b017afeb..6157608f 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -377,6 +377,9 @@ DownloadUpdateTask::processFileLists(NetJob *job, // yep. this file actually needs an upgrade. PROCEED. QLOG_DEBUG() << "Found file" << entry.path << " that needs updating."; + // if it's the updater we want to treat it separately + bool isUpdater = entry.path.endsWith("updater") || entry.path.endsWith("updater.exe"); + // Go through the sources list and find one to use. // TODO: Make a NetAction that takes a source list and tries each of them until one // works. For now, we'll just use the first http one. @@ -398,10 +401,19 @@ DownloadUpdateTask::processFileLists(NetJob *job, auto download = MD5EtagDownload::make(source.url, dlPath); download->m_expected_md5 = entry.md5; job->addNetAction(download); + + if (isUpdater) + { + download->setProperty("finalPath", entry.path); + connect(download.get(), &MD5EtagDownload::succeeded, this, &DownloadUpdateTask::directDeployFile); + } } - // Now add a copy operation to our operations list to install the file. - ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + if (!isUpdater) + { + // Now add a copy operation to our operations list to install the file. + ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + } } } } @@ -512,6 +524,21 @@ void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 tota setProgress((int)(((float)current / (float)total) * 100)); } +void DownloadUpdateTask::directDeployFile(const int index) +{ + Md5EtagDownloadPtr download = std::dynamic_pointer_cast(m_filesNetJob->operator[](index)); + const QString finalPath = download->property("finalPath").toString(); + QLOG_INFO() << "Replacing" << finalPath << "with" << download->m_output_file.fileName(); + if (QFile::remove(finalPath)) + { + if (download->m_output_file.copy(finalPath)) + { + return; + } + } + emitFailed("Couldn't copy updater files"); +} + QString DownloadUpdateTask::updateFilesDir() { return m_updateFilesDir.path(); diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 1fc14049..79d73af3 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -207,5 +207,7 @@ protected slots: void fileDownloadFinished(); void fileDownloadFailed(); void fileDownloadProgressChanged(qint64 current, qint64 total); + + void directDeployFile(const int index); }; -- cgit v1.2.3 From 027aafc3c1fc5e78c91ee439cd38562387f7ed9f Mon Sep 17 00:00:00 2001 From: Sky Date: Mon, 23 Dec 2013 15:46:01 +0000 Subject: Tidy status messages a bit --- logic/LegacyUpdate.cpp | 18 +++++++++--------- logic/OneSixUpdate.cpp | 14 +++++++------- logic/auth/YggdrasilTask.cpp | 6 +++--- logic/auth/flows/AuthenticateTask.cpp | 4 ++-- logic/auth/flows/RefreshTask.cpp | 4 ++-- logic/auth/flows/ValidateTask.cpp | 4 ++-- logic/lists/ForgeVersionList.cpp | 2 +- logic/lists/JavaVersionList.cpp | 2 +- logic/lists/MinecraftVersionList.cpp | 2 +- logic/updater/DownloadUpdateTask.cpp | 16 +++++++--------- 10 files changed, 35 insertions(+), 37 deletions(-) (limited to 'logic') diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index e71b270e..fb9dcf2b 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -76,7 +76,7 @@ void LegacyUpdate::lwjglStart() return; } - setStatus("Downloading new LWJGL."); + setStatus(tr("Downloading new LWJGL...")); auto version = list->getVersion(lwjglVersion); if (!version) { @@ -144,7 +144,7 @@ void LegacyUpdate::lwjglFinished(QNetworkReply *reply) saveMe.open(QIODevice::WriteOnly); saveMe.write(m_reply->readAll()); saveMe.close(); - setStatus("Installing new LWJGL..."); + setStatus(tr("Installing new LWJGL...")); extractLwjgl(); jarStart(); } @@ -220,7 +220,7 @@ void LegacyUpdate::extractLwjgl() // Now if destFileName is still empty, go to the next file. if (!destFileName.isEmpty()) { - setStatus("Installing new LWJGL - Extracting " + name); + setStatus(tr("Installing new LWJGL - extracting ") + name + "..."); QFile output(destFileName); output.open(QIODevice::WriteOnly); output.write(file.readAll()); // FIXME: wste of memory!? @@ -250,7 +250,7 @@ void LegacyUpdate::jarStart() return; } - setStatus("Checking for jar updates..."); + setStatus(tr("Checking for jar updates...")); // Make directories QDir binDir(inst->binDir()); if (!binDir.exists() && !binDir.mkpath(".")) @@ -260,7 +260,7 @@ void LegacyUpdate::jarStart() } // Build a list of URLs that will need to be downloaded. - setStatus("Downloading new minecraft.jar"); + setStatus(tr("Downloading new minecraft.jar ...")); QString version_id = inst->intendedVersionId(); QString localPath = version_id + "/" + version_id + ".jar"; @@ -294,7 +294,7 @@ void LegacyUpdate::jarFailed() bool LegacyUpdate::MergeZipFiles(QuaZip *into, QFileInfo from, QSet &contained, MetainfAction metainf) { - setStatus("Installing mods - Adding " + from.fileName()); + setStatus(tr("Installing mods: Adding ") + from.fileName() + " ..."); QuaZip modZip(from.filePath()); modZip.open(QuaZip::mdUnzip); @@ -380,7 +380,7 @@ void LegacyUpdate::ModTheJar() return; } - setStatus("Installing mods - backing up minecraft.jar..."); + setStatus(tr("Installing mods: Backing up minecraft.jar ...")); if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath())) { emitFailed("It seems both the active and base jar are gone. A fresh base jar will " @@ -405,7 +405,7 @@ void LegacyUpdate::ModTheJar() } // TaskStep(); // STEP 1 - setStatus("Installing mods - Opening minecraft.jar"); + setStatus(tr("Installing mods: Opening minecraft.jar ...")); QuaZip zipOut(runnableJar.filePath()); if (!zipOut.open(QuaZip::mdCreate)) @@ -419,7 +419,7 @@ void LegacyUpdate::ModTheJar() QSet addedFiles; // Modify the jar - setStatus("Installing mods - Adding mod files..."); + setStatus(tr("Installing mods: Adding mod files...")); for (int i = modList->size() - 1; i >= 0; i--) { auto &mod = modList->operator[](i); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 696eeff0..4d93477a 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -57,7 +57,7 @@ void OneSixUpdate::executeTask() /* * FIXME: in offline mode, do not proceed! */ - setStatus("Testing the Java installation."); + setStatus(tr("Testing the Java installation...")); QString java_path = m_inst->settings().get("JavaPath").toString(); checker.reset(new JavaChecker()); @@ -89,7 +89,7 @@ void OneSixUpdate::executeTask() void OneSixUpdate::checkJavaOnline() { - setStatus("Testing the Java installation."); + setStatus(tr("Testing the Java installation...")); QString java_path = m_inst->settings().get("JavaPath").toString(); checker.reset(new JavaChecker()); @@ -128,7 +128,7 @@ void OneSixUpdate::checkFinishedOffline(JavaCheckResult result) void OneSixUpdate::versionFileStart() { QLOG_INFO() << m_inst->name() << ": getting version file."; - setStatus("Getting the version files from Mojang."); + setStatus(tr("Getting the version files from Mojang...")); QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; auto job = new NetJob("Version index"); @@ -196,7 +196,7 @@ void OneSixUpdate::versionFileFailed() void OneSixUpdate::assetIndexStart() { - setStatus("Updating asset index."); + setStatus(tr("Updating assets index...")); OneSixInstance *inst = (OneSixInstance *)m_inst; std::shared_ptr version = inst->getFullVersion(); QString assetName = version->assets; @@ -247,7 +247,7 @@ void OneSixUpdate::assetIndexFinished() } if(dls.size()) { - setStatus("Getting the assets files from Mojang..."); + setStatus(tr("Getting the assets files from Mojang...")); auto job = new NetJob("Assets for " + inst->name()); for(auto dl: dls) job->addNetAction(dl); @@ -281,7 +281,7 @@ void OneSixUpdate::assetsFailed() void OneSixUpdate::jarlibStart() { - setStatus("Getting the library files from Mojang."); + setStatus(tr("Getting the library files from Mojang...")); QLOG_INFO() << m_inst->name() << ": downloading libraries"; OneSixInstance *inst = (OneSixInstance *)m_inst; bool successful = inst->reloadFullVersion(); @@ -369,7 +369,7 @@ void OneSixUpdate::jarlibFailed() void OneSixUpdate::prepareForLaunch() { - setStatus("Preparing for launch."); + setStatus(tr("Preparing for launch...")); QLOG_INFO() << m_inst->name() << ": preparing for launch"; auto onesix_inst = (OneSixInstance *)m_inst; diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp index 088e1fc0..573dd57a 100644 --- a/logic/auth/YggdrasilTask.cpp +++ b/logic/auth/YggdrasilTask.cpp @@ -172,10 +172,10 @@ QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const switch (state) { case STATE_SENDING_REQUEST: - return tr("Sending request to auth servers."); + return tr("Sending request to auth servers..."); case STATE_PROCESSING_RESPONSE: - return tr("Processing response from servers."); + return tr("Processing response from servers..."); default: - return tr("Processing. Please wait."); + return tr("Processing. Please wait..."); } } diff --git a/logic/auth/flows/AuthenticateTask.cpp b/logic/auth/flows/AuthenticateTask.cpp index f60be35d..6548c4e9 100644 --- a/logic/auth/flows/AuthenticateTask.cpp +++ b/logic/auth/flows/AuthenticateTask.cpp @@ -194,9 +194,9 @@ QString AuthenticateTask::getStateMessage(const YggdrasilTask::State state) cons switch (state) { case STATE_SENDING_REQUEST: - return tr("Authenticating: Sending request."); + return tr("Authenticating: Sending request..."); case STATE_PROCESSING_RESPONSE: - return tr("Authenticating: Processing response."); + return tr("Authenticating: Processing response..."); default: return YggdrasilTask::getStateMessage(state); } diff --git a/logic/auth/flows/RefreshTask.cpp b/logic/auth/flows/RefreshTask.cpp index 5f68ccc7..f63c736e 100644 --- a/logic/auth/flows/RefreshTask.cpp +++ b/logic/auth/flows/RefreshTask.cpp @@ -145,9 +145,9 @@ QString RefreshTask::getStateMessage(const YggdrasilTask::State state) const switch (state) { case STATE_SENDING_REQUEST: - return tr("Refreshing login token."); + return tr("Refreshing login token..."); case STATE_PROCESSING_RESPONSE: - return tr("Refreshing login token: Processing response."); + return tr("Refreshing login token: Processing response..."); default: return YggdrasilTask::getStateMessage(state); } diff --git a/logic/auth/flows/ValidateTask.cpp b/logic/auth/flows/ValidateTask.cpp index 84d5e703..4f7323fd 100644 --- a/logic/auth/flows/ValidateTask.cpp +++ b/logic/auth/flows/ValidateTask.cpp @@ -55,9 +55,9 @@ QString ValidateTask::getStateMessage(const YggdrasilTask::State state) const switch (state) { case STATE_SENDING_REQUEST: - return tr("Validating Access Token: Sending request."); + return tr("Validating access token: Sending request..."); case STATE_PROCESSING_RESPONSE: - return tr("Validating Access Token: Processing response."); + return tr("Validating access token: Processing response..."); default: return YggdrasilTask::getStateMessage(state); } diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index 78cb0de0..56eca744 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -158,7 +158,7 @@ ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task() void ForgeListLoadTask::executeTask() { - setStatus(tr("Fetching Forge version list")); + setStatus(tr("Fetching Forge version lists...")); auto job = new NetJob("Version index"); // we do not care if the version is stale or not. auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); diff --git a/logic/lists/JavaVersionList.cpp b/logic/lists/JavaVersionList.cpp index c2886c67..e8c5acd0 100644 --- a/logic/lists/JavaVersionList.cpp +++ b/logic/lists/JavaVersionList.cpp @@ -172,7 +172,7 @@ JavaListLoadTask::~JavaListLoadTask() void JavaListLoadTask::executeTask() { - setStatus("Detecting Java installations..."); + setStatus(tr("Detecting Java installations...")); JavaUtils ju; QList candidate_paths = ju.FindJavaPaths(); diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 523b81ac..91f86df0 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -139,7 +139,7 @@ MCVListLoadTask::~MCVListLoadTask() void MCVListLoadTask::executeTask() { - setStatus("Loading instance version list..."); + setStatus(tr("Loading instance version list...")); auto worker = MMC->qnam(); vlistReply = worker->get(QNetworkRequest(QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json"))); connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded())); diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index b017afeb..cffac75f 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -77,7 +77,7 @@ void DownloadUpdateTask::processChannels() void DownloadUpdateTask::findCurrentVersionInfo() { - setStatus(tr("Finding information about the current version.")); + setStatus(tr("Finding information about the current version...")); auto checker = MMC->updateChecker(); @@ -98,7 +98,7 @@ void DownloadUpdateTask::findCurrentVersionInfo() void DownloadUpdateTask::loadVersionInfo() { - setStatus(tr("Loading version information.")); + setStatus(tr("Loading version information...")); // Create the net job for loading version info. NetJob *netJob = new NetJob("Version Info"); @@ -153,10 +153,8 @@ void DownloadUpdateTask::vinfoDownloadFailed() void DownloadUpdateTask::parseDownloadedVersionInfo() { - setStatus(tr("Reading file lists.")); - - setStatus(tr("Reading file list for new version.")); - QLOG_DEBUG() << "Reading file list for new version."; + setStatus(tr("Reading file list for new version...")); + QLOG_DEBUG() << "Reading file list for new version..."; QString error; if (!parseVersionInfo( std::dynamic_pointer_cast(m_vinfoNetJob->first())->m_data, @@ -170,8 +168,8 @@ void DownloadUpdateTask::parseDownloadedVersionInfo() // info. if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed) { - setStatus(tr("Reading file list for current version.")); - QLOG_DEBUG() << "Reading file list for current version."; + setStatus(tr("Reading file list for current version...")); + QLOG_DEBUG() << "Reading file list for current version..."; QString error; parseVersionInfo( std::dynamic_pointer_cast(m_vinfoNetJob->operator[](1))->m_data, @@ -278,7 +276,7 @@ DownloadUpdateTask::processFileLists(NetJob *job, const DownloadUpdateTask::VersionFileList &newVersion, DownloadUpdateTask::UpdateOperationList &ops) { - setStatus(tr("Processing file lists. Figuring out how to install the update.")); + setStatus(tr("Processing file lists - figuring out how to install the update...")); // First, if we've loaded the current version's file list, we need to iterate through it and // delete anything in the current one version's list that isn't in the new version's list. -- cgit v1.2.3 From b1ec7841e04f2a60f54895e1646bc33486fd9fbf Mon Sep 17 00:00:00 2001 From: robotbrainify Date: Tue, 24 Dec 2013 16:00:07 -0500 Subject: Get the updater to display a no update found message. --- logic/updater/UpdateChecker.cpp | 71 +++++++++++++++++++++++++---------------- logic/updater/UpdateChecker.h | 6 ++-- 2 files changed, 48 insertions(+), 29 deletions(-) (limited to 'logic') diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp index af56288c..d0795c0d 100644 --- a/logic/updater/UpdateChecker.cpp +++ b/logic/updater/UpdateChecker.cpp @@ -47,14 +47,16 @@ bool UpdateChecker::hasChannels() const return !m_channels.isEmpty(); } -void UpdateChecker::checkForUpdate() +void UpdateChecker::checkForUpdate(bool notifyNoUpdate) { QLOG_DEBUG() << "Checking for updates."; - // If the channel list hasn't loaded yet, load it and defer checking for updates until later. + // If the channel list hasn't loaded yet, load it and defer checking for updates until + // later. if (!m_chanListLoaded) { - QLOG_DEBUG() << "Channel list isn't loaded yet. Loading channel list and deferring update check."; + QLOG_DEBUG() << "Channel list isn't loaded yet. Loading channel list and deferring " + "update check."; m_checkUpdateWaiting = true; updateChanList(); return; @@ -72,7 +74,8 @@ void UpdateChecker::checkForUpdate() // TODO: Allow user to select channels. For now, we'll just use the current channel. QString updateChannel = m_currentChannel; - // Find the desired channel within the channel list and get its repo URL. If if cannot be found, error. + // Find the desired channel within the channel list and get its repo URL. If if cannot be + // found, error. m_repoUrl = ""; for (ChannelListEntry entry : m_channels) { @@ -91,20 +94,22 @@ void UpdateChecker::checkForUpdate() auto job = new NetJob("GoUpdate Repository Index"); job->addNetAction(ByteArrayDownload::make(indexUrl)); - connect(job, SIGNAL(succeeded()), SLOT(updateCheckFinished())); + connect(job, &NetJob::succeeded, [this, notifyNoUpdate]() + { updateCheckFinished(notifyNoUpdate); }); connect(job, SIGNAL(failed()), SLOT(updateCheckFailed())); indexJob.reset(job); job->start(); } -void UpdateChecker::updateCheckFinished() +void UpdateChecker::updateCheckFinished(bool notifyNoUpdate) { QLOG_DEBUG() << "Finished downloading repo index. Checking for new versions."; QJsonParseError jsonError; QByteArray data; { - ByteArrayDownloadPtr dl = std::dynamic_pointer_cast(indexJob->first()); + ByteArrayDownloadPtr dl = + std::dynamic_pointer_cast(indexJob->first()); data = dl->m_data; indexJob.reset(); } @@ -112,7 +117,8 @@ void UpdateChecker::updateCheckFinished() QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject()) { - QLOG_ERROR() << "Failed to parse GoUpdate repository index. JSON error" << jsonError.errorString() << "at offset" << jsonError.offset; + QLOG_ERROR() << "Failed to parse GoUpdate repository index. JSON error" + << jsonError.errorString() << "at offset" << jsonError.offset; return; } @@ -122,7 +128,8 @@ void UpdateChecker::updateCheckFinished() int apiVersion = object.value("ApiVersion").toVariant().toInt(&success); if (apiVersion != API_VERSION || !success) { - QLOG_ERROR() << "Failed to check for updates. API version mismatch. We're using" << API_VERSION << "server has" << apiVersion; + QLOG_ERROR() << "Failed to check for updates. API version mismatch. We're using" + << API_VERSION << "server has" << apiVersion; return; } @@ -132,19 +139,27 @@ void UpdateChecker::updateCheckFinished() for (QJsonValue versionVal : versions) { QJsonObject version = versionVal.toObject(); - if (newestVersion.value("Id").toVariant().toInt() < version.value("Id").toVariant().toInt()) + if (newestVersion.value("Id").toVariant().toInt() < + version.value("Id").toVariant().toInt()) { - QLOG_DEBUG() << "Found newer version with ID" << version.value("Id").toVariant().toInt(); + QLOG_DEBUG() << "Found newer version with ID" + << version.value("Id").toVariant().toInt(); newestVersion = version; } } - // We've got the version with the greatest ID number. Now compare it to our current build number and update if they're different. + // We've got the version with the greatest ID number. Now compare it to our current build + // number and update if they're different. int newBuildNumber = newestVersion.value("Id").toVariant().toInt(); if (newBuildNumber != MMC->version().build) { // Update! - emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(), newBuildNumber); + emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(), + newBuildNumber); + } + else if (notifyNoUpdate) + { + emit noUpdateFound(); } m_updateChecking = false; @@ -163,12 +178,13 @@ void UpdateChecker::updateChanList() if (m_channelListUrl.isEmpty()) { QLOG_ERROR() << "Failed to update channel list. No channel list URL set." - << "If you'd like to use MultiMC's update system, please pass the channel list URL to CMake at compile time."; + << "If you'd like to use MultiMC's update system, please pass the channel " + "list URL to CMake at compile time."; return; } m_chanListLoading = true; - NetJob* job = new NetJob("Update System Channel List"); + NetJob *job = new NetJob("Update System Channel List"); job->addNetAction(ByteArrayDownload::make(QUrl(m_channelListUrl))); QObject::connect(job, &NetJob::succeeded, this, &UpdateChecker::chanListDownloadFinished); QObject::connect(job, &NetJob::failed, this, &UpdateChecker::chanListDownloadFailed); @@ -180,7 +196,8 @@ void UpdateChecker::chanListDownloadFinished() { QByteArray data; { - ByteArrayDownloadPtr dl = std::dynamic_pointer_cast(chanListJob->first()); + ByteArrayDownloadPtr dl = + std::dynamic_pointer_cast(chanListJob->first()); data = dl->m_data; chanListJob.reset(); } @@ -190,17 +207,20 @@ void UpdateChecker::chanListDownloadFinished() if (jsonError.error != QJsonParseError::NoError) { // TODO: Report errors to the user. - QLOG_ERROR() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" << jsonError.offset; + QLOG_ERROR() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" + << jsonError.offset; return; } - + QJsonObject object = jsonDoc.object(); bool success = false; int formatVersion = object.value("format_version").toVariant().toInt(&success); if (formatVersion != CHANLIST_FORMAT || !success) { - QLOG_ERROR() << "Failed to check for updates. Channel list format version mismatch. We're using" << CHANLIST_FORMAT << "server has" << formatVersion; + QLOG_ERROR() + << "Failed to check for updates. Channel list format version mismatch. We're using" + << CHANLIST_FORMAT << "server has" << formatVersion; return; } @@ -210,12 +230,10 @@ void UpdateChecker::chanListDownloadFinished() for (QJsonValue chanVal : channelArray) { QJsonObject channelObj = chanVal.toObject(); - ChannelListEntry entry{ - channelObj.value("id").toVariant().toString(), - channelObj.value("name").toVariant().toString(), - channelObj.value("description").toVariant().toString(), - channelObj.value("url").toVariant().toString() - }; + ChannelListEntry entry{channelObj.value("id").toVariant().toString(), + channelObj.value("name").toVariant().toString(), + channelObj.value("description").toVariant().toString(), + channelObj.value("url").toVariant().toString()}; if (entry.id.isEmpty() || entry.name.isEmpty() || entry.url.isEmpty()) { QLOG_ERROR() << "Channel list entry with empty ID, name, or URL. Skipping."; @@ -233,7 +251,7 @@ void UpdateChecker::chanListDownloadFinished() // If we're waiting to check for updates, do that now. if (m_checkUpdateWaiting) - checkForUpdate(); + checkForUpdate(false); emit channelListLoaded(); } @@ -244,4 +262,3 @@ void UpdateChecker::chanListDownloadFailed() QLOG_ERROR() << "Failed to download channel list."; emit channelListLoaded(); } - diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h index 5b7efc05..a47e8903 100644 --- a/logic/updater/UpdateChecker.h +++ b/logic/updater/UpdateChecker.h @@ -25,7 +25,7 @@ class UpdateChecker : public QObject public: UpdateChecker(); - void checkForUpdate(); + void checkForUpdate(bool notifyNoUpdate); void setCurrentChannel(const QString &channel) { m_currentChannel = channel; } void setChannelListUrl(const QString &url) { m_channelListUrl = url; } @@ -65,8 +65,10 @@ signals: //! Signal emitted when the channel list finishes loading or fails to load. void channelListLoaded(); + void noUpdateFound(); + private slots: - void updateCheckFinished(); + void updateCheckFinished(bool notifyNoUpdate); void updateCheckFailed(); void chanListDownloadFinished(); -- cgit v1.2.3 From 8d0ca72abb10b0cb77816d44f3f768865cc23aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 24 Dec 2013 23:38:37 +0100 Subject: Log SSL errors, give common solutions --- logic/auth/YggdrasilTask.cpp | 24 ++++++++++++++++++++++++ logic/auth/YggdrasilTask.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'logic') diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp index 573dd57a..3ba53bd7 100644 --- a/logic/auth/YggdrasilTask.cpp +++ b/logic/auth/YggdrasilTask.cpp @@ -48,6 +48,7 @@ void YggdrasilTask::executeTask() connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply); connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers); connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers); + connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors); timeout_keeper.setSingleShot(true); timeout_keeper.start(timeout_max); counter.setSingleShot(false); @@ -75,10 +76,33 @@ void YggdrasilTask::abort() m_netReply->abort(); } +void YggdrasilTask::sslErrors(QList errors) +{ + int i = 1; + for(auto error: errors) + { + QLOG_ERROR() << "LOGIN SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + QLOG_ERROR() << "Certificate in question:\n" << cert.toText(); + i++; + } +} + void YggdrasilTask::processReply() { setStatus(getStateMessage(STATE_PROCESSING_RESPONSE)); + if (m_netReply->error() == QNetworkReply::SslHandshakeFailedError) + { + emitFailed(tr("SSL Handshake failed.
There might be a few causes for it:
" + "
    " + "
  • You use Windows XP and need to update your root certificates
  • " + "
  • Some device on your network is interfering with SSL traffic. In that case, you have bigger worries than Minecraft not starting.
  • " + "
  • Possibly something else. Check the MultiMC log file for details
  • " + "
")); + return; + } + // any network errors lead to offline mode right now if (m_netReply->error() >= QNetworkReply::ConnectionRefusedError && m_netReply->error() <= QNetworkReply::UnknownNetworkError) diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h index 1f81a2d0..85f5a1e1 100644 --- a/logic/auth/YggdrasilTask.h +++ b/logic/auth/YggdrasilTask.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "logic/auth/MojangAccount.h" @@ -99,6 +100,7 @@ slots: void processReply(); void refreshTimers(qint64, qint64); void heartbeat(); + void sslErrors(QList); public slots: -- cgit v1.2.3 From e3389a4eef5beaa8db49fcd82a8b725f234d3840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 25 Dec 2013 01:27:38 +0100 Subject: Log even more error stuff for login. --- logic/auth/YggdrasilTask.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'logic') diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp index 3ba53bd7..277d7bfd 100644 --- a/logic/auth/YggdrasilTask.cpp +++ b/logic/auth/YggdrasilTask.cpp @@ -79,7 +79,7 @@ void YggdrasilTask::abort() void YggdrasilTask::sslErrors(QList errors) { int i = 1; - for(auto error: errors) + for (auto error : errors) { QLOG_ERROR() << "LOGIN SSL Error #" << i << " : " << error.errorString(); auto cert = error.certificate(); @@ -94,12 +94,16 @@ void YggdrasilTask::processReply() if (m_netReply->error() == QNetworkReply::SslHandshakeFailedError) { - emitFailed(tr("SSL Handshake failed.
There might be a few causes for it:
" - "
    " - "
  • You use Windows XP and need to update your root certificates
  • " - "
  • Some device on your network is interfering with SSL traffic. In that case, you have bigger worries than Minecraft not starting.
  • " - "
  • Possibly something else. Check the MultiMC log file for details
  • " - "
")); + emitFailed( + tr("SSL Handshake failed.
There might be a few causes for it:
" + "
    " + "
  • You use Windows XP and need to update " + "your root certificates
  • " + "
  • Some device on your network is interfering with SSL traffic. In that case, " + "you have bigger worries than Minecraft not starting.
  • " + "
  • Possibly something else. Check the MultiMC log file for details
  • " + "
")); return; } @@ -109,6 +113,8 @@ void YggdrasilTask::processReply() { // WARNING/FIXME: the value here is used in MojangAccount to detect the cancel/timeout emitFailed("Yggdrasil task cancelled."); + QLOG_ERROR() << "Yggdrasil task cancelled because of: " << m_netReply->error() << " : " + << m_netReply->errorString(); return; } -- cgit v1.2.3 From 8edd0100e852cda9f44abd12cc06931542a2004a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 25 Dec 2013 02:46:06 +0100 Subject: Fix more updater derps. * Updater requires unix style paths on input. * No update notification was getting cloned with every check --- logic/updater/DownloadUpdateTask.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 5f05b0ba..057ca436 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -438,9 +438,6 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin { QDomElement file = doc.createElement("file"); - QString native_file = QDir::toNativeSeparators(op.file); - QString native_dest = QDir::toNativeSeparators(op.dest); - switch (op.type) { case UpdateOperation::OP_COPY: @@ -449,8 +446,8 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin QDomElement name = doc.createElement("source"); QDomElement path = doc.createElement("dest"); QDomElement mode = doc.createElement("mode"); - name.appendChild(doc.createTextNode(native_file)); - path.appendChild(doc.createTextNode(native_dest)); + name.appendChild(doc.createTextNode(op.file)); + path.appendChild(doc.createTextNode(op.dest)); // We need to add a 0 at the beginning here, because Qt doesn't convert to octal // correctly. mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8))); @@ -458,16 +455,16 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin file.appendChild(path); file.appendChild(mode); installFiles.appendChild(file); - QLOG_DEBUG() << "Will install file" << native_file; + QLOG_DEBUG() << "Will install file " << op.file << " to " << op.dest; } break; case UpdateOperation::OP_DELETE: { // Delete the file. - file.appendChild(doc.createTextNode(native_file)); + file.appendChild(doc.createTextNode(op.file)); removeFiles.appendChild(file); - QLOG_DEBUG() << "Will remove file" << native_file; + QLOG_DEBUG() << "Will remove file" << op.file; } break; -- cgit v1.2.3 From acf25d8a33ef67b79d8e8a8859f5559e011373a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 26 Dec 2013 05:14:32 +0100 Subject: Disable/enable mods with checkboxes. Needs testing. A lot of testing! --- logic/LegacyInstance.cpp | 1 + logic/Mod.cpp | 87 +++++++++++++++++--- logic/Mod.h | 30 ++++--- logic/ModList.cpp | 210 +++++++++++++++++++++++++++++++++++------------ logic/ModList.h | 18 +++- 5 files changed, 268 insertions(+), 78 deletions(-) (limited to 'logic') diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index fef27bcd..5c82b837 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -150,6 +150,7 @@ std::shared_ptr LegacyInstance::jarModList() void LegacyInstance::jarModsChanged() { + QLOG_INFO() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt."; setShouldRebuild(true); } diff --git a/logic/Mod.cpp b/logic/Mod.cpp index cff9467e..8e880be1 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -35,20 +35,40 @@ Mod::Mod(const QFileInfo &file) void Mod::repath(const QFileInfo &file) { m_file = file; - m_name = file.completeBaseName(); - m_id = file.fileName(); + QString name_base = file.fileName(); m_type = Mod::MOD_UNKNOWN; + if (m_file.isDir()) + { m_type = MOD_FOLDER; + m_name = name_base; + m_mmc_id = name_base; + } else if (m_file.isFile()) { - QString ext = m_file.suffix().toLower(); - if (ext == "zip" || ext == "jar") + if(name_base.endsWith(".disabled")) + { + m_enabled = false; + name_base.chop(9); + } + else + { + m_enabled = true; + } + m_mmc_id = name_base; + if (name_base.endsWith(".zip") || name_base.endsWith(".jar")) + { m_type = MOD_ZIPFILE; + name_base.chop(4); + } else + { m_type = MOD_SINGLEFILE; + } + m_name = name_base; } + if (m_type == MOD_ZIPFILE) { QuaZip zip(m_file.filePath()); @@ -114,7 +134,7 @@ void Mod::ReadMCModInfo(QByteArray contents) if (!arr.at(0).isObject()) return; auto firstObj = arr.at(0).toObject(); - m_id = firstObj.value("modid").toString(); + m_mod_id = firstObj.value("modid").toString(); m_name = firstObj.value("name").toString(); m_version = firstObj.value("version").toString(); m_homeurl = firstObj.value("url").toString(); @@ -163,7 +183,7 @@ void Mod::ReadForgeInfo(QByteArray contents) { // Read the data m_name = "Minecraft Forge"; - m_id = "Forge"; + m_mod_id = "Forge"; m_homeurl = "http://www.minecraftforge.net/forum/"; INIFile ini; if (!ini.loadFile(contents)) @@ -183,9 +203,11 @@ bool Mod::replace(Mod &with) return false; bool success = false; auto t = with.type(); + if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE) { - success = QFile::copy(with.m_file.filePath(), m_file.path()); + QLOG_DEBUG() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath(); + success = QFile::copy(with.m_file.filePath(), m_file.filePath()); } if (t == MOD_FOLDER) { @@ -193,11 +215,17 @@ bool Mod::replace(Mod &with) } if (success) { - m_id = with.m_id; - m_mcversion = with.m_mcversion; - m_type = with.m_type; m_name = with.m_name; + m_mmc_id = with.m_mmc_id; + m_mod_id = with.m_mod_id; m_version = with.m_version; + m_mcversion = with.m_mcversion; + m_description = with.m_description; + m_authors = with.m_authors; + m_credits = with.m_credits; + m_homeurl = with.m_homeurl; + m_type = with.m_type; + m_file.refresh(); } return success; } @@ -241,3 +269,42 @@ QString Mod::version() const return "VOID"; } } + +bool Mod::enable(bool value) +{ + if(m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) + return false; + + if(m_enabled == value) + return false; + + QString path = m_file.absoluteFilePath(); + if(value) + { + QFile foo(path); + if(!path.endsWith(".disabled")) + return false; + path.chop(9); + if(!foo.rename(path)) + return false; + } + else + { + QFile foo(path); + path += ".disabled"; + if(!foo.rename(path)) + return false; + } + m_file = QFileInfo(path); + m_enabled = value; + return true; +} +bool Mod::operator==(const Mod &other) const +{ + return mmc_id() == other.mmc_id(); +} +bool Mod::strongCompare(const Mod &other) const +{ + return mmc_id() == other.mmc_id() && + version() == other.version() && type() == other.type(); +} diff --git a/logic/Mod.h b/logic/Mod.h index ca362a9d..05d3cea2 100644 --- a/logic/Mod.h +++ b/logic/Mod.h @@ -33,9 +33,13 @@ public: { return m_file; } - QString id() const + QString mmc_id() const { - return m_id; + return m_mmc_id; + } + QString mod_id() const + { + return m_mod_id; } ModType type() const { @@ -77,6 +81,13 @@ public: return m_credits; } + bool enabled() const + { + return m_enabled; + } + + bool enable(bool value); + // delete all the files of this mod bool destroy(); // replace this mod with a copy of the other @@ -85,15 +96,8 @@ public: void repath(const QFileInfo &file); // WEAK compare operator - used for replacing mods - 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(); - } + bool operator==(const Mod &other) const; + bool strongCompare(const Mod &other) const; private: void ReadMCModInfo(QByteArray contents); @@ -108,7 +112,9 @@ protected: */ QFileInfo m_file; - QString m_id; + QString m_mmc_id; + QString m_mod_id; + bool m_enabled = true; QString m_name; QString m_version; QString m_mcversion; diff --git a/logic/ModList.cpp b/logic/ModList.cpp index d5235fe9..dbc85320 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "logger/QsLog.h" @@ -27,7 +28,7 @@ ModList::ModList(const QString &dir, const QString &list_file) { m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks); - m_dir.setSorting(QDir::Name); + m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); m_list_id = QUuid::createUuid().toString(); m_watcher = new QFileSystemWatcher(this); is_watching = false; @@ -66,52 +67,79 @@ bool ModList::update() if (!isValid()) return false; + QList orderedMods; QList newMods; m_dir.refresh(); auto folderContents = m_dir.entryInfoList(); - bool orderWasInvalid = false; + bool orderOrStateChanged = false; // first, process the ordered items (if any) - QStringList listOrder = readListFile(); + OrderList listOrder = readListFile(); for (auto item : listOrder) { - QFileInfo info(m_dir.filePath(item)); - int idx = folderContents.indexOf(info); + QFileInfo infoEnabled(m_dir.filePath(item.id)); + QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled")); + int idxEnabled = folderContents.indexOf(infoEnabled); + int idxDisabled = folderContents.indexOf(infoDisabled); + // if both enabled and disabled versions are present, PANIC! + if (idxEnabled >= 0 && idxDisabled >= 0) + { + return false; + } + bool isEnabled = idxEnabled >= 0; + int idx = isEnabled ? idxEnabled : idxDisabled; + QFileInfo info = isEnabled ? infoEnabled : infoDisabled; // if the file from the index file exists if (idx != -1) { // remove from the actual folder contents list folderContents.takeAt(idx); // append the new mod - newMods.append(Mod(info)); + orderedMods.append(Mod(info)); + if (isEnabled != item.enabled) + orderOrStateChanged = true; } else { - orderWasInvalid = true; + orderOrStateChanged = true; } } - for (auto entry : folderContents) + // if there are any untracked files... + if (folderContents.size()) { - newMods.append(Mod(entry)); + // the order surely changed! + for (auto entry : folderContents) + { + newMods.append(Mod(entry)); + } + std::sort(newMods.begin(), newMods.end(), [](const Mod & left, const Mod & right) + { return left.name().localeAwareCompare(right.name()) <= 0; }); + orderedMods.append(newMods); + orderOrStateChanged = true; } - if (mods.size() != newMods.size()) + // otherwise, if we were already tracking some mods + else if (mods.size()) { - orderWasInvalid = true; - } - else - for (int i = 0; i < mods.size(); i++) - { - if (!mods[i].strongCompare(newMods[i])) + // if the number doesn't match, order changed. + if (mods.size() != orderedMods.size()) + orderOrStateChanged = true; + // if it does match, compare the mods themselves + else + for (int i = 0; i < mods.size(); i++) { - orderWasInvalid = true; - break; + if (!mods[i].strongCompare(orderedMods[i])) + { + orderOrStateChanged = true; + break; + } } - } + } beginResetModel(); - mods.swap(newMods); + mods.swap(orderedMods); endResetModel(); - if (orderWasInvalid) + if (orderOrStateChanged && !m_list_file.isEmpty()) { + QLOG_INFO() << "Mod list " << m_list_file << " changed!"; saveListFile(); emit changed(); } @@ -123,17 +151,19 @@ void ModList::directoryChanged(QString path) update(); } -QStringList ModList::readListFile() +ModList::OrderList ModList::readListFile() { - QStringList stringList; + OrderList itemList; if (m_list_file.isNull() || m_list_file.isEmpty()) - return stringList; + return itemList; QFile textFile(m_list_file); if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) - return QStringList(); + return OrderList(); - QTextStream textStream(&textFile); + QTextStream textStream; + textStream.setAutoDetectUnicode(true); + textStream.setDevice(&textFile); while (true) { QString line = textStream.readLine(); @@ -141,11 +171,18 @@ QStringList ModList::readListFile() break; else { - stringList.append(line); + OrderItem it; + it.enabled = !line.endsWith(".disabled"); + if (!it.enabled) + { + line.chop(9); + } + it.id = line; + itemList.append(it); } } textFile.close(); - return stringList; + return itemList; } bool ModList::saveListFile() @@ -155,12 +192,16 @@ bool ModList::saveListFile() QFile textFile(m_list_file); if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) return false; - QTextStream textStream(&textFile); + QTextStream textStream; + textStream.setGenerateByteOrderMark(true); + textStream.setCodec("UTF-8"); + textStream.setDevice(&textFile); for (auto mod : mods) { - auto pathname = mod.filename(); - QString filename = pathname.fileName(); - textStream << filename << endl; + textStream << mod.mmc_id(); + if (!mod.enabled()) + textStream << ".disabled"; + textStream << endl; } textFile.close(); return false; @@ -327,7 +368,7 @@ bool ModList::moveModsDown(int first, int last) int ModList::columnCount(const QModelIndex &parent) const { - return 2; + return 3; } QVariant ModList::data(const QModelIndex &index, int role) const @@ -341,43 +382,96 @@ QVariant ModList::data(const QModelIndex &index, int role) const if (row < 0 || row >= mods.size()) return QVariant(); - if (role != Qt::DisplayRole) - return QVariant(); - - switch (column) + switch (role) { - case 0: - return mods[row].name(); - case 1: - return mods[row].version(); - case 2: - return mods[row].mcversion(); + case Qt::DisplayRole: + switch (index.column()) + { + case NameColumn: + return mods[row].name(); + case VersionColumn: + return mods[row].version(); + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + return mods[row].mmc_id(); + + case Qt::CheckStateRole: + switch (index.column()) + { + case ActiveColumn: + return mods[row].enabled(); + default: + return QVariant(); + } default: return QVariant(); } } +bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) + { + return false; + } + + if (role == Qt::CheckStateRole) + { + auto &mod = mods[index.row()]; + if (mod.enable(!mod.enabled())) + { + emit dataChanged(index, index); + return true; + } + } + return false; +} + QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - switch (section) + switch (role) { - case 0: - return QString("Name"); - case 1: - return QString("Version"); - case 2: - return QString("Minecraft"); + case Qt::DisplayRole: + switch (section) + { + case ActiveColumn: + return QString(); + case NameColumn: + return QString("Name"); + case VersionColumn: + return QString("Version"); + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case ActiveColumn: + return "Is the mod enabled?"; + case NameColumn: + return "The name of the mod."; + case VersionColumn: + return "The version of the mod."; + default: + return QVariant(); + } + default: + return QVariant(); } - return QString(); + return QVariant(); } Qt::ItemFlags ModList::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); if (index.isValid()) - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | + defaultFlags; else return Qt::ItemIsDropEnabled | defaultFlags; } @@ -456,6 +550,14 @@ bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row QString filename = url.toLocalFile(); installMod(filename, row); QLOG_INFO() << "installing: " << filename; + // if there is no ordering, re-sort the list + if (m_list_file.isEmpty()) + { + beginResetModel(); + std::sort(mods.begin(), mods.end(), [](const Mod & left, const Mod & right) + { return left.name().localeAwareCompare(right.name()) <= 0; }); + endResetModel(); + } } if (was_watching) startWatching(); diff --git a/logic/ModList.h b/logic/ModList.h index 803a5429..0d6507fb 100644 --- a/logic/ModList.h +++ b/logic/ModList.h @@ -34,9 +34,18 @@ class ModList : public QAbstractListModel { Q_OBJECT public: + enum Columns + { + ActiveColumn = 0, + NameColumn, + VersionColumn + }; ModList(const QString &dir, const QString &list_file = QString()); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole); + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const { return size(); @@ -59,7 +68,6 @@ public: { return mods[index]; } - ; /// Reloads the mod list and returns true if the list changed. virtual bool update(); @@ -119,7 +127,13 @@ public: } private: - QStringList readListFile(); + struct OrderItem + { + QString id; + bool enabled = false; + }; + typedef QList OrderList; + OrderList readListFile(); bool saveListFile(); private slots: -- cgit v1.2.3 From aa91d89aaafd21f5196a250b3764c44050ebd990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 26 Dec 2013 05:19:11 +0100 Subject: Do not merge disabled jar mods. --- logic/LegacyUpdate.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'logic') diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index fb9dcf2b..cb3598a7 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -423,6 +423,11 @@ void LegacyUpdate::ModTheJar() for (int i = modList->size() - 1; i >= 0; i--) { auto &mod = modList->operator[](i); + + // do not merge disabled mods. + if(!mod.enabled()) + continue; + if (mod.type() == Mod::MOD_ZIPFILE) { if (!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf)) -- cgit v1.2.3 From 4bf1cac8d89809106819c86543ef8efbf78f163f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 27 Dec 2013 02:18:40 +0100 Subject: Handle the foo + foo.disabled jar mod corner case better. --- logic/ModList.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'logic') diff --git a/logic/ModList.cpp b/logic/ModList.cpp index dbc85320..eb7f3128 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -81,14 +81,24 @@ bool ModList::update() QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled")); int idxEnabled = folderContents.indexOf(infoEnabled); int idxDisabled = folderContents.indexOf(infoDisabled); - // if both enabled and disabled versions are present, PANIC! + bool isEnabled; + // if both enabled and disabled versions are present, it's a special case... if (idxEnabled >= 0 && idxDisabled >= 0) { - return false; + // we only process the one we actually have in the order file. + // and exactly as we have it. + // THIS IS A CORNER CASE + isEnabled = item.enabled; + } + else + { + // only one is present. + // we pick the one that we found. + // we assume the mod was enabled/disabled by external means + isEnabled = idxEnabled >= 0; } - bool isEnabled = idxEnabled >= 0; int idx = isEnabled ? idxEnabled : idxDisabled; - QFileInfo info = isEnabled ? infoEnabled : infoDisabled; + QFileInfo & info = isEnabled ? infoEnabled : infoDisabled; // if the file from the index file exists if (idx != -1) { @@ -226,6 +236,9 @@ bool ModList::installMod(const QFileInfo &filename, int index) int idx = mods.indexOf(m); if (idx != -1) { + int idx2 = mods.indexOf(m,idx+1); + if(idx2 != -1) + return false; if (mods[idx].replace(m)) { -- cgit v1.2.3 From 7652b3d64a63c587f520633364412345083210d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 28 Dec 2013 02:03:53 +0100 Subject: Various updater fixes Updater tests for path utils The updater now doesn't use splitpath on Windows (fixes problems with Windows XP) Fix up paths for the OSX updater - should now install the updates into the right place Fix translations install path - translation isntall and deploy should be fixed --- logic/updater/DownloadUpdateTask.cpp | 36 +++++++++++++++++++++++++++++++----- logic/updater/DownloadUpdateTask.h | 16 +++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 057ca436..0b09ad2a 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -66,7 +66,7 @@ void DownloadUpdateTask::processChannels() if (channel.id == channelId) { QLOG_INFO() << "Found matching channel."; - m_cRepoUrl = preparePath(channel.url); + m_cRepoUrl = fixPathForTests(channel.url); break; } } @@ -207,8 +207,17 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis { QJsonObject fileObj = fileValue.toObject(); + QString file_path = fileObj.value("Path").toString(); +#ifdef Q_OS_MAC + // On OSX, the paths for the updater need to be fixed. + // basically, anything that isn't in the .app folder is ignored. + // everything else is changed so the code that processes the files actually finds + // them and puts the replacements in the right spots. + if(!fixPathForOSX(file_path)) + continue; +#endif VersionFileEntry file{ - fileObj.value("Path").toString(), fileObj.value("Perms").toVariant().toInt(), + file_path , fileObj.value("Perms").toVariant().toInt(), FileSourceList(), fileObj.value("MD5").toString(), }; QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode; @@ -221,12 +230,12 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis if (type == "http") { file.sources.append( - FileSource("http", preparePath(sourceObj.value("Url").toString()))); + FileSource("http", fixPathForTests(sourceObj.value("Url").toString()))); } else if (type == "httpc") { file.sources.append(FileSource("httpc", - preparePath(sourceObj.value("Url").toString()), + fixPathForTests(sourceObj.value("Url").toString()), sourceObj.value("CompressionType").toString())); } else @@ -491,7 +500,7 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin return true; } -QString DownloadUpdateTask::preparePath(const QString &path) +QString DownloadUpdateTask::fixPathForTests(const QString &path) { if(path.startsWith("$PWD")) { @@ -502,6 +511,23 @@ QString DownloadUpdateTask::preparePath(const QString &path) return path; } +bool DownloadUpdateTask::fixPathForOSX(QString &path) +{ + if(path.startsWith("MultiMC.app/")) + { + // remove the prefix and add a new, more appropriate one. + path.remove(0,12); + path = QString("../../") + path; + return true; + } + else + { + QLOG_ERROR() << "Update path not within .app: " << path; + return false; + } +} + + void DownloadUpdateTask::fileDownloadFinished() { emitSucceeded(); diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index 79d73af3..d82b044f 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -198,7 +198,21 @@ protected: * Filters paths * Path of the format $PWD/path, it is converted to a file:///$PWD/ URL */ - static QString preparePath(const QString &path); + static QString fixPathForTests(const QString &path); + + /*! + * Filters paths + * This fixes destination paths for OSX. + * The updater runs in MultiMC.app/Contents/MacOs by default + * The destination paths are such as this: MultiMC.app/blah/blah + * + * Therefore we chop off the 'MultiMC.app' prefix and prepend ../.. + * + * Returns false if the path couldn't be fixed (is invalid) + * + * Has no effect on systems that aren't OSX + */ + static bool fixPathForOSX(QString &path); protected slots: void vinfoDownloadFinished(); -- cgit v1.2.3 From 55e62a81b69663041cb2402f779d0d957c499cb1 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 28 Dec 2013 14:22:36 +0100 Subject: LiteLoader support --- logic/LiteLoaderInstaller.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++ logic/LiteLoaderInstaller.h | 28 +++++++++++++++++++ logic/OneSixLibrary.h | 6 +++++ 3 files changed, 96 insertions(+) create mode 100644 logic/LiteLoaderInstaller.cpp create mode 100644 logic/LiteLoaderInstaller.h (limited to 'logic') diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp new file mode 100644 index 00000000..7588ae6a --- /dev/null +++ b/logic/LiteLoaderInstaller.cpp @@ -0,0 +1,62 @@ +/* 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 "LiteLoaderInstaller.h" + +#include "OneSixVersion.h" +#include "OneSixLibrary.h" + +LiteLoaderInstaller::LiteLoaderInstaller() +{ + +} + +bool hasLibrary(const QString &rawName, const QList > &libs) +{ + for (auto lib : libs) + { + if (lib->rawName() == rawName) + { + return true; + } + } + return false; +} + +bool LiteLoaderInstaller::apply(std::shared_ptr to) +{ + to->externalUpdateStart(); + + if (!hasLibrary("net.minecraft:launchwrapper:1.8", to->libraries)) + { + std::shared_ptr lib(new OneSixLibrary("net.minecraft:launchwrapper:1.8")); + lib->finalize(); + to->libraries.prepend(lib); + } + + if (!hasLibrary("com.mumfrey:liteloader:1.6.4", to->libraries)) + { + std::shared_ptr lib(new OneSixLibrary("com.mumfrey:liteloader:1.6.4")); + lib->setBaseUrl("http://dl.liteloader.com/versions/"); + lib->finalize(); + to->libraries.prepend(lib); + } + + to->mainClass = "net.minecraft.launchwrapper.Launch"; + to->minecraftArguments.append(" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"); + + to->externalUpdateFinish(); + return to->toOriginalFile(); +} diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h new file mode 100644 index 00000000..6a5ee1f2 --- /dev/null +++ b/logic/LiteLoaderInstaller.h @@ -0,0 +1,28 @@ +/* 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 +#include + +class OneSixVersion; + +class LiteLoaderInstaller +{ +public: + LiteLoaderInstaller(); + + bool apply(std::shared_ptr to); +}; diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 5cb867c2..3f0bc83d 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -68,6 +68,12 @@ public: m_name = name; } + /// Returns the raw name field + QString rawName() const + { + return m_name; + } + QJsonObject toJson(); /** -- cgit v1.2.3 From c816a26647ca0537709f0d15cdd550feea4de109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 28 Dec 2013 22:32:45 +0100 Subject: Set permissions for the updater binary after updating it. --- logic/updater/DownloadUpdateTask.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 0b09ad2a..9282c4d8 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -412,6 +412,7 @@ DownloadUpdateTask::processFileLists(NetJob *job, if (isUpdater) { download->setProperty("finalPath", entry.path); + download->setProperty("finalPerms", entry.mode); connect(download.get(), &MD5EtagDownload::succeeded, this, &DownloadUpdateTask::directDeployFile); } } @@ -549,11 +550,16 @@ void DownloadUpdateTask::directDeployFile(const int index) { Md5EtagDownloadPtr download = std::dynamic_pointer_cast(m_filesNetJob->operator[](index)); const QString finalPath = download->property("finalPath").toString(); + bool ok = true; + int finalMode = download->property("finalPerms").toInt(&ok); + if(!ok) + finalMode = 0755; QLOG_INFO() << "Replacing" << finalPath << "with" << download->m_output_file.fileName(); if (QFile::remove(finalPath)) { if (download->m_output_file.copy(finalPath)) { + QFile::setPermissions(finalPath, (QFileDevice::Permission) finalMode); return; } } -- cgit v1.2.3 From 5b54a4ca8c4849a4476bb9a5e1c2414463949621 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 28 Dec 2013 23:34:18 +0100 Subject: Don't hardcode stuff!!! --- logic/LiteLoaderInstaller.cpp | 88 +++++++++++++++++++++++++++++++------------ logic/LiteLoaderInstaller.h | 13 ++++++- 2 files changed, 76 insertions(+), 25 deletions(-) (limited to 'logic') diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 7588ae6a..07fffff3 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -18,45 +18,85 @@ #include "OneSixVersion.h" #include "OneSixLibrary.h" -LiteLoaderInstaller::LiteLoaderInstaller() -{ - -} +QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; -bool hasLibrary(const QString &rawName, const QList > &libs) +LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion) { - for (auto lib : libs) + if (m_launcherWrapperVersionMapping.isEmpty()) { - if (lib->rawName() == rawName) - { - return true; - } + m_launcherWrapperVersionMapping["1.6.2"] = "1.3"; + m_launcherWrapperVersionMapping["1.6.4"] = "1.8"; + //m_launcherWrapperVersionMapping["1.7.2"] = "1.8"; + //m_launcherWrapperVersionMapping["1.7.4"] = "1.8"; } - return false; +} + +bool LiteLoaderInstaller::canApply() const +{ + return m_launcherWrapperVersionMapping.contains(m_mcVersion); } bool LiteLoaderInstaller::apply(std::shared_ptr to) { to->externalUpdateStart(); - if (!hasLibrary("net.minecraft:launchwrapper:1.8", to->libraries)) + applyLaunchwrapper(to); + applyLiteLoader(to); + + to->mainClass = "net.minecraft.launchwrapper.Launch"; + if (!to->minecraftArguments.contains( + " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker")) { - std::shared_ptr lib(new OneSixLibrary("net.minecraft:launchwrapper:1.8")); - lib->finalize(); - to->libraries.prepend(lib); + to->minecraftArguments.append( + " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"); } - if (!hasLibrary("com.mumfrey:liteloader:1.6.4", to->libraries)) + to->externalUpdateFinish(); + return to->toOriginalFile(); +} + +void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) +{ + const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion]; + + QMutableListIterator> it(to->libraries); + while (it.hasNext()) { - std::shared_ptr lib(new OneSixLibrary("com.mumfrey:liteloader:1.6.4")); - lib->setBaseUrl("http://dl.liteloader.com/versions/"); - lib->finalize(); - to->libraries.prepend(lib); + it.next(); + if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:")) + { + if (it.value()->version() >= intendedVersion) + { + return; + } + else + { + it.remove(); + } + } } - to->mainClass = "net.minecraft.launchwrapper.Launch"; - to->minecraftArguments.append(" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"); + std::shared_ptr lib(new OneSixLibrary( + "net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion])); + lib->finalize(); + to->libraries.prepend(lib); +} - to->externalUpdateFinish(); - return to->toOriginalFile(); +void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) +{ + QMutableListIterator> it(to->libraries); + while (it.hasNext()) + { + it.next(); + if (it.value()->rawName().startsWith("com.mumfrey:liteloader:")) + { + it.remove(); + } + } + + std::shared_ptr lib( + new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion)); + lib->setBaseUrl("http://dl.liteloader.com/versions/"); + lib->finalize(); + to->libraries.prepend(lib); } diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index 6a5ee1f2..44b306d6 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -15,6 +15,7 @@ #pragma once #include +#include #include class OneSixVersion; @@ -22,7 +23,17 @@ class OneSixVersion; class LiteLoaderInstaller { public: - LiteLoaderInstaller(); + LiteLoaderInstaller(const QString &mcVersion); + + bool canApply() const; bool apply(std::shared_ptr to); + +private: + QString m_mcVersion; + + void applyLaunchwrapper(std::shared_ptr to); + void applyLiteLoader(std::shared_ptr to); + + static QMap m_launcherWrapperVersionMapping; }; -- cgit v1.2.3 From 997be947c9baa1499f708594d7a954d772ea99b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 29 Dec 2013 01:13:57 +0100 Subject: Maybe break updater even more? --- logic/updater/DownloadUpdateTask.cpp | 64 +++++++++++------------------------- logic/updater/DownloadUpdateTask.h | 2 -- 2 files changed, 20 insertions(+), 46 deletions(-) (limited to 'logic') diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp index 9282c4d8..6e0a92f0 100644 --- a/logic/updater/DownloadUpdateTask.cpp +++ b/logic/updater/DownloadUpdateTask.cpp @@ -213,12 +213,11 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis // basically, anything that isn't in the .app folder is ignored. // everything else is changed so the code that processes the files actually finds // them and puts the replacements in the right spots. - if(!fixPathForOSX(file_path)) + if (!fixPathForOSX(file_path)) continue; #endif - VersionFileEntry file{ - file_path , fileObj.value("Perms").toVariant().toInt(), - FileSourceList(), fileObj.value("MD5").toString(), }; + VersionFileEntry file{file_path, fileObj.value("Perms").toVariant().toInt(), + FileSourceList(), fileObj.value("MD5").toString(), }; QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode; QJsonArray sourceArray = fileObj.value("Sources").toArray(); @@ -234,9 +233,9 @@ bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileLis } else if (type == "httpc") { - file.sources.append(FileSource("httpc", - fixPathForTests(sourceObj.value("Url").toString()), - sourceObj.value("CompressionType").toString())); + file.sources.append( + FileSource("httpc", fixPathForTests(sourceObj.value("Url").toString()), + sourceObj.value("CompressionType").toString())); } else { @@ -401,25 +400,23 @@ DownloadUpdateTask::processFileLists(NetJob *job, QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); - if (job) + if (isUpdater) + { + auto cache_entry = MMC->metacache()->resolveEntry("root", entry.path); + QLOG_DEBUG() << "Updater will be in " << cache_entry->getFullPath(); + if(cache_entry->stale) + { + auto download = CacheDownload::make(QUrl(source.url), cache_entry); + job->addNetAction(download); + } + } + else { // We need to download the file to the updatefiles folder and add a task // to copy it to its install path. auto download = MD5EtagDownload::make(source.url, dlPath); download->m_expected_md5 = entry.md5; job->addNetAction(download); - - if (isUpdater) - { - download->setProperty("finalPath", entry.path); - download->setProperty("finalPerms", entry.mode); - connect(download.get(), &MD5EtagDownload::succeeded, this, &DownloadUpdateTask::directDeployFile); - } - } - - if (!isUpdater) - { - // Now add a copy operation to our operations list to install the file. ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); } } @@ -503,7 +500,7 @@ bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QStrin QString DownloadUpdateTask::fixPathForTests(const QString &path) { - if(path.startsWith("$PWD")) + if (path.startsWith("$PWD")) { QString foo = path; foo.replace("$PWD", qApp->applicationDirPath()); @@ -514,10 +511,10 @@ QString DownloadUpdateTask::fixPathForTests(const QString &path) bool DownloadUpdateTask::fixPathForOSX(QString &path) { - if(path.startsWith("MultiMC.app/")) + if (path.startsWith("MultiMC.app/")) { // remove the prefix and add a new, more appropriate one. - path.remove(0,12); + path.remove(0, 12); path = QString("../../") + path; return true; } @@ -528,7 +525,6 @@ bool DownloadUpdateTask::fixPathForOSX(QString &path) } } - void DownloadUpdateTask::fileDownloadFinished() { emitSucceeded(); @@ -546,26 +542,6 @@ void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 tota setProgress((int)(((float)current / (float)total) * 100)); } -void DownloadUpdateTask::directDeployFile(const int index) -{ - Md5EtagDownloadPtr download = std::dynamic_pointer_cast(m_filesNetJob->operator[](index)); - const QString finalPath = download->property("finalPath").toString(); - bool ok = true; - int finalMode = download->property("finalPerms").toInt(&ok); - if(!ok) - finalMode = 0755; - QLOG_INFO() << "Replacing" << finalPath << "with" << download->m_output_file.fileName(); - if (QFile::remove(finalPath)) - { - if (download->m_output_file.copy(finalPath)) - { - QFile::setPermissions(finalPath, (QFileDevice::Permission) finalMode); - return; - } - } - emitFailed("Couldn't copy updater files"); -} - QString DownloadUpdateTask::updateFilesDir() { return m_updateFilesDir.path(); diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h index d82b044f..b1d14846 100644 --- a/logic/updater/DownloadUpdateTask.h +++ b/logic/updater/DownloadUpdateTask.h @@ -221,7 +221,5 @@ protected slots: void fileDownloadFinished(); void fileDownloadFailed(); void fileDownloadProgressChanged(qint64 current, qint64 total); - - void directDeployFile(const int index); }; -- cgit v1.2.3 From 8549e5317f708e71c2dead9d84cc27d6185b523e Mon Sep 17 00:00:00 2001 From: robotbrainify Date: Sat, 28 Dec 2013 20:28:24 -0500 Subject: Litemod version info. --- logic/Mod.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++----------- logic/Mod.h | 2 ++ logic/ModList.cpp | 2 +- 3 files changed, 57 insertions(+), 14 deletions(-) (limited to 'logic') diff --git a/logic/Mod.cpp b/logic/Mod.cpp index 8e880be1..009a32a4 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -47,7 +47,7 @@ void Mod::repath(const QFileInfo &file) } else if (m_file.isFile()) { - if(name_base.endsWith(".disabled")) + if (name_base.endsWith(".disabled")) { m_enabled = false; name_base.chop(9); @@ -62,6 +62,11 @@ void Mod::repath(const QFileInfo &file) m_type = MOD_ZIPFILE; name_base.chop(4); } + else if (name_base.endsWith(".litemod")) + { + m_type = MOD_LITEMOD; + name_base.chop(8); + } else { m_type = MOD_SINGLEFILE; @@ -79,7 +84,7 @@ void Mod::repath(const QFileInfo &file) if (zip.setCurrentFile("mcmod.info")) { - if(!file.open(QIODevice::ReadOnly)) + if (!file.open(QIODevice::ReadOnly)) { zip.close(); return; @@ -120,6 +125,27 @@ void Mod::repath(const QFileInfo &file) ReadMCModInfo(data); } } + else if (m_type == MOD_LITEMOD) + { + QuaZip zip(m_file.filePath()); + if (!zip.open(QuaZip::mdUnzip)) + return; + + QuaZipFile file(&zip); + + if (zip.setCurrentFile("litemod.json")) + { + if (!file.open(QIODevice::ReadOnly)) + { + zip.close(); + return; + } + + ReadLiteModInfo(file.readAll()); + file.close(); + } + zip.close(); + } } // NEW format @@ -152,8 +178,7 @@ void Mod::ReadMCModInfo(QByteArray contents) } 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 @@ -197,13 +222,29 @@ void Mod::ReadForgeInfo(QByteArray contents) m_version = major + "." + minor + "." + revision + "." + build; } +void Mod::ReadLiteModInfo(QByteArray contents) +{ + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); + auto object = jsonDoc.object(); + m_mod_id = object.value("name").toString(); + if(object.contains("version")) + m_version=object.value("version").toString(""); + else + m_version=object.value("revision").toString(""); + m_mcversion = object.value("mcversion").toString(); + m_authors = object.value("author").toString(); + m_description = object.value("description").toString(); + m_homeurl = object.value("url").toString(); +} + bool Mod::replace(Mod &with) { if (!destroy()) return false; bool success = false; auto t = with.type(); - + if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE) { QLOG_DEBUG() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath(); @@ -260,6 +301,7 @@ QString Mod::version() const switch (type()) { case MOD_ZIPFILE: + case MOD_LITEMOD: return m_version; case MOD_FOLDER: return "Folder"; @@ -272,27 +314,27 @@ QString Mod::version() const bool Mod::enable(bool value) { - if(m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) + if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) return false; - if(m_enabled == value) + if (m_enabled == value) return false; QString path = m_file.absoluteFilePath(); - if(value) + if (value) { QFile foo(path); - if(!path.endsWith(".disabled")) + if (!path.endsWith(".disabled")) return false; path.chop(9); - if(!foo.rename(path)) + if (!foo.rename(path)) return false; } else { QFile foo(path); path += ".disabled"; - if(!foo.rename(path)) + if (!foo.rename(path)) return false; } m_file = QFileInfo(path); @@ -305,6 +347,5 @@ bool Mod::operator==(const Mod &other) const } bool Mod::strongCompare(const Mod &other) const { - return mmc_id() == other.mmc_id() && - version() == other.version() && type() == other.type(); + return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type(); } diff --git a/logic/Mod.h b/logic/Mod.h index 05d3cea2..2eb2b97a 100644 --- a/logic/Mod.h +++ b/logic/Mod.h @@ -25,6 +25,7 @@ public: 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_LITEMOD, //!< The mod is a litemod }; Mod(const QFileInfo &file); @@ -102,6 +103,7 @@ public: private: void ReadMCModInfo(QByteArray contents); void ReadForgeInfo(QByteArray contents); + void ReadLiteModInfo(QByteArray contents); protected: diff --git a/logic/ModList.cpp b/logic/ModList.cpp index eb7f3128..fd41bcf7 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -255,7 +255,7 @@ bool ModList::installMod(const QFileInfo &filename, int index) auto type = m.type(); 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 || type == Mod::MOD_LITEMOD) { QString newpath = PathCombine(m_dir.path(), filename.fileName()); if (!QFile::copy(filename.filePath(), newpath)) -- cgit v1.2.3 From 654f444f55de58f5cf0477e3b08b8f18e8d60831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 29 Dec 2013 04:17:52 +0100 Subject: Fix various LiteLoader related bugs. --- logic/Mod.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/Mod.cpp b/logic/Mod.cpp index 009a32a4..6732446d 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -227,11 +227,18 @@ void Mod::ReadLiteModInfo(QByteArray contents) QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); auto object = jsonDoc.object(); - m_mod_id = object.value("name").toString(); + if(object.contains("name")) + { + m_mod_id = m_name = object.value("name").toString(); + } if(object.contains("version")) + { m_version=object.value("version").toString(""); + } else + { m_version=object.value("revision").toString(""); + } m_mcversion = object.value("mcversion").toString(); m_authors = object.value("author").toString(); m_description = object.value("description").toString(); -- cgit v1.2.3 From 952b63f68de93e8acf7aab81373661dae8d5098b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 31 Dec 2013 01:24:28 +0100 Subject: Refactor icon lists heavily * Icon list now uses a filesystem watcher for updates * Icon folder is user-customizable * All the little details. ALL OF THEM. --- logic/BaseInstance.cpp | 10 ++ logic/BaseInstance.h | 3 + logic/LegacyInstance.cpp | 2 +- logic/icons/IconList.cpp | 351 +++++++++++++++++++++++++++++++++++++++++++ logic/icons/IconList.h | 77 ++++++++++ logic/icons/MMCIcon.cpp | 89 +++++++++++ logic/icons/MMCIcon.h | 52 +++++++ logic/lists/IconList.cpp | 271 --------------------------------- logic/lists/IconList.h | 54 ------- logic/lists/InstanceList.cpp | 7 +- 10 files changed, 587 insertions(+), 329 deletions(-) create mode 100644 logic/icons/IconList.cpp create mode 100644 logic/icons/IconList.h create mode 100644 logic/icons/MMCIcon.cpp create mode 100644 logic/icons/MMCIcon.h delete mode 100644 logic/lists/IconList.cpp delete mode 100644 logic/lists/IconList.h (limited to 'logic') diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index bc82fee1..b39db03b 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -27,6 +27,7 @@ #include "pathutils.h" #include "lists/MinecraftVersionList.h" +#include "logic/icons/IconList.h" BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, SettingsObject *settings_obj, QObject *parent) @@ -38,6 +39,7 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, settings().registerSetting(new Setting("name", "Unnamed Instance")); settings().registerSetting(new Setting("iconKey", "default")); + connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString))); settings().registerSetting(new Setting("notes", "")); settings().registerSetting(new Setting("lastLaunchTime", 0)); @@ -93,6 +95,14 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, "AutoCloseConsole", globalSettings->getSetting("AutoCloseConsole"))); } +void BaseInstance::iconUpdated(QString key) +{ + if(iconKey() == key) + { + emit propertiesChanged(this); + } +} + void BaseInstance::nuke() { QDir(instanceRoot()).removeRecursively(); diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index 5f426676..01d6dc7d 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -184,6 +184,9 @@ signals: */ void nuked(BaseInstance *inst); +protected slots: + void iconUpdated(QString key); + protected: std::shared_ptr inst_d; }; diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 5c82b837..08d7c147 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -27,7 +27,7 @@ #include "logic/MinecraftProcess.h" #include "logic/LegacyUpdate.h" -#include "logic/lists/IconList.h" +#include "logic/icons/IconList.h" #include "gui/dialogs/LegacyModEditDialog.h" diff --git a/logic/icons/IconList.cpp b/logic/icons/IconList.cpp new file mode 100644 index 00000000..1e692d1d --- /dev/null +++ b/logic/icons/IconList.cpp @@ -0,0 +1,351 @@ +/* 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 "IconList.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SIZE 1024 + +IconList::IconList(QObject *parent) : QAbstractListModel(parent) +{ + // add builtin icons + QDir instance_icons(":/icons/instances/"); + auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); + for (auto file_info : file_info_list) + { + QString key = file_info.baseName(); + addIcon(key, key, file_info.absoluteFilePath(), MMCIcon::Builtin); + } + + m_watcher.reset(new QFileSystemWatcher()); + is_watching = false; + connect(m_watcher.get(), SIGNAL(directoryChanged(QString)), + SLOT(directoryChanged(QString))); + connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString))); + + auto setting = MMC->settings()->getSetting("IconsDir"); + QString path = setting->get().toString(); + connect(setting, SIGNAL(settingChanged(const Setting &, QVariant)), + SLOT(settingChanged(const Setting &, QVariant))); + directoryChanged(path); +} + +void IconList::directoryChanged(const QString &path) +{ + QDir new_dir (path); + if(m_dir.absolutePath() != new_dir.absolutePath()) + { + m_dir.setPath(path); + m_dir.refresh(); + if(is_watching) + stopWatching(); + startWatching(); + } + if(!m_dir.exists()) + if(!ensureFolderPathExists(m_dir.absolutePath())) + return; + m_dir.refresh(); + auto new_list = m_dir.entryList(QDir::Files, QDir::Name); + for (auto it = new_list.begin(); it != new_list.end(); it++) + { + QString &foo = (*it); + foo = m_dir.filePath(foo); + } + auto new_set = new_list.toSet(); + QList current_list; + for (auto &it : icons) + { + if (!it.has(MMCIcon::FileBased)) + continue; + current_list.push_back(it.m_images[MMCIcon::FileBased].filename); + } + QSet current_set = current_list.toSet(); + + QSet to_remove = current_set; + to_remove -= new_set; + + QSet to_add = new_set; + to_add -= current_set; + + for (auto remove : to_remove) + { + QLOG_INFO() << "Removing " << remove; + QFileInfo rmfile(remove); + QString key = rmfile.baseName(); + int idx = getIconIndex(key); + if (idx == -1) + continue; + icons[idx].remove(MMCIcon::FileBased); + if (icons[idx].type() == MMCIcon::ToBeDeleted) + { + beginRemoveRows(QModelIndex(), idx, idx); + icons.remove(idx); + reindex(); + endRemoveRows(); + } + else + { + dataChanged(index(idx), index(idx)); + } + m_watcher->removePath(remove); + emit iconUpdated(key); + } + + for (auto add : to_add) + { + QLOG_INFO() << "Adding " << add; + QFileInfo addfile(add); + QString key = addfile.baseName(); + if (addIcon(key, QString(), addfile.filePath(), MMCIcon::FileBased)) + { + m_watcher->addPath(add); + emit iconUpdated(key); + } + } +} + +void IconList::fileChanged(const QString &path) +{ + QLOG_INFO() << "Checking " << path; + QFileInfo checkfile(path); + if (!checkfile.exists()) + return; + QString key = checkfile.baseName(); + int idx = getIconIndex(key); + if (idx == -1) + return; + QIcon icon(path); + if (!icon.availableSizes().size()) + return; + + icons[idx].m_images[MMCIcon::FileBased].icon = icon; + dataChanged(index(idx), index(idx)); + emit iconUpdated(key); +} + +void IconList::settingChanged(const Setting &setting, QVariant value) +{ + if(setting.configKey() != "IconsDir") + return; + + directoryChanged(value.toString()); +} + +void IconList::startWatching() +{ + auto abs_path = m_dir.absolutePath(); + ensureFolderPathExists(abs_path); + is_watching = m_watcher->addPath(abs_path); + if (is_watching) + { + QLOG_INFO() << "Started watching " << abs_path; + } + else + { + QLOG_INFO() << "Failed to start watching " << abs_path; + } +} + +void IconList::stopWatching() +{ + m_watcher->removePaths(m_watcher->files()); + m_watcher->removePaths(m_watcher->directories()); + is_watching = false; +} + +QStringList IconList::mimeTypes() const +{ + QStringList types; + types << "text/uri-list"; + return types; +} +Qt::DropActions IconList::supportedDropActions() const +{ + return Qt::CopyAction; +} + +bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + // check if the action is supported + if (!data || !(action & supportedDropActions())) + return false; + + // files dropped from outside? + if (data->hasUrls()) + { + auto urls = data->urls(); + QStringList iconFiles; + for (auto url : urls) + { + // only local files may be dropped... + if (!url.isLocalFile()) + continue; + iconFiles += url.toLocalFile(); + } + installIcons(iconFiles); + return true; + } + return false; +} + +Qt::ItemFlags IconList::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); + if (index.isValid()) + return Qt::ItemIsDropEnabled | defaultFlags; + else + return Qt::ItemIsDropEnabled | defaultFlags; +} + +QVariant IconList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + + if (row < 0 || row >= icons.size()) + return QVariant(); + + switch (role) + { + case Qt::DecorationRole: + return icons[row].icon(); + case Qt::DisplayRole: + return icons[row].name(); + case Qt::UserRole: + return icons[row].m_key; + default: + return QVariant(); + } +} + +int IconList::rowCount(const QModelIndex &parent) const +{ + return icons.size(); +} + +void IconList::installIcons(QStringList iconFiles) +{ + for (QString file : iconFiles) + { + QFileInfo fileinfo(file); + if (!fileinfo.isReadable() || !fileinfo.isFile()) + continue; + QString target = PathCombine("icons", fileinfo.fileName()); + + QString suffix = fileinfo.suffix(); + if (suffix != "jpeg" && suffix != "png" && suffix != "jpg") + continue; + + if (!QFile::copy(file, target)) + continue; + } +} + +bool IconList::deleteIcon(QString key) +{ + int iconIdx = getIconIndex(key); + if (iconIdx == -1) + return false; + auto &iconEntry = icons[iconIdx]; + if (iconEntry.has(MMCIcon::FileBased)) + { + return QFile::remove(iconEntry.m_images[MMCIcon::FileBased].filename); + } + return false; +} + +bool IconList::addIcon(QString key, QString name, QString path, MMCIcon::Type type) +{ + // replace the icon even? is the input valid? + QIcon icon(path); + if (!icon.availableSizes().size()) + return false; + auto iter = name_index.find(key); + if (iter != name_index.end()) + { + auto &oldOne = icons[*iter]; + oldOne.replace(type, icon, path); + dataChanged(index(*iter), index(*iter)); + return true; + } + else + { + // add a new icon + beginInsertRows(QModelIndex(), icons.size(), icons.size()); + { + MMCIcon mmc_icon; + mmc_icon.m_name = name; + mmc_icon.m_key = key; + mmc_icon.replace(type, icon, path); + icons.push_back(mmc_icon); + name_index[key] = icons.size() - 1; + } + endInsertRows(); + return true; + } +} + +void IconList::reindex() +{ + name_index.clear(); + int i = 0; + for (auto &iter : icons) + { + name_index[iter.m_key] = i; + i++; + } +} + +QIcon IconList::getIcon(QString key) +{ + int icon_index = getIconIndex(key); + + if (icon_index != -1) + return icons[icon_index].icon(); + + // Fallback for icons that don't exist. + icon_index = getIconIndex("infinity"); + + if (icon_index != -1) + return icons[icon_index].icon(); + return QIcon(); +} + +int IconList::getIconIndex(QString key) +{ + if (key == "default") + key = "infinity"; + + auto iter = name_index.find(key); + if (iter != name_index.end()) + return *iter; + + return -1; +} + +//#include "IconList.moc" \ No newline at end of file diff --git a/logic/icons/IconList.h b/logic/icons/IconList.h new file mode 100644 index 00000000..322411d1 --- /dev/null +++ b/logic/icons/IconList.h @@ -0,0 +1,77 @@ +/* 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 +#include +#include +#include +#include +#include +#include "MMCIcon.h" +#include "setting.h" + +class QFileSystemWatcher; + +class IconList : public QAbstractListModel +{ + Q_OBJECT +public: + explicit IconList(QObject *parent = 0); + virtual ~IconList() {}; + + QIcon getIcon(QString key); + int getIconIndex(QString key); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + + bool addIcon(QString key, QString name, QString path, MMCIcon::Type type); + bool deleteIcon(QString key); + + virtual QStringList mimeTypes() const; + virtual Qt::DropActions supportedDropActions() const; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent); + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + void installIcons(QStringList iconFiles); + + void startWatching(); + void stopWatching(); + +signals: + void iconUpdated(QString key); + +private: + // hide copy constructor + IconList(const IconList &) = delete; + // hide assign op + IconList &operator=(const IconList &) = delete; + void reindex(); + +protected +slots: + void directoryChanged(const QString &path); + void fileChanged(const QString &path); + void settingChanged(const Setting & setting, QVariant value); +private: + std::shared_ptr m_watcher; + bool is_watching; + QMap name_index; + QVector icons; + QDir m_dir; +}; diff --git a/logic/icons/MMCIcon.cpp b/logic/icons/MMCIcon.cpp new file mode 100644 index 00000000..d721513d --- /dev/null +++ b/logic/icons/MMCIcon.cpp @@ -0,0 +1,89 @@ +/* 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 "MMCIcon.h" +#include + +MMCIcon::Type operator--(MMCIcon::Type &t, int) +{ + MMCIcon::Type temp = t; + switch (t) + { + case MMCIcon::Type::Builtin: + t = MMCIcon::Type::ToBeDeleted; + break; + case MMCIcon::Type::Transient: + t = MMCIcon::Type::Builtin; + break; + case MMCIcon::Type::FileBased: + t = MMCIcon::Type::Transient; + break; + default: + { + } + } + return temp; +} + +MMCIcon::Type MMCIcon::type() const +{ + return m_current_type; +} + +QString MMCIcon::name() const +{ + if (m_name.size()) + return m_name; + return m_key; +} + +bool MMCIcon::has(MMCIcon::Type _type) const +{ + return m_images[_type].present(); +} + +QIcon MMCIcon::icon() const +{ + if (m_current_type == Type::ToBeDeleted) + return QIcon(); + return m_images[m_current_type].icon; +} + +void MMCIcon::remove(Type rm_type) +{ + m_images[rm_type].filename = QString(); + m_images[rm_type].icon = QIcon(); + for (auto iter = rm_type; iter != Type::ToBeDeleted; iter--) + { + if (m_images[iter].present()) + { + m_current_type = iter; + return; + } + } + m_current_type = Type::ToBeDeleted; +} + +void MMCIcon::replace(MMCIcon::Type new_type, QIcon icon, QString path) +{ + QFileInfo foo(path); + if (new_type > m_current_type || m_current_type == MMCIcon::ToBeDeleted) + { + m_current_type = new_type; + } + m_images[new_type].icon = icon; + m_images[new_type].changed = foo.lastModified(); + m_images[new_type].filename = path; +} diff --git a/logic/icons/MMCIcon.h b/logic/icons/MMCIcon.h new file mode 100644 index 00000000..5e4b3bb6 --- /dev/null +++ b/logic/icons/MMCIcon.h @@ -0,0 +1,52 @@ +/* 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 +#include +#include +struct MMCImage +{ + QIcon icon; + QString filename; + QDateTime changed; + bool present() const + { + return !icon.isNull(); + } +}; + +struct MMCIcon +{ + enum Type : unsigned + { + Builtin, + Transient, + FileBased, + ICONS_TOTAL, + ToBeDeleted + }; + QString m_key; + QString m_name; + MMCImage m_images[ICONS_TOTAL]; + Type m_current_type = ToBeDeleted; + + Type type() const; + QString name() const; + bool has(Type _type) const; + QIcon icon() const; + void remove(Type rm_type); + void replace(Type new_type, QIcon icon, QString path = QString()); +}; diff --git a/logic/lists/IconList.cpp b/logic/lists/IconList.cpp deleted file mode 100644 index ecfb8c3c..00000000 --- a/logic/lists/IconList.cpp +++ /dev/null @@ -1,271 +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 "IconList.h" -#include -#include -#include -#include -#include -#include -#define MAX_SIZE 1024 - -struct entry -{ - QString key; - QString name; - QIcon icon; - bool is_builtin; - QString filename; -}; - -class Private : public QObject -{ - Q_OBJECT -public: - QMap index; - QVector icons; - Private() - { - } -}; - -IconList::IconList() : QAbstractListModel(), d(new Private()) -{ - QDir instance_icons(":/icons/instances/"); - auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); - for (auto file_info : file_info_list) - { - QString key = file_info.baseName(); - addIcon(key, key, file_info.absoluteFilePath(), true); - } - - // FIXME: get from settings - ensureFolderPathExists("icons"); - QDir user_icons("icons"); - file_info_list = user_icons.entryInfoList(QDir::Files, QDir::Name); - for (auto file_info : file_info_list) - { - QString filename = file_info.absoluteFilePath(); - QString key = file_info.baseName(); - addIcon(key, key, filename); - } -} - -IconList::~IconList() -{ - delete d; - d = nullptr; -} - -QStringList IconList::mimeTypes() const -{ - QStringList types; - types << "text/uri-list"; - return types; -} -Qt::DropActions IconList::supportedDropActions() const -{ - return Qt::CopyAction; -} - -bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent) -{ - if (action == Qt::IgnoreAction) - return true; - // check if the action is supported - if (!data || !(action & supportedDropActions())) - return false; - - // files dropped from outside? - if (data->hasUrls()) - { - /* - bool was_watching = is_watching; - if(was_watching) - stopWatching(); - */ - auto urls = data->urls(); - QStringList iconFiles; - for (auto url : urls) - { - // only local files may be dropped... - if (!url.isLocalFile()) - continue; - iconFiles += url.toLocalFile(); - } - installIcons(iconFiles); - /* - if(was_watching) - startWatching(); - */ - return true; - } - return false; -} - -Qt::ItemFlags IconList::flags(const QModelIndex &index) const -{ - Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); - if (index.isValid()) - return Qt::ItemIsDropEnabled | defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; -} - -QVariant IconList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - - if (row < 0 || row >= d->icons.size()) - return QVariant(); - - switch (role) - { - case Qt::DecorationRole: - return d->icons[row].icon; - case Qt::DisplayRole: - return d->icons[row].name; - case Qt::UserRole: - return d->icons[row].key; - default: - return QVariant(); - } -} - -int IconList::rowCount(const QModelIndex &parent) const -{ - return d->icons.size(); -} - -void IconList::installIcons(QStringList iconFiles) -{ - for (QString file : iconFiles) - { - QFileInfo fileinfo(file); - if (!fileinfo.isReadable() || !fileinfo.isFile()) - continue; - QString target = PathCombine("icons", fileinfo.fileName()); - - QString suffix = fileinfo.suffix(); - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg") - continue; - - if (!QFile::copy(file, target)) - continue; - - QString key = fileinfo.baseName(); - addIcon(key, key, target); - } -} - -bool IconList::deleteIcon(QString key) -{ - int iconIdx = getIconIndex(key); - if (iconIdx == -1) - return false; - auto &iconEntry = d->icons[iconIdx]; - if (iconEntry.is_builtin) - return false; - if (QFile::remove(iconEntry.filename)) - { - beginRemoveRows(QModelIndex(), iconIdx, iconIdx); - d->icons.remove(iconIdx); - reindex(); - endRemoveRows(); - } - return true; -} - -bool IconList::addIcon(QString key, QString name, QString path, bool is_builtin) -{ - auto iter = d->index.find(key); - if (iter != d->index.end()) - { - if (d->icons[*iter].is_builtin) - return false; - - QIcon icon(path); - if (icon.isNull()) - return false; - - auto &oldOne = d->icons[*iter]; - - if (!QFile::remove(oldOne.filename)) - return false; - - // replace the icon - oldOne = {key, name, icon, is_builtin, path}; - dataChanged(index(*iter), index(*iter)); - return true; - } - else - { - QIcon icon(path); - if (icon.isNull()) - return false; - - // add a new icon - beginInsertRows(QModelIndex(), d->icons.size(), d->icons.size()); - d->icons.push_back({key, name, icon, is_builtin, path}); - d->index[key] = d->icons.size() - 1; - endInsertRows(); - return true; - } -} - -void IconList::reindex() -{ - d->index.clear(); - int i = 0; - for (auto &iter : d->icons) - { - d->index[iter.key] = i; - i++; - } -} - -QIcon IconList::getIcon(QString key) -{ - int icon_index = getIconIndex(key); - - if (icon_index != -1) - return d->icons[icon_index].icon; - - // Fallback for icons that don't exist. - icon_index = getIconIndex("infinity"); - - if (icon_index != -1) - return d->icons[icon_index].icon; - return QIcon(); -} - -int IconList::getIconIndex(QString key) -{ - if (key == "default") - key = "infinity"; - - auto iter = d->index.find(key); - if (iter != d->index.end()) - return *iter; - - return -1; -} - -#include "IconList.moc" \ No newline at end of file diff --git a/logic/lists/IconList.h b/logic/lists/IconList.h deleted file mode 100644 index 40ad043b..00000000 --- a/logic/lists/IconList.h +++ /dev/null @@ -1,54 +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 -#include -#include - -class Private; - -class IconList : public QAbstractListModel -{ -public: - IconList(); - virtual ~IconList(); - - QIcon getIcon(QString key); - int getIconIndex(QString key); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - - bool addIcon(QString key, QString name, QString path, bool is_builtin = false); - bool deleteIcon(QString key); - - virtual QStringList mimeTypes() const; - virtual Qt::DropActions supportedDropActions() const; - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent); - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - void installIcons(QStringList iconFiles); - -private: - // hide copy constructor - IconList(const IconList &) = delete; - // hide assign op - IconList &operator=(const IconList &) = delete; - void reindex(); - Private *d; -}; diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 0ecb387d..48a2865a 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -28,7 +28,7 @@ #include "MultiMC.h" #include "logic/lists/InstanceList.h" -#include "logic/lists/IconList.h" +#include "logic/icons/IconList.h" #include "logic/lists/MinecraftVersionList.h" #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" @@ -356,12 +356,13 @@ void InstanceList::loadForgeInstances(QMap groupMap) QString iconKey = record.logo; iconKey.remove(QRegularExpression("\\..*")); - MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo), true); + MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo), + MMCIcon::Transient); if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists()) { BaseInstance *instPtr = NULL; - auto & factory = InstanceFactory::get(); + auto &factory = InstanceFactory::get(); auto version = MMC->minecraftlist()->findVersion(record.mcVersion); if (!version) { -- cgit v1.2.3 From 396e63500ec189b003d36504bae91a2b6a78a69d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 1 Jan 2014 15:08:40 +0100 Subject: Allow the use of synonyms in settings. Refactor settings. Remove a bunch of obsolete/unused code. --- logic/BaseInstance.cpp | 58 +++++++++++++++++++---------------------------- logic/InstanceFactory.cpp | 6 ++--- logic/LegacyInstance.cpp | 10 ++++---- logic/OneSixInstance.cpp | 4 ++-- logic/icons/IconList.cpp | 6 ++--- 5 files changed, 36 insertions(+), 48 deletions(-) (limited to 'logic') diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index b39db03b..ac66a8d5 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -37,11 +37,11 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, d->m_settings = settings_obj; d->m_rootDir = rootDir; - settings().registerSetting(new Setting("name", "Unnamed Instance")); - settings().registerSetting(new Setting("iconKey", "default")); + settings().registerSetting("name", "Unnamed Instance"); + settings().registerSetting("iconKey", "default"); connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString))); - settings().registerSetting(new Setting("notes", "")); - settings().registerSetting(new Setting("lastLaunchTime", 0)); + settings().registerSetting("notes", ""); + settings().registerSetting("lastLaunchTime", 0); /* * custom base jar has no default. it is determined in code... see the accessor methods for @@ -50,49 +50,37 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, * for instances that DO NOT have the CustomBaseJar setting (legacy instances), * [.]minecraft/bin/mcbackup.jar is the default base jar */ - settings().registerSetting(new Setting("UseCustomBaseJar", true)); - settings().registerSetting(new Setting("CustomBaseJar", "")); + settings().registerSetting("UseCustomBaseJar", true); + settings().registerSetting("CustomBaseJar", ""); auto globalSettings = MMC->settings(); // Java Settings - settings().registerSetting(new Setting("OverrideJava", false)); - settings().registerSetting( - new OverrideSetting("JavaPath", globalSettings->getSetting("JavaPath"))); - settings().registerSetting( - new OverrideSetting("JvmArgs", globalSettings->getSetting("JvmArgs"))); + settings().registerSetting("OverrideJava", false); + settings().registerOverride(globalSettings->getSetting("JavaPath")); + settings().registerOverride(globalSettings->getSetting("JvmArgs")); // Custom Commands - settings().registerSetting(new Setting("OverrideCommands", false)); - settings().registerSetting(new OverrideSetting( - "PreLaunchCommand", globalSettings->getSetting("PreLaunchCommand"))); - settings().registerSetting( - new OverrideSetting("PostExitCommand", globalSettings->getSetting("PostExitCommand"))); + settings().registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false); + settings().registerOverride(globalSettings->getSetting("PreLaunchCommand")); + settings().registerOverride(globalSettings->getSetting("PostExitCommand")); // Window Size - settings().registerSetting(new Setting("OverrideWindow", false)); - settings().registerSetting( - new OverrideSetting("LaunchMaximized", globalSettings->getSetting("LaunchMaximized"))); - settings().registerSetting(new OverrideSetting( - "MinecraftWinWidth", globalSettings->getSetting("MinecraftWinWidth"))); - settings().registerSetting(new OverrideSetting( - "MinecraftWinHeight", globalSettings->getSetting("MinecraftWinHeight"))); + settings().registerSetting("OverrideWindow", false); + settings().registerOverride(globalSettings->getSetting("LaunchMaximized")); + settings().registerOverride(globalSettings->getSetting("MinecraftWinWidth")); + settings().registerOverride(globalSettings->getSetting("MinecraftWinHeight")); // Memory - settings().registerSetting(new Setting("OverrideMemory", false)); - settings().registerSetting( - new OverrideSetting("MinMemAlloc", globalSettings->getSetting("MinMemAlloc"))); - settings().registerSetting( - new OverrideSetting("MaxMemAlloc", globalSettings->getSetting("MaxMemAlloc"))); - settings().registerSetting( - new OverrideSetting("PermGen", globalSettings->getSetting("PermGen"))); + settings().registerSetting("OverrideMemory", false); + settings().registerOverride(globalSettings->getSetting("MinMemAlloc")); + settings().registerOverride(globalSettings->getSetting("MaxMemAlloc")); + settings().registerOverride(globalSettings->getSetting("PermGen")); // Console - settings().registerSetting(new Setting("OverrideConsole", false)); - settings().registerSetting( - new OverrideSetting("ShowConsole", globalSettings->getSetting("ShowConsole"))); - settings().registerSetting(new OverrideSetting( - "AutoCloseConsole", globalSettings->getSetting("AutoCloseConsole"))); + settings().registerSetting("OverrideConsole", false); + settings().registerOverride(globalSettings->getSetting("ShowConsole")); + settings().registerOverride(globalSettings->getSetting("AutoCloseConsole")); } void BaseInstance::iconUpdated(QString key) diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 7c778035..1f1a5879 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -45,7 +45,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg")); - m_settings->registerSetting(new Setting("InstanceType", "Legacy")); + m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); @@ -94,7 +94,7 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& return InstanceFactory::NoSuchVersion; auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg")); - m_settings->registerSetting(new Setting("InstanceType", "Legacy")); + m_settings->registerSetting("InstanceType", "Legacy"); if (type == NormalInst) { @@ -171,7 +171,7 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne return InstanceFactory::CantCreateDir; } auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg")); - m_settings->registerSetting(new Setting("InstanceType", "Legacy")); + m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); if(inst_type == "OneSixFTB") diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 08d7c147..0bc0961e 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -37,11 +37,11 @@ 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)); - settings->registerSetting(new Setting("JarVersion", "Unknown")); - settings->registerSetting(new Setting("LwjglVersion", "2.9.0")); - settings->registerSetting(new Setting("IntendedJarVersion", "")); + settings->registerSetting("NeedsRebuild", true); + settings->registerSetting("ShouldUpdate", false); + settings->registerSetting("JarVersion", "Unknown"); + settings->registerSetting("LwjglVersion", "2.9.0"); + settings->registerSetting("IntendedJarVersion", ""); } std::shared_ptr LegacyInstance::doUpdate(bool only_prepare) diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index fd41b9e5..2392c683 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -33,8 +33,8 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o : BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent) { I_D(OneSixInstance); - d->m_settings->registerSetting(new Setting("IntendedVersion", "")); - d->m_settings->registerSetting(new Setting("ShouldUpdate", false)); + d->m_settings->registerSetting("IntendedVersion", ""); + d->m_settings->registerSetting("ShouldUpdate", false); reloadFullVersion(); } diff --git a/logic/icons/IconList.cpp b/logic/icons/IconList.cpp index 1e692d1d..3a745662 100644 --- a/logic/icons/IconList.cpp +++ b/logic/icons/IconList.cpp @@ -42,10 +42,10 @@ IconList::IconList(QObject *parent) : QAbstractListModel(parent) connect(m_watcher.get(), SIGNAL(directoryChanged(QString)), SLOT(directoryChanged(QString))); connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString))); - + auto setting = MMC->settings()->getSetting("IconsDir"); QString path = setting->get().toString(); - connect(setting, SIGNAL(settingChanged(const Setting &, QVariant)), + connect(setting.get(), SIGNAL(settingChanged(const Setting &, QVariant)), SLOT(settingChanged(const Setting &, QVariant))); directoryChanged(path); } @@ -145,7 +145,7 @@ void IconList::fileChanged(const QString &path) void IconList::settingChanged(const Setting &setting, QVariant value) { - if(setting.configKey() != "IconsDir") + if(setting.id() != "IconsDir") return; directoryChanged(value.toString()); -- cgit v1.2.3 From 821fb5e0ae5b231224fdc56a053109b675006f34 Mon Sep 17 00:00:00 2001 From: Sky Date: Wed, 1 Jan 2014 14:56:26 +0000 Subject: Move assets migration to a task. Needs threading. --- logic/assets/AssetsMigrateTask.cpp | 141 +++++++++++++++++++++++++++++++++++++ logic/assets/AssetsMigrateTask.h | 18 +++++ logic/assets/AssetsUtils.cpp | 83 ++-------------------- logic/assets/AssetsUtils.h | 2 +- 4 files changed, 165 insertions(+), 79 deletions(-) create mode 100644 logic/assets/AssetsMigrateTask.cpp create mode 100644 logic/assets/AssetsMigrateTask.h (limited to 'logic') diff --git a/logic/assets/AssetsMigrateTask.cpp b/logic/assets/AssetsMigrateTask.cpp new file mode 100644 index 00000000..be45718d --- /dev/null +++ b/logic/assets/AssetsMigrateTask.cpp @@ -0,0 +1,141 @@ +#include "AssetsMigrateTask.h" +#include "MultiMC.h" +#include "logger/QsLog.h" +#include +#include +#include +#include "gui/dialogs/CustomMessageBox.h" +#include + +AssetsMigrateTask::AssetsMigrateTask(int expected, QObject *parent) + : Task(parent) +{ + this->m_expected = expected; +} + +void AssetsMigrateTask::executeTask() +{ + this->setStatus(tr("Migrating legacy assets...")); + this->setProgress(0); + + QDir assets_dir("assets"); + if (!assets_dir.exists()) + { + emitFailed("Assets directory didn't exist"); + return; + } + assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + int base_length = assets_dir.path().length(); + + QList blacklist = {"indexes", "objects", "virtual"}; + + if (!assets_dir.exists("objects")) + assets_dir.mkdir("objects"); + QDir objects_dir("assets/objects"); + + QDirIterator iterator(assets_dir, QDirIterator::Subdirectories); + int successes = 0; + int failures = 0; + while (iterator.hasNext()) + { + QString currentDir = iterator.next(); + currentDir = currentDir.remove(0, base_length + 1); + + bool ignore = false; + for (QString blacklisted : blacklist) + { + if (currentDir.startsWith(blacklisted)) + ignore = true; + } + + if (!iterator.fileInfo().isDir() && !ignore) + { + QString filename = iterator.filePath(); + + QFile input(filename); + input.open(QIODevice::ReadOnly | QIODevice::WriteOnly); + QString sha1sum = + QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1) + .toHex() + .constData(); + + QString object_name = filename.remove(0, base_length + 1); + QLOG_DEBUG() << "Processing" << object_name << ":" << sha1sum << input.size(); + + QString object_tlk = sha1sum.left(2); + QString object_tlk_dir = objects_dir.path() + "/" + object_tlk; + + QDir tlk_dir(object_tlk_dir); + if (!tlk_dir.exists()) + objects_dir.mkdir(object_tlk); + + QString new_filename = tlk_dir.path() + "/" + sha1sum; + QFile new_object(new_filename); + if (!new_object.exists()) + { + bool rename_success = input.rename(new_filename); + QLOG_DEBUG() << " Doesn't exist, copying to" << new_filename << ":" + << QString::number(rename_success); + if (rename_success) + successes++; + else + failures++; + } + else + { + input.remove(); + QLOG_DEBUG() << " Already exists, deleting original and not copying."; + } + + this->setProgress(100 * ((successes + failures) / (float) this->m_expected)); + } + } + + if (successes + failures == 0) + { + this->setProgress(100); + QLOG_DEBUG() << "No legacy assets needed importing."; + } + else + { + QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and" + << failures << "failures."; + + this->setStatus("Cleaning up legacy assets..."); + this->setProgress(100); + + QDirIterator cleanup_iterator(assets_dir); + + while (cleanup_iterator.hasNext()) + { + QString currentDir = cleanup_iterator.next(); + currentDir = currentDir.remove(0, base_length + 1); + + bool ignore = false; + for (QString blacklisted : blacklist) + { + if (currentDir.startsWith(blacklisted)) + ignore = true; + } + + if (cleanup_iterator.fileInfo().isDir() && !ignore) + { + QString path = cleanup_iterator.filePath(); + QDir folder(path); + + QLOG_DEBUG() << "Cleaning up legacy assets folder:" << path; + + folder.removeRecursively(); + } + } + } + + if(failures > 0) + { + emitFailed(QString("Failed to migrate %1 legacy assets").arg(failures)); + } + else + { + emitSucceeded(); + } +} diff --git a/logic/assets/AssetsMigrateTask.h b/logic/assets/AssetsMigrateTask.h new file mode 100644 index 00000000..d8d58c97 --- /dev/null +++ b/logic/assets/AssetsMigrateTask.h @@ -0,0 +1,18 @@ +#pragma once +#include "logic/tasks/Task.h" +#include +#include +#include + +class AssetsMigrateTask : public Task +{ + Q_OBJECT +public: + explicit AssetsMigrateTask(int expected, QObject* parent=0); + +protected: + virtual void executeTask(); + +private: + int m_expected; +}; diff --git a/logic/assets/AssetsUtils.cpp b/logic/assets/AssetsUtils.cpp index 11d928cf..bca7773d 100644 --- a/logic/assets/AssetsUtils.cpp +++ b/logic/assets/AssetsUtils.cpp @@ -25,23 +25,18 @@ namespace AssetsUtils { -void migrateOldAssets() +int findLegacyAssets() { QDir assets_dir("assets"); if (!assets_dir.exists()) - return; + return 0; assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); int base_length = assets_dir.path().length(); QList blacklist = {"indexes", "objects", "virtual"}; - if (!assets_dir.exists("objects")) - assets_dir.mkdir("objects"); - QDir objects_dir("assets/objects"); - QDirIterator iterator(assets_dir, QDirIterator::Subdirectories); - int successes = 0; - int failures = 0; + int found = 0; while (iterator.hasNext()) { QString currentDir = iterator.next(); @@ -56,79 +51,11 @@ void migrateOldAssets() if (!iterator.fileInfo().isDir() && !ignore) { - QString filename = iterator.filePath(); - - QFile input(filename); - input.open(QIODevice::ReadOnly | QIODevice::WriteOnly); - QString sha1sum = - QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1) - .toHex() - .constData(); - - QString object_name = filename.remove(0, base_length + 1); - QLOG_DEBUG() << "Processing" << object_name << ":" << sha1sum << input.size(); - - QString object_tlk = sha1sum.left(2); - QString object_tlk_dir = objects_dir.path() + "/" + object_tlk; - - QDir tlk_dir(object_tlk_dir); - if (!tlk_dir.exists()) - objects_dir.mkdir(object_tlk); - - QString new_filename = tlk_dir.path() + "/" + sha1sum; - QFile new_object(new_filename); - if (!new_object.exists()) - { - bool rename_success = input.rename(new_filename); - QLOG_DEBUG() << " Doesn't exist, copying to" << new_filename << ":" - << QString::number(rename_success); - if (rename_success) - successes++; - else - failures++; - } - else - { - input.remove(); - QLOG_DEBUG() << " Already exists, deleting original and not copying."; - } + found++; } } - if (successes + failures == 0) - { - QLOG_DEBUG() << "No legacy assets needed importing."; - } - else - { - QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and" - << failures << "failures."; - - QDirIterator cleanup_iterator(assets_dir); - - while (cleanup_iterator.hasNext()) - { - QString currentDir = cleanup_iterator.next(); - currentDir = currentDir.remove(0, base_length + 1); - - bool ignore = false; - for (QString blacklisted : blacklist) - { - if (currentDir.startsWith(blacklisted)) - ignore = true; - } - - if (cleanup_iterator.fileInfo().isDir() && !ignore) - { - QString path = cleanup_iterator.filePath(); - QDir folder(path); - - QLOG_DEBUG() << "Cleaning up legacy assets folder:" << path; - - folder.removeRecursively(); - } - } - } + return found; } /* diff --git a/logic/assets/AssetsUtils.h b/logic/assets/AssetsUtils.h index 5276d5a5..aaacc2db 100644 --- a/logic/assets/AssetsUtils.h +++ b/logic/assets/AssetsUtils.h @@ -34,6 +34,6 @@ struct AssetsIndex namespace AssetsUtils { -void migrateOldAssets(); bool loadAssetsIndexJson(QString file, AssetsIndex* index); +int findLegacyAssets(); } -- cgit v1.2.3 From 0e78d34d4cbed0ceb913ee70e89f36462bfe1dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 1 Jan 2014 19:21:58 +0100 Subject: ThreadTask for all your ThreadNeeds Use only when absolutely required. --- logic/assets/AssetsMigrateTask.cpp | 2 ++ logic/tasks/ThreadTask.cpp | 41 ++++++++++++++++++++++++++++++++++++++ logic/tasks/ThreadTask.h | 25 +++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 logic/tasks/ThreadTask.cpp create mode 100644 logic/tasks/ThreadTask.h (limited to 'logic') diff --git a/logic/assets/AssetsMigrateTask.cpp b/logic/assets/AssetsMigrateTask.cpp index be45718d..7c1f5204 100644 --- a/logic/assets/AssetsMigrateTask.cpp +++ b/logic/assets/AssetsMigrateTask.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "gui/dialogs/CustomMessageBox.h" #include @@ -139,3 +140,4 @@ void AssetsMigrateTask::executeTask() emitSucceeded(); } } + diff --git a/logic/tasks/ThreadTask.cpp b/logic/tasks/ThreadTask.cpp new file mode 100644 index 00000000..ddd1dee5 --- /dev/null +++ b/logic/tasks/ThreadTask.cpp @@ -0,0 +1,41 @@ +#include "ThreadTask.h" +#include +ThreadTask::ThreadTask(Task * internal, QObject *parent) : Task(parent), m_internal(internal) +{ +} + +void ThreadTask::start() +{ + connect(m_internal, SIGNAL(failed(QString)), SLOT(iternal_failed(QString))); + connect(m_internal, SIGNAL(progress(qint64,qint64)), SLOT(iternal_progress(qint64,qint64))); + connect(m_internal, SIGNAL(started()), SLOT(iternal_started())); + connect(m_internal, SIGNAL(status(QString)), SLOT(iternal_status(QString))); + connect(m_internal, SIGNAL(succeeded()), SLOT(iternal_succeeded())); + m_running = true; + QtConcurrent::run(m_internal, &Task::start); +} + +void ThreadTask::iternal_failed(QString reason) +{ + emitFailed(reason); +} + +void ThreadTask::iternal_progress(qint64 current, qint64 total) +{ + progress(current, total); +} + +void ThreadTask::iternal_started() +{ + emit started(); +} + +void ThreadTask::iternal_status(QString status) +{ + setStatus(status); +} + +void ThreadTask::iternal_succeeded() +{ + emitSucceeded(); +} diff --git a/logic/tasks/ThreadTask.h b/logic/tasks/ThreadTask.h new file mode 100644 index 00000000..718dbc91 --- /dev/null +++ b/logic/tasks/ThreadTask.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Task.h" + +class ThreadTask : public Task +{ + Q_OBJECT +public: + explicit ThreadTask(Task * internal, QObject * parent = nullptr); + +protected: + void executeTask() {}; + +public slots: + virtual void start(); + +private slots: + void iternal_started(); + void iternal_progress(qint64 current, qint64 total); + void iternal_succeeded(); + void iternal_failed(QString reason); + void iternal_status(QString status); +private: + Task * m_internal; +}; \ No newline at end of file -- cgit v1.2.3 From cdebeff179b78b9acb013cea7a9b71f45bef751d Mon Sep 17 00:00:00 2001 From: Noah Mayr Date: Thu, 2 Jan 2014 17:01:26 +0100 Subject: Improved java detection on OSX. --- logic/JavaUtils.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'logic') diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index e1b3bc64..8005c375 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -181,6 +181,8 @@ QList JavaUtils::FindJavaPaths() QList javas; javas.append(this->GetDefaultJava()->path); + javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); + javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); return javas; } -- cgit v1.2.3 From 092b15fc8716af61472ffdd953429792183b350d Mon Sep 17 00:00:00 2001 From: max96at Date: Thu, 2 Jan 2014 17:07:47 +0100 Subject: Removed incomplete osx java detection log message --- logic/JavaUtils.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'logic') diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index 8005c375..cf47df6f 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -177,8 +177,6 @@ QList JavaUtils::FindJavaPaths() #elif OSX QList JavaUtils::FindJavaPaths() { - QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\""; - QList javas; javas.append(this->GetDefaultJava()->path); javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); -- cgit v1.2.3 From c0b6fd0647fe90084f3d2750cb26e3b9cb611266 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 2 Jan 2014 18:51:40 +0100 Subject: Make .ico files usable (the plugin is already included) --- logic/icons/IconList.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'logic') diff --git a/logic/icons/IconList.cpp b/logic/icons/IconList.cpp index 3a745662..cda2db7b 100644 --- a/logic/icons/IconList.cpp +++ b/logic/icons/IconList.cpp @@ -258,7 +258,7 @@ void IconList::installIcons(QStringList iconFiles) QString target = PathCombine("icons", fileinfo.fileName()); QString suffix = fileinfo.suffix(); - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg") + if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico") continue; if (!QFile::copy(file, target)) @@ -348,4 +348,4 @@ int IconList::getIconIndex(QString key) return -1; } -//#include "IconList.moc" \ No newline at end of file +//#include "IconList.moc" -- cgit v1.2.3