Finish assets update for 1.7.3
This commit is contained in:
		| @@ -284,8 +284,8 @@ logic/InstanceLauncher.cpp | ||||
|  | ||||
| # network stuffs | ||||
| logic/net/NetAction.h | ||||
| logic/net/FileDownload.h | ||||
| logic/net/FileDownload.cpp | ||||
| logic/net/MD5EtagDownload.h | ||||
| logic/net/MD5EtagDownload.cpp | ||||
| logic/net/ByteArrayDownload.h | ||||
| logic/net/ByteArrayDownload.cpp | ||||
| logic/net/CacheDownload.h | ||||
| @@ -325,8 +325,6 @@ logic/LegacyForge.h | ||||
| logic/LegacyForge.cpp | ||||
|  | ||||
| # 1.6 instances | ||||
| logic/OneSixAssets.h | ||||
| logic/OneSixAssets.cpp | ||||
| logic/OneSixInstance.h | ||||
| logic/OneSixInstance.cpp | ||||
| logic/OneSixInstance_p.h | ||||
| @@ -385,13 +383,8 @@ logic/SkinUtils.h | ||||
| logic/SkinUtils.cpp | ||||
|  | ||||
| # Assets | ||||
| logic/assets/AssetsIndex.h | ||||
| logic/assets/AssetsIndex.cpp | ||||
| logic/assets/AssetsUtils.h | ||||
| logic/assets/AssetsUtils.cpp | ||||
| logic/assets/Assets.h | ||||
| logic/assets/Assets.cpp | ||||
|  | ||||
| ) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -351,7 +351,8 @@ void MultiMC::initGlobalSettings() | ||||
| void MultiMC::initHttpMetaCache() | ||||
| { | ||||
| 	m_metacache.reset(new HttpMetaCache("metacache")); | ||||
| 	m_metacache->addBase("assets", QDir("assets").absolutePath()); | ||||
| 	m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath()); | ||||
| 	m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath()); | ||||
| 	m_metacache->addBase("versions", QDir("versions").absolutePath()); | ||||
| 	m_metacache->addBase("libraries", QDir("libraries").absolutePath()); | ||||
| 	m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath()); | ||||
|   | ||||
| @@ -72,7 +72,6 @@ | ||||
| #include "logic/BaseInstance.h" | ||||
| #include "logic/InstanceFactory.h" | ||||
| #include "logic/MinecraftProcess.h" | ||||
| #include "logic/OneSixAssets.h" | ||||
| #include "logic/OneSixUpdate.h" | ||||
| #include "logic/JavaUtils.h" | ||||
| #include "logic/NagUtils.h" | ||||
| @@ -233,15 +232,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi | ||||
| 		{ | ||||
| 			MMC->lwjgllist()->loadList(); | ||||
| 		} | ||||
|  | ||||
| 		assets_downloader = new OneSixAssets(); | ||||
| 		connect(assets_downloader, SIGNAL(indexStarted()), SLOT(assetsIndexStarted())); | ||||
| 		connect(assets_downloader, SIGNAL(filesStarted()), SLOT(assetsFilesStarted())); | ||||
| 		connect(assets_downloader, SIGNAL(filesProgress(int, int, int)), | ||||
| 				SLOT(assetsFilesProgress(int, int, int))); | ||||
| 		connect(assets_downloader, SIGNAL(failed()), SLOT(assetsFailed())); | ||||
| 		connect(assets_downloader, SIGNAL(finished()), SLOT(assetsFinished())); | ||||
| 		//assets_downloader->start(); | ||||
| 	} | ||||
|  | ||||
| 	const QString currentInstanceId = MMC->settings()->get("SelectedInstance").toString(); | ||||
| @@ -274,7 +264,6 @@ MainWindow::~MainWindow() | ||||
| 	delete ui; | ||||
| 	delete proxymodel; | ||||
| 	delete drawer; | ||||
| 	delete assets_downloader; | ||||
| } | ||||
|  | ||||
| void MainWindow::repopulateAccountsMenu() | ||||
|   | ||||
| @@ -31,7 +31,6 @@ class KCategorizedView; | ||||
| class KCategoryDrawer; | ||||
| class MinecraftProcess; | ||||
| class ConsoleWindow; | ||||
| class OneSixAssets; | ||||
|  | ||||
| namespace Ui | ||||
| { | ||||
| @@ -173,7 +172,6 @@ private: | ||||
| 	InstanceProxyModel *proxymodel; | ||||
| 	MinecraftProcess *proc; | ||||
| 	ConsoleWindow *console; | ||||
| 	OneSixAssets *assets_downloader; | ||||
| 	LabeledToolButton *renameButton; | ||||
| 	QToolButton *changeIconButton; | ||||
|  | ||||
|   | ||||
| @@ -266,7 +266,7 @@ void LegacyUpdate::jarStart() | ||||
| 	urlstr += intended_version_id + "/" + intended_version_id + ".jar"; | ||||
|  | ||||
| 	auto dljob = new NetJob("Minecraft.jar for version " + intended_version_id); | ||||
| 	dljob->addNetAction(FileDownload::make(QUrl(urlstr), inst->defaultBaseJar())); | ||||
| 	dljob->addNetAction(MD5EtagDownload::make(QUrl(urlstr), inst->defaultBaseJar())); | ||||
| 	legacyDownloadJob.reset(dljob); | ||||
| 	connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); | ||||
| 	connect(dljob, SIGNAL(failed()), SLOT(jarFailed())); | ||||
|   | ||||
| @@ -1,127 +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 <QString> | ||||
| #include "logger/QsLog.h" | ||||
| #include <QtXml/QtXml> | ||||
| #include "OneSixAssets.h" | ||||
| #include "net/NetJob.h" | ||||
| #include "net/HttpMetaCache.h" | ||||
| #include "net/S3ListBucket.h" | ||||
| #include "MultiMC.h" | ||||
|  | ||||
| #define ASSETS_URL "http://resources.download.minecraft.net/" | ||||
|  | ||||
| class ThreadedDeleter : public QThread | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	void run() | ||||
| 	{ | ||||
| 		QLOG_INFO() << "Cleaning up assets folder..."; | ||||
| 		QDirIterator iter(m_base, QDirIterator::Subdirectories); | ||||
| 		int base_length = m_base.length(); | ||||
| 		while (iter.hasNext()) | ||||
| 		{ | ||||
| 			QString filename = iter.next(); | ||||
| 			QFileInfo current(filename); | ||||
| 			// we keep the dirs... whatever | ||||
| 			if (current.isDir()) | ||||
| 				continue; | ||||
| 			QString trimmedf = filename; | ||||
| 			trimmedf.remove(0, base_length + 1); | ||||
| 			if (m_whitelist.contains(trimmedf)) | ||||
| 			{ | ||||
| 				QLOG_TRACE() << trimmedf << " gets to live"; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				// DO NOT TOLERATE JUNK | ||||
| 				QLOG_TRACE() << trimmedf << " dies"; | ||||
| 				QFile f(filename); | ||||
| 				f.remove(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	QString m_base; | ||||
| 	QStringList m_whitelist; | ||||
| }; | ||||
|  | ||||
| void OneSixAssets::downloadFinished() | ||||
| { | ||||
| 	deleter = new ThreadedDeleter(); | ||||
| 	QDir dir("assets"); | ||||
| 	deleter->m_base = dir.absolutePath(); | ||||
| 	deleter->m_whitelist = nuke_whitelist; | ||||
| 	connect(deleter, SIGNAL(finished()), SIGNAL(finished())); | ||||
| 	deleter->start(); | ||||
| } | ||||
|  | ||||
| void OneSixAssets::S3BucketFinished() | ||||
| { | ||||
| 	QString prefix(ASSETS_URL); | ||||
| 	nuke_whitelist.clear(); | ||||
|  | ||||
| 	emit filesStarted(); | ||||
|  | ||||
| 	auto firstJob = index_job->first(); | ||||
| 	auto objectList = std::dynamic_pointer_cast<S3ListBucket>(firstJob)->objects; | ||||
|  | ||||
| 	NetJob *job = new NetJob("Assets"); | ||||
|  | ||||
| 	connect(job, SIGNAL(succeeded()), SLOT(downloadFinished())); | ||||
| 	connect(job, SIGNAL(failed()), SIGNAL(failed())); | ||||
| 	connect(job, SIGNAL(filesProgress(int, int, int)), SIGNAL(filesProgress(int, int, int))); | ||||
|  | ||||
| 	auto metacache = MMC->metacache(); | ||||
|  | ||||
| 	for (auto object : objectList) | ||||
| 	{ | ||||
| 		// Filter folder keys (zero size) | ||||
| 		if (object.size == 0) | ||||
| 			continue; | ||||
|  | ||||
| 		nuke_whitelist.append(object.Key); | ||||
|  | ||||
| 		auto entry = metacache->resolveEntry("assets", object.Key, object.ETag); | ||||
| 		if (entry->stale) | ||||
| 		{ | ||||
| 			job->addNetAction(CacheDownload::make(QUrl(prefix + object.Key), entry)); | ||||
| 		} | ||||
| 	} | ||||
| 	if (job->size()) | ||||
| 	{ | ||||
| 		files_job.reset(job); | ||||
| 		files_job->start(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		delete job; | ||||
| 		emit finished(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void OneSixAssets::start() | ||||
| { | ||||
| 	auto job = new NetJob("Assets index"); | ||||
| 	job->addNetAction(S3ListBucket::make(QUrl(ASSETS_URL))); | ||||
| 	connect(job, SIGNAL(succeeded()), SLOT(S3BucketFinished())); | ||||
| 	connect(job, SIGNAL(failed()), SIGNAL(failed())); | ||||
| 	emit indexStarted(); | ||||
| 	index_job.reset(job); | ||||
| 	job->start(); | ||||
| } | ||||
|  | ||||
| #include "OneSixAssets.moc" | ||||
| @@ -1,45 +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 "net/NetJob.h" | ||||
|  | ||||
| class Private; | ||||
| class ThreadedDeleter; | ||||
|  | ||||
| class OneSixAssets : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
| signals: | ||||
| 	void failed(); | ||||
| 	void finished(); | ||||
| 	void indexStarted(); | ||||
| 	void filesStarted(); | ||||
| 	void filesProgress(int, int, int); | ||||
|  | ||||
| public | ||||
| slots: | ||||
| 	void S3BucketFinished(); | ||||
| 	void downloadFinished(); | ||||
|  | ||||
| public: | ||||
| 	void start(); | ||||
|  | ||||
| private: | ||||
| 	ThreadedDeleter *deleter; | ||||
| 	QStringList nuke_whitelist; | ||||
| 	NetJobPtr index_job; | ||||
| 	NetJobPtr files_job; | ||||
| }; | ||||
| @@ -26,7 +26,6 @@ | ||||
| #include <JlCompress.h> | ||||
| #include "gui/dialogs/OneSixModEditDialog.h" | ||||
| #include "logger/QsLog.h" | ||||
| #include "logic/assets/AssetsIndex.h" | ||||
| #include "logic/assets/AssetsUtils.h" | ||||
|  | ||||
| OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj, | ||||
| @@ -96,9 +95,9 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version) | ||||
| 		{ | ||||
| 			QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path(); | ||||
|  | ||||
| 			for(QString map : index.objects->keys()) | ||||
| 			for(QString map : index.objects.keys()) | ||||
| 			{ | ||||
| 				AssetObject asset_object = index.objects->value(map); | ||||
| 				AssetObject asset_object = index.objects.value(map); | ||||
| 				QString target_path = PathCombine(virtualRoot.path(), map); | ||||
| 				QFile target(target_path); | ||||
|  | ||||
|   | ||||
| @@ -29,6 +29,7 @@ | ||||
| #include "OneSixLibrary.h" | ||||
| #include "OneSixInstance.h" | ||||
| #include "net/ForgeMirrors.h" | ||||
| #include "assets/AssetsUtils.h" | ||||
|  | ||||
| #include "pathutils.h" | ||||
| #include <JlCompress.h> | ||||
| @@ -50,7 +51,7 @@ void OneSixUpdate::executeTask() | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if(m_only_prepare) | ||||
| 	if (m_only_prepare) | ||||
| 	{ | ||||
| 		if (m_inst->shouldUpdate()) | ||||
| 		{ | ||||
| @@ -93,7 +94,7 @@ void OneSixUpdate::checkJavaOnline() | ||||
|  | ||||
| 	checker.reset(new JavaChecker()); | ||||
| 	connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, | ||||
| 		SLOT(checkFinishedOnline(JavaCheckResult))); | ||||
| 			SLOT(checkFinishedOnline(JavaCheckResult))); | ||||
| 	checker->performCheck(java_path); | ||||
| } | ||||
|  | ||||
| @@ -193,6 +194,96 @@ void OneSixUpdate::versionFileFailed() | ||||
| 	emitFailed("Failed to download the version description. Try again."); | ||||
| } | ||||
|  | ||||
| void OneSixUpdate::assetIndexStart() | ||||
| { | ||||
| 	setStatus("Updating asset index."); | ||||
| 	OneSixInstance *inst = (OneSixInstance *)m_inst; | ||||
| 	std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); | ||||
| 	QString assetName = version->assets; | ||||
| 	QUrl indexUrl("https://s3.amazonaws.com/Minecraft.Download/indexes/" + assetName + ".json"); | ||||
| 	QString localPath = assetName + ".json"; | ||||
| 	auto job = new NetJob("Asset index for " + inst->name()); | ||||
|  | ||||
| 	auto metacache = MMC->metacache(); | ||||
| 	auto entry = metacache->resolveEntry("asset_indexes", localPath); | ||||
| 	job->addNetAction(CacheDownload::make(indexUrl, entry)); | ||||
| 	jarlibDownloadJob.reset(job); | ||||
|  | ||||
| 	connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetIndexFinished())); | ||||
| 	connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetIndexFailed())); | ||||
| 	connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), | ||||
| 			SIGNAL(progress(qint64, qint64))); | ||||
|  | ||||
| 	jarlibDownloadJob->start(); | ||||
| } | ||||
|  | ||||
| void OneSixUpdate::assetIndexFinished() | ||||
| { | ||||
| 	AssetsIndex index; | ||||
|  | ||||
| 	OneSixInstance *inst = (OneSixInstance *)m_inst; | ||||
| 	std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); | ||||
| 	QString assetName = version->assets; | ||||
|  | ||||
| 	QString asset_fname = "assets/indexes/" + assetName + ".json"; | ||||
| 	if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index)) | ||||
| 	{ | ||||
| 		emitFailed("Failed to read the assets index!"); | ||||
| 	} | ||||
| 	 | ||||
| 	QList<Md5EtagDownloadPtr> dls; | ||||
| 	for (auto object : index.objects.values()) | ||||
| 	{ | ||||
| 		QString objectName = object.hash.left(2) + "/" + object.hash; | ||||
| 		QFileInfo objectFile("assets/objects/" + objectName); | ||||
| 		if ((!objectFile.isFile()) || (objectFile.size() != object.size)) | ||||
| 		{ | ||||
| 			auto objectDL = MD5EtagDownload::make( | ||||
| 				QUrl("http://resources.download.minecraft.net/" + objectName), | ||||
| 				objectFile.filePath()); | ||||
| 			dls.append(objectDL); | ||||
| 			/* | ||||
| 			Downloadable downloadable = new AssetDownloadable( | ||||
| 				proxy, new URL("http://resources.download.minecraft.net/" + filename), file, | ||||
| 				false, object.getHash(), object.getSize()); | ||||
| 			downloadable.setExpectedSize(object.getSize()); | ||||
| 			result.add(downloadable); | ||||
| 			*/ | ||||
| 		} | ||||
| 	} | ||||
| 	if(dls.size()) | ||||
| 	{ | ||||
| 		auto job = new NetJob("Assets for " + inst->name()); | ||||
| 		for(auto dl: dls) | ||||
| 			job->addNetAction(dl); | ||||
| 		jarlibDownloadJob.reset(job); | ||||
| 		connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished())); | ||||
| 		connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetsFailed())); | ||||
| 		connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), | ||||
| 			SIGNAL(progress(qint64, qint64))); | ||||
| 		jarlibDownloadJob->start(); | ||||
| 		return; | ||||
| 	} | ||||
| 	assetsFinished(); | ||||
| } | ||||
|  | ||||
| void OneSixUpdate::assetIndexFailed() | ||||
| { | ||||
| 	emitFailed("Failed to download the assets index!"); | ||||
| } | ||||
|  | ||||
| void OneSixUpdate::assetsFinished() | ||||
| { | ||||
| 	prepareForLaunch(); | ||||
| } | ||||
|  | ||||
| void OneSixUpdate::assetsFailed() | ||||
| { | ||||
| 	emitFailed("Failed to download assets!"); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void OneSixUpdate::jarlibStart() | ||||
| { | ||||
| 	setStatus("Getting the library files from Mojang."); | ||||
| @@ -215,7 +306,7 @@ void OneSixUpdate::jarlibStart() | ||||
| 	targetstr += version->id + "/" + version->id + ".jar"; | ||||
|  | ||||
| 	auto job = new NetJob("Libraries for instance " + inst->name()); | ||||
| 	job->addNetAction(FileDownload::make(QUrl(urlstr), targetstr)); | ||||
| 	job->addNetAction(MD5EtagDownload::make(QUrl(urlstr), targetstr)); | ||||
| 	jarlibDownloadJob.reset(job); | ||||
|  | ||||
| 	auto libs = version->getActiveNativeLibs(); | ||||
| @@ -265,7 +356,7 @@ void OneSixUpdate::jarlibStart() | ||||
|  | ||||
| void OneSixUpdate::jarlibFinished() | ||||
| { | ||||
| 	prepareForLaunch(); | ||||
| 	assetIndexStart(); | ||||
| } | ||||
|  | ||||
| void OneSixUpdate::jarlibFailed() | ||||
|   | ||||
| @@ -43,6 +43,13 @@ slots: | ||||
| 	void jarlibFinished(); | ||||
| 	void jarlibFailed(); | ||||
|  | ||||
| 	void assetIndexStart(); | ||||
| 	void assetIndexFinished(); | ||||
| 	void assetIndexFailed(); | ||||
|  | ||||
| 	void assetsFinished(); | ||||
| 	void assetsFailed(); | ||||
|  | ||||
| 	void checkJavaOnline(); | ||||
| 	void checkFinishedOnline(JavaCheckResult result); | ||||
| 	void checkFinishedOffline(JavaCheckResult result); | ||||
|   | ||||
| @@ -1,14 +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. | ||||
|  */ | ||||
| @@ -1,16 +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 | ||||
| @@ -1,31 +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 "AssetsIndex.h" | ||||
|  | ||||
| AssetsIndex::AssetsIndex() | ||||
| { | ||||
| 	// TODO: leak? | ||||
| 	this->objects = new QMap<QString, AssetObject>(); | ||||
| 	this->isVirtual = false; | ||||
| } | ||||
|  | ||||
| AssetObject::AssetObject(QString hash, qint64 size) : hash(hash), size(size) | ||||
| { | ||||
| } | ||||
|  | ||||
| AssetObject::AssetObject() | ||||
| { | ||||
| } | ||||
| @@ -1,42 +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 <QString> | ||||
| #include <QMap> | ||||
|  | ||||
| class AssetObject; | ||||
|  | ||||
| class AssetsIndex | ||||
| { | ||||
| public: | ||||
| 	QMap<QString, AssetObject> *objects; | ||||
| 	bool isVirtual; | ||||
|  | ||||
| 	AssetsIndex(); | ||||
| }; | ||||
|  | ||||
| class AssetObject | ||||
| { | ||||
| public: | ||||
| 	AssetObject(QString hash, qint64 size); | ||||
| 	AssetObject(); | ||||
| 	bool equals(AssetObject* other); | ||||
| 	QString getHashCode(); | ||||
|  | ||||
| 	QString hash; | ||||
| 	qint64 size; | ||||
| }; | ||||
| @@ -28,54 +28,64 @@ namespace AssetsUtils | ||||
| void migrateOldAssets() | ||||
| { | ||||
| 	QDir assets_dir("assets"); | ||||
| 	if(!assets_dir.exists()) return; | ||||
| 	if (!assets_dir.exists()) | ||||
| 		return; | ||||
| 	assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); | ||||
| 	int base_length = assets_dir.path().length(); | ||||
|  | ||||
| 	QList<QString> blacklist = {"indexes", "objects", "virtual"}; | ||||
|  | ||||
| 	if(!assets_dir.exists("objects")) assets_dir.mkdir("objects"); | ||||
| 	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()) { | ||||
| 	while (iterator.hasNext()) | ||||
| 	{ | ||||
| 		QString currentDir = iterator.next(); | ||||
| 		currentDir = currentDir.remove(0, base_length+1); | ||||
| 		currentDir = currentDir.remove(0, base_length + 1); | ||||
|  | ||||
| 		bool ignore = false; | ||||
| 		for(QString blacklisted : blacklist) | ||||
| 		for (QString blacklisted : blacklist) | ||||
| 		{ | ||||
| 			if(currentDir.startsWith(blacklisted)) ignore = true; | ||||
| 			if (currentDir.startsWith(blacklisted)) | ||||
| 				ignore = true; | ||||
| 		} | ||||
|  | ||||
| 		if (!iterator.fileInfo().isDir() && !ignore) { | ||||
| 		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 sha1sum = | ||||
| 				QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1) | ||||
| 					.toHex() | ||||
| 					.constData(); | ||||
|  | ||||
| 			QString object_name = filename.remove(0, base_length+1); | ||||
| 			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); | ||||
| 			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()) | ||||
| 			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++; | ||||
| 				QLOG_DEBUG() << " Doesn't exist, copying to" << new_filename << ":" | ||||
| 							 << QString::number(rename_success); | ||||
| 				if (rename_success) | ||||
| 					successes++; | ||||
| 				else | ||||
| 					failures++; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| @@ -85,26 +95,31 @@ void migrateOldAssets() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(successes + failures == 0) { | ||||
| 	if (successes + failures == 0) | ||||
| 	{ | ||||
| 		QLOG_DEBUG() << "No legacy assets needed importing."; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and" << failures << "failures."; | ||||
| 		QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and" | ||||
| 					 << failures << "failures."; | ||||
|  | ||||
| 		QDirIterator cleanup_iterator(assets_dir); | ||||
|  | ||||
| 		while (cleanup_iterator.hasNext()) { | ||||
| 		while (cleanup_iterator.hasNext()) | ||||
| 		{ | ||||
| 			QString currentDir = cleanup_iterator.next(); | ||||
| 			currentDir = currentDir.remove(0, base_length+1); | ||||
| 			currentDir = currentDir.remove(0, base_length + 1); | ||||
|  | ||||
| 			bool ignore = false; | ||||
| 			for(QString blacklisted : blacklist) | ||||
| 			for (QString blacklisted : blacklist) | ||||
| 			{ | ||||
| 				if(currentDir.startsWith(blacklisted)) ignore = true; | ||||
| 				if (currentDir.startsWith(blacklisted)) | ||||
| 					ignore = true; | ||||
| 			} | ||||
|  | ||||
| 			if (cleanup_iterator.fileInfo().isDir() && !ignore) { | ||||
| 			if (cleanup_iterator.fileInfo().isDir() && !ignore) | ||||
| 			{ | ||||
| 				QString path = cleanup_iterator.filePath(); | ||||
| 				QDir folder(path); | ||||
|  | ||||
| @@ -122,18 +137,18 @@ void migrateOldAssets() | ||||
|  */ | ||||
| bool loadAssetsIndexJson(QString path, AssetsIndex *index) | ||||
| { | ||||
| /* | ||||
| { | ||||
|   "objects": { | ||||
| 	"icons/icon_16x16.png": { | ||||
| 	  "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a", | ||||
| 	  "size": 3665 | ||||
| 	}, | ||||
| 	... | ||||
| 	/* | ||||
| 	{ | ||||
| 	  "objects": { | ||||
| 		"icons/icon_16x16.png": { | ||||
| 		  "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a", | ||||
| 		  "size": 3665 | ||||
| 		}, | ||||
| 		... | ||||
| 		} | ||||
| 	  } | ||||
| 	} | ||||
|   } | ||||
| } | ||||
| */ | ||||
| 	*/ | ||||
|  | ||||
| 	QFile file(path); | ||||
|  | ||||
| @@ -155,7 +170,8 @@ bool loadAssetsIndexJson(QString path, AssetsIndex *index) | ||||
| 	// Fail if the JSON is invalid. | ||||
| 	if (parseError.error != QJsonParseError::NoError) | ||||
| 	{ | ||||
| 		QLOG_ERROR() << "Failed to parse assets index file:" << parseError.errorString() << "at offset " << QString::number(parseError.offset); | ||||
| 		QLOG_ERROR() << "Failed to parse assets index file:" << parseError.errorString() | ||||
| 					 << "at offset " << QString::number(parseError.offset); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| @@ -169,7 +185,7 @@ bool loadAssetsIndexJson(QString path, AssetsIndex *index) | ||||
| 	QJsonObject root = jsonDoc.object(); | ||||
|  | ||||
| 	QJsonValue isVirtual = root.value("virtual"); | ||||
| 	if(!isVirtual.isUndefined()) | ||||
| 	if (!isVirtual.isUndefined()) | ||||
| 	{ | ||||
| 		index->isVirtual = isVirtual.toBool(false); | ||||
| 	} | ||||
| @@ -177,48 +193,35 @@ bool loadAssetsIndexJson(QString path, AssetsIndex *index) | ||||
| 	QJsonValue objects = root.value("objects"); | ||||
| 	QVariantMap map = objects.toVariant().toMap(); | ||||
|  | ||||
| 	for(QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { | ||||
| 		//QLOG_DEBUG() << iter.key(); | ||||
| 	for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) | ||||
| 	{ | ||||
| 		// QLOG_DEBUG() << iter.key(); | ||||
|  | ||||
| 		QVariant variant = iter.value(); | ||||
| 		QVariantMap nested_objects = variant.toMap(); | ||||
|  | ||||
| 		AssetObject object; | ||||
|  | ||||
| 		for(QVariantMap::const_iterator nested_iter = nested_objects.begin(); nested_iter != nested_objects.end(); ++nested_iter) { | ||||
| 			//QLOG_DEBUG() << nested_iter.key() << nested_iter.value().toString(); | ||||
| 		for (QVariantMap::const_iterator nested_iter = nested_objects.begin(); | ||||
| 			 nested_iter != nested_objects.end(); ++nested_iter) | ||||
| 		{ | ||||
| 			// QLOG_DEBUG() << nested_iter.key() << nested_iter.value().toString(); | ||||
| 			QString key = nested_iter.key(); | ||||
| 			QVariant value = nested_iter.value(); | ||||
|  | ||||
| 			if(key == "hash") | ||||
| 			if (key == "hash") | ||||
| 			{ | ||||
| 				object.hash = value.toString(); | ||||
| 			} | ||||
| 			else if(key == "size") | ||||
| 			else if (key == "size") | ||||
| 			{ | ||||
| 				object.size = value.toDouble(); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		index->objects->insert(iter.key(), object); | ||||
| 		index->objects.insert(iter.key(), object); | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| 	/*for (QJsonValue accountVal : objects) | ||||
| 	{ | ||||
| 		QJsonObject accountObj = accountVal.toObject(); | ||||
| 		MojangAccountPtr account = MojangAccount::loadFromJson(accountObj); | ||||
| 		if (account.get() != nullptr) | ||||
| 		{ | ||||
| 			connect(account.get(), SIGNAL(changed()), SLOT(accountChanged())); | ||||
| 			m_accounts.append(account); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			QLOG_WARN() << "Failed to load an account."; | ||||
| 		} | ||||
| 	}*/ | ||||
|  | ||||
| 	//return false; | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -15,7 +15,22 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "AssetsIndex.h" | ||||
| #include <QString> | ||||
| #include <QMap> | ||||
|  | ||||
| class AssetObject; | ||||
|  | ||||
| struct AssetObject | ||||
| { | ||||
| 	QString hash; | ||||
| 	qint64 size; | ||||
| }; | ||||
|  | ||||
| struct AssetsIndex | ||||
| { | ||||
| 	QMap<QString, AssetObject> objects; | ||||
| 	bool isVirtual = false; | ||||
| }; | ||||
|  | ||||
| namespace AssetsUtils | ||||
| { | ||||
|   | ||||
| @@ -14,12 +14,12 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include "MultiMC.h" | ||||
| #include "FileDownload.h" | ||||
| #include "MD5EtagDownload.h" | ||||
| #include <pathutils.h> | ||||
| #include <QCryptographicHash> | ||||
| #include "logger/QsLog.h" | ||||
| 
 | ||||
| FileDownload::FileDownload(QUrl url, QString target_path) : NetAction() | ||||
| MD5EtagDownload::MD5EtagDownload(QUrl url, QString target_path) : NetAction() | ||||
| { | ||||
| 	m_url = url; | ||||
| 	m_target_path = target_path; | ||||
| @@ -27,7 +27,7 @@ FileDownload::FileDownload(QUrl url, QString target_path) : NetAction() | ||||
| 	m_status = Job_NotStarted; | ||||
| } | ||||
| 
 | ||||
| void FileDownload::start() | ||||
| void MD5EtagDownload::start() | ||||
| { | ||||
| 	QString filename = m_target_path; | ||||
| 	m_output_file.setFileName(filename); | ||||
| @@ -58,7 +58,7 @@ void FileDownload::start() | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	QLOG_INFO() << "Downloading " << m_url.toString(); | ||||
| 	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()); | ||||
| 	request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); | ||||
| @@ -75,19 +75,19 @@ void FileDownload::start() | ||||
| 	connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); | ||||
| } | ||||
| 
 | ||||
| void FileDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) | ||||
| void MD5EtagDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) | ||||
| { | ||||
| 	emit progress(index_within_job, bytesReceived, bytesTotal); | ||||
| } | ||||
| 
 | ||||
| void FileDownload::downloadError(QNetworkReply::NetworkError error) | ||||
| void MD5EtagDownload::downloadError(QNetworkReply::NetworkError error) | ||||
| { | ||||
| 	// error happened during download.
 | ||||
| 	// TODO: log the reason why
 | ||||
| 	m_status = Job_Failed; | ||||
| } | ||||
| 
 | ||||
| void FileDownload::downloadFinished() | ||||
| void MD5EtagDownload::downloadFinished() | ||||
| { | ||||
| 	// if the download succeeded
 | ||||
| 	if (m_status != Job_Failed) | ||||
| @@ -96,6 +96,7 @@ void FileDownload::downloadFinished() | ||||
| 		m_status = Job_Finished; | ||||
| 		m_output_file.close(); | ||||
| 
 | ||||
| 		QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData(); | ||||
| 		m_reply.reset(); | ||||
| 		emit succeeded(index_within_job); | ||||
| 		return; | ||||
| @@ -110,7 +111,7 @@ void FileDownload::downloadFinished() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void FileDownload::downloadReadyRead() | ||||
| void MD5EtagDownload::downloadReadyRead() | ||||
| { | ||||
| 	if (!m_output_file.isOpen()) | ||||
| 	{ | ||||
| @@ -18,8 +18,8 @@ | ||||
| #include "NetAction.h" | ||||
| #include <QFile> | ||||
| 
 | ||||
| typedef std::shared_ptr<class FileDownload> FileDownloadPtr; | ||||
| class FileDownload : public NetAction | ||||
| typedef std::shared_ptr<class MD5EtagDownload> Md5EtagDownloadPtr; | ||||
| class MD5EtagDownload : public NetAction | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| @@ -35,10 +35,10 @@ public: | ||||
| 	QFile m_output_file; | ||||
| 
 | ||||
| public: | ||||
| 	explicit FileDownload(QUrl url, QString target_path); | ||||
| 	static FileDownloadPtr make(QUrl url, QString target_path) | ||||
| 	explicit MD5EtagDownload(QUrl url, QString target_path); | ||||
| 	static Md5EtagDownloadPtr make(QUrl url, QString target_path) | ||||
| 	{ | ||||
| 		return FileDownloadPtr(new FileDownload(url, target_path)); | ||||
| 		return Md5EtagDownloadPtr(new MD5EtagDownload(url, target_path)); | ||||
| 	} | ||||
| protected | ||||
| slots: | ||||
| @@ -16,7 +16,7 @@ | ||||
| #include "NetJob.h" | ||||
| #include "pathutils.h" | ||||
| #include "MultiMC.h" | ||||
| #include "FileDownload.h" | ||||
| #include "MD5EtagDownload.h" | ||||
| #include "ByteArrayDownload.h" | ||||
| #include "CacheDownload.h" | ||||
|  | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
| #include <QLabel> | ||||
| #include "NetAction.h" | ||||
| #include "ByteArrayDownload.h" | ||||
| #include "FileDownload.h" | ||||
| #include "MD5EtagDownload.h" | ||||
| #include "CacheDownload.h" | ||||
| #include "HttpMetaCache.h" | ||||
| #include "ForgeXzDownload.h" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user