diff --git a/config/schemas/mod.json b/config/schemas/mod.json index 2c1fc8501..5415d0275 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -22,6 +22,10 @@ "type" : "string", "description" : "Author of the mod. Can be nickname, real name or name of team" }, + "downloadSize": { + "type" : "number", + "description" : "Approximate size of mod, compressed by zip algorithm, in Mb" + }, "changelog" : { "type" : "object", "description" : "List of changes/new features in each version", diff --git a/launcher/modManager/cdownloadmanager_moc.cpp b/launcher/modManager/cdownloadmanager_moc.cpp index a190bf757..3a9c86471 100644 --- a/launcher/modManager/cdownloadmanager_moc.cpp +++ b/launcher/modManager/cdownloadmanager_moc.cpp @@ -18,13 +18,13 @@ CDownloadManager::CDownloadManager() SLOT(downloadFinished(QNetworkReply *))); } -void CDownloadManager::downloadFile(const QUrl & url, const QString & file) +void CDownloadManager::downloadFile(const QUrl & url, const QString & file, qint64 bytesTotal) { QNetworkRequest request(url); FileEntry entry; entry.file.reset(new QFile(CLauncherDirs::get().downloadsPath() + '/' + file)); entry.bytesReceived = 0; - entry.totalSize = 0; + entry.totalSize = bytesTotal; entry.filename = file; if(entry.file->open(QIODevice::WriteOnly | QIODevice::Truncate)) @@ -79,7 +79,7 @@ void CDownloadManager::downloadFinished(QNetworkReply * reply) break; } } - downloadFile(qurl, filename); + downloadFile(qurl, filename, file.totalSize); return; } @@ -131,7 +131,8 @@ void CDownloadManager::downloadProgressChanged(qint64 bytesReceived, qint64 byte entry.file->write(entry.reply->readAll()); entry.bytesReceived = bytesReceived; - entry.totalSize = bytesTotal; + if(bytesTotal) + entry.totalSize = bytesTotal; quint64 total = 0; for(auto & entry : currentDownloads) @@ -140,11 +141,14 @@ void CDownloadManager::downloadProgressChanged(qint64 bytesReceived, qint64 byte quint64 received = 0; for(auto & entry : currentDownloads) received += entry.bytesReceived > 0 ? entry.bytesReceived : 0; + + if(received > total) + total = received; emit downloadProgress(received, total); } -bool CDownloadManager::downloadInProgress(const QUrl & url) +bool CDownloadManager::downloadInProgress(const QUrl & url) const { for(auto & entry : currentDownloads) { diff --git a/launcher/modManager/cdownloadmanager_moc.h b/launcher/modManager/cdownloadmanager_moc.h index 6d33ba8b3..8c0e8d664 100644 --- a/launcher/modManager/cdownloadmanager_moc.h +++ b/launcher/modManager/cdownloadmanager_moc.h @@ -48,10 +48,10 @@ public: // returns true if download with such URL is in progress/queued // FIXME: not sure what's right place for "mod download in progress" check - bool downloadInProgress(const QUrl & url); + bool downloadInProgress(const QUrl & url) const; // returns network reply so caller can connect to required signals - void downloadFile(const QUrl & url, const QString & file); + void downloadFile(const QUrl & url, const QString & file, qint64 bytesTotal = 0); public slots: void downloadFinished(QNetworkReply * reply); diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index 8733adc42..b6cf4cc07 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -18,12 +18,9 @@ QString CModEntry::sizeToString(double size) { - static const QString sizes[] = - { - /*"%1 B", */ "%1 KiB", "%1 MiB", "%1 GiB", "%1 TiB" - }; + static const std::array sizes { "%1 B", "%1 KiB", "%1 MiB", "%1 GiB", "%1 TiB" }; size_t index = 0; - while(size > 1024 && index < 4) + while(size > 1024 && index < sizes.size()) { size /= 1024; index++; @@ -285,9 +282,10 @@ CModEntry CModList::getMod(QString modname) const || CModVersion::fromString(repo["version"].toString().toStdString()) < CModVersion::fromString(repoValMap["version"].toString().toStdString())) { - //take valid download link and screenshots before assignment + //take valid download link, screenshots and mod size before assignment auto download = repo.value("download"); auto screenshots = repo.value("screenshots"); + auto size = repo.value("downloadSize"); repo = repoValMap; if(repo.value("download").isNull()) { @@ -295,6 +293,8 @@ CModEntry CModList::getMod(QString modname) const if(repo.value("screenshots").isNull()) //taking screenshot from the downloadable version repo["screenshots"] = screenshots; } + if(repo.value("downloadSize").isNull()) + repo["downloadSize"] = size; } } } diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 665f60e62..9a79ce03a 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -27,6 +27,11 @@ #include "../../lib/Languages.h" #include "../../lib/modding/CModVersion.h" +static double mbToBytes(double mb) +{ + return mb * 1024 * 1024; +} + void CModListView::setupModModel() { modModel = new CModListModel(this); @@ -244,8 +249,11 @@ QString CModListView::genModInfoText(CModEntry & mod) result += replaceIfNotEmpty(mod.getValue("installedVersion"), lineTemplate.arg(tr("Installed version"))); result += replaceIfNotEmpty(mod.getValue("latestVersion"), lineTemplate.arg(tr("Latest version"))); - if(mod.getValue("size").isValid()) - result += replaceIfNotEmpty(CModEntry::sizeToString(mod.getValue("size").toDouble()), lineTemplate.arg(tr("Download size"))); + if(mod.getValue("localSizeBytes").isValid()) + result += replaceIfNotEmpty(CModEntry::sizeToString(mod.getValue("localSizeBytes").toDouble()), lineTemplate.arg(tr("downloadSize"))); + if((mod.isAvailable() || mod.isUpdateable()) && mod.getValue("downloadSize").isValid()) + result += replaceIfNotEmpty(CModEntry::sizeToString(mbToBytes(mod.getValue("downloadSize").toDouble())), lineTemplate.arg(tr("Download size"))); + result += replaceIfNotEmpty(mod.getValue("author"), lineTemplate.arg(tr("Authors"))); if(mod.getValue("licenseURL").isValid()) @@ -536,7 +544,7 @@ void CModListView::on_updateButton_clicked() auto mod = modModel->getMod(name); // update required mod, install missing (can be new dependency) if(mod.isUpdateable() || !mod.isInstalled()) - downloadFile(name + ".zip", mod.getValue("download").toString(), "mods"); + downloadFile(name + ".zip", mod.getValue("download").toString(), "mods", mbToBytes(mod.getValue("downloadSize").toDouble())); } } @@ -566,11 +574,11 @@ void CModListView::on_installButton_clicked() { auto mod = modModel->getMod(name); if(!mod.isInstalled()) - downloadFile(name + ".zip", mod.getValue("download").toString(), "mods"); + downloadFile(name + ".zip", mod.getValue("download").toString(), "mods", mbToBytes(mod.getValue("downloadSize").toDouble())); } } -void CModListView::downloadFile(QString file, QString url, QString description) +void CModListView::downloadFile(QString file, QString url, QString description, qint64 size) { if(!dlManager) { @@ -585,28 +593,28 @@ void CModListView::downloadFile(QString file, QString url, QString description) connect(modModel, &CModListModel::dataChanged, filterModel, &QAbstractItemModel::dataChanged); - - QString progressBarFormat = "Downloading %s%. %p% (%v KB out of %m KB) finished"; + + QString progressBarFormat = tr("Downloading %s%. %p% (%v MB out of %m MB) finished"); progressBarFormat.replace("%s%", description); ui->progressBar->setFormat(progressBarFormat); } - dlManager->downloadFile(QUrl(url), file); + dlManager->downloadFile(QUrl(url), file, size); } void CModListView::downloadProgress(qint64 current, qint64 max) { - // display progress, in kilobytes - ui->progressBar->setMaximum(max / 1024); - ui->progressBar->setValue(current / 1024); + // display progress, in megabytes + ui->progressBar->setMaximum(max / (1024 * 1024)); + ui->progressBar->setValue(current / (1024 * 1024)); } void CModListView::downloadFinished(QStringList savedFiles, QStringList failedFiles, QStringList errors) { - QString title = "Download failed"; - QString firstLine = "Unable to download all files.\n\nEncountered errors:\n\n"; - QString lastLine = "\n\nInstall successfully downloaded?"; + QString title = tr("Download failed"); + QString firstLine = tr("Unable to download all files.\n\nEncountered errors:\n\n"); + QString lastLine = tr("\n\nInstall successfully downloaded?"); bool doInstallFiles = false; // if all files were d/loaded there should be no errors. And on failure there must be an error @@ -789,8 +797,8 @@ void CModListView::checkManagerErrors() QString errors = manager->getErrors().join('\n'); if(errors.size() != 0) { - QString title = "Operation failed"; - QString description = "Encountered errors:\n" + errors; + QString title = tr("Operation failed"); + QString description = tr("Encountered errors:\n") + errors; QMessageBox::warning(this, title, description, QMessageBox::Ok, QMessageBox::Ok); } } @@ -856,7 +864,7 @@ void CModListView::doInstallMod(const QString & modName) { auto mod = modModel->getMod(name); if(!mod.isInstalled()) - downloadFile(name + ".zip", mod.getValue("download").toString(), "mods"); + downloadFile(name + ".zip", mod.getValue("download").toString(), "mods", mbToBytes(mod.getValue("downloadSize").toDouble())); } } diff --git a/launcher/modManager/cmodlistview_moc.h b/launcher/modManager/cmodlistview_moc.h index c645d503f..a1538755d 100644 --- a/launcher/modManager/cmodlistview_moc.h +++ b/launcher/modManager/cmodlistview_moc.h @@ -51,7 +51,7 @@ class CModListView : public QWidget // find mods that depend on this one QStringList findDependentMods(QString mod, bool excludeDisabled); - void downloadFile(QString file, QString url, QString description); + void downloadFile(QString file, QString url, QString description, qint64 size = 0); void installMods(QStringList archives); void installFiles(QStringList mods); diff --git a/launcher/modManager/cmodmanager.cpp b/launcher/modManager/cmodmanager.cpp index 82ec45d56..8ba55876b 100644 --- a/launcher/modManager/cmodmanager.cpp +++ b/launcher/modManager/cmodmanager.cpp @@ -87,15 +87,23 @@ void CModManager::loadMods() auto resID = CModInfo::getModFile(modname); if(CResourceHandler::get()->existsResource(resID)) { - boost::filesystem::path name = *CResourceHandler::get()->getResourceName(resID); - auto mod = JsonUtils::JsonFromFile(pathToQString(name)); - if(!name.is_absolute()) + //calculate mod size + qint64 total = 0; + ResourcePath resDir(CModInfo::getModDir(modname), EResType::DIRECTORY); + if(CResourceHandler::get()->existsResource(resDir)) { - auto json = JsonUtils::toJson(mod); - json["storedLocaly"].Bool() = true; - mod = JsonUtils::toVariant(json); + for(QDirIterator iter(QString::fromStdString(CResourceHandler::get()->getResourceName(resDir)->string()), QDirIterator::Subdirectories); iter.hasNext(); iter.next()) + total += iter.fileInfo().size(); } + boost::filesystem::path name = *CResourceHandler::get()->getResourceName(resID); + auto mod = JsonUtils::JsonFromFile(pathToQString(name)); + auto json = JsonUtils::toJson(mod); + json["localSizeBytes"].Float() = total; + if(!name.is_absolute()) + json["storedLocaly"].Bool() = true; + + mod = JsonUtils::toVariant(json); localMods.insert(QString::fromUtf8(modname.c_str()).toLower(), mod); } }