Пример #1
0
void OneSixUpdate::executeTask()
{
	QString intendedVersion = m_inst->intendedVersionId();

	// Make directories
	QDir mcDir(m_inst->minecraftRoot());
	if (!mcDir.exists() && !mcDir.mkpath("."))
	{
		emitFailed("Failed to create bin folder.");
		return;
	}

	// Get a pointer to the version object that corresponds to the instance's version.
	targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
		MMC->minecraftlist()->findVersion(intendedVersion));
	if (targetVersion == nullptr)
	{
		// don't do anything if it was invalid
		emitSucceeded();
		return;
	}

	if (m_inst->shouldUpdate())
	{
		versionFileStart();
	}
	else
	{
		jarlibStart();
	}
}
Пример #2
0
void PasteUpload::downloadFinished()
{
	// if the download succeeded
	if (m_reply->error() == QNetworkReply::NetworkError::NoError)
	{
		QByteArray data = m_reply->readAll();
		m_reply.reset();
		QJsonParseError jsonError;
		QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
		if (jsonError.error != QJsonParseError::NoError)
		{
			emitFailed(jsonError.errorString());
			return;
		}
		if (!parseResult(doc))
		{
			emitFailed(tr("paste.ee returned an error. Please consult the logs for more information"));
			return;
		}
	}
	// else the download failed
	else
	{
		emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
		m_reply.reset();
		return;
	}
	emitSucceeded();
}
Пример #3
0
void SequentialTask::startNext()
{
	if (m_currentIndex != -1)
	{
		std::shared_ptr<ProgressProvider> 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<ProgressProvider> 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());
}
Пример #4
0
void OneSixUpdate::prepareForLaunch()
{
	setStatus(tr("Preparing for launch..."));
	QLOG_INFO() << m_inst->name() << ": preparing for launch";
	auto onesix_inst = (OneSixInstance *)m_inst;

	// delete any leftovers, if they are present.
	onesix_inst->cleanupAfterRun();

	QString natives_dir_raw = PathCombine(onesix_inst->instanceRoot(), "natives/");
	auto version = onesix_inst->getFullVersion();
	if (!version)
	{
		emitFailed("The version information for this instance is not complete. Try re-creating "
				   "it or changing the version.");
		return;
	}
	/*
	for (auto lib : version->getActiveNativeLibs())
	{
		if (!lib->filesExist())
		{
			emitFailed("Native library is missing some files:\n" + lib->storagePath() +
					   "\n\nRun the instance at least once in online mode to get all the "
					   "required files.");
			return;
		}
		if (!lib->extractTo(natives_dir_raw))
		{
			emitFailed("Could not extract the native library:\n" + lib->storagePath() + " to " +
					   natives_dir_raw +
					   "\n\nMake sure MultiMC has appropriate permissions and there is enough "
					   "space on the storage device.");
			return;
		}
	}
*/
	emitSucceeded();
}
Пример #5
0
void JavaListLoadTask::javaCheckerFinished(QList<JavaCheckResult> results)
{
	QList<JavaVersionPtr> candidates;
	m_job.reset();

	QLOG_DEBUG() << "Found the following valid Java installations:";
	for(JavaCheckResult result : results)
	{
		if(result.valid)
		{
			JavaVersionPtr javaVersion(new JavaVersion());

			javaVersion->id = result.javaVersion;
			javaVersion->arch = result.mojangPlatform;
			javaVersion->path = result.path;
			candidates.append(javaVersion);

			QLOG_DEBUG() << " " << javaVersion->id << javaVersion->arch << javaVersion->path;
		}
	}

	QList<BaseVersionPtr> javas_bvp;
	for (auto &java : candidates)
	{
		//QLOG_INFO() << java->id << java->arch << " at " << java->path;
		BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);

		if (bp_java)
		{
			javas_bvp.append(bp_java);
		}
	}

	m_list->updateListData(javas_bvp);
	emitSucceeded();
}
Пример #6
0
void MCVListLoadTask::list_downloaded()
{
	if (vlistReply->error() != QNetworkReply::NoError)
	{
		vlistReply->deleteLater();
		emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString());
		return;
	}

	QJsonParseError jsonError;
	QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
	vlistReply->deleteLater();

	if (jsonError.error != QJsonParseError::NoError)
	{
		emitFailed("Error parsing version list JSON:" + jsonError.errorString());
		return;
	}

	if (!jsonDoc.isObject())
	{
		emitFailed("Error parsing version list JSON: jsonDoc is not an object");
		return;
	}

	QJsonObject root = jsonDoc.object();

	// Get the ID of the latest release and the latest snapshot.
	if (!root.value("latest").isObject())
	{
		emitFailed("Error parsing version list JSON: version list is missing 'latest' object");
		return;
	}

	QJsonObject latest = root.value("latest").toObject();

	QString latestReleaseID = latest.value("release").toString("");
	QString latestSnapshotID = latest.value("snapshot").toString("");
	if (latestReleaseID.isEmpty())
	{
		emitFailed("Error parsing version list JSON: latest release field is missing");
		return;
	}
	if (latestSnapshotID.isEmpty())
	{
		emitFailed("Error parsing version list JSON: latest snapshot field is missing");
		return;
	}

	// Now, get the array of versions.
	if (!root.value("versions").isArray())
	{
		emitFailed(
			"Error parsing version list JSON: version list object is missing 'versions' array");
		return;
	}
	QJsonArray versions = root.value("versions").toArray();

	QList<BaseVersionPtr> tempList;
	for (int i = 0; i < versions.count(); i++)
	{
		bool is_snapshot = false;
		bool is_latest = false;

		// Load the version info.
		if (!versions[i].isObject())
		{
			// FIXME: log this somewhere
			continue;
		}
		QJsonObject version = versions[i].toObject();
		QString versionID = version.value("id").toString("");
		QString versionTimeStr = version.value("releaseTime").toString("");
		QString versionTypeStr = version.value("type").toString("");
		if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
		{
			// FIXME: log this somewhere
			continue;
		}

		// Parse the timestamp.
		QDateTime versionTime = timeFromS3Time(versionTimeStr);
		if (!versionTime.isValid())
		{
			// FIXME: log this somewhere
			continue;
		}
		// Parse the type.
		MinecraftVersion::VersionType versionType;
		// OneSix or Legacy. use filter to determine type
		if (versionTypeStr == "release")
		{
			versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
															  : MinecraftVersion::OneSix;
			is_latest = (versionID == latestReleaseID);
			is_snapshot = false;
		}
		else if (versionTypeStr == "snapshot") // It's a snapshot... yay
		{
			versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
															  : MinecraftVersion::OneSix;
			is_latest = (versionID == latestSnapshotID);
			is_snapshot = true;
		}
		else if (versionTypeStr == "old_alpha")
		{
			versionType = MinecraftVersion::Nostalgia;
			is_latest = false;
			is_snapshot = false;
		}
		else if (versionTypeStr == "old_beta")
		{
			versionType = MinecraftVersion::Legacy;
			is_latest = false;
			is_snapshot = false;
		}
		else
		{
			// FIXME: log this somewhere
			continue;
		}
		// Get the download URL.
		QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";

		// Now, we construct the version object and add it to the list.
		std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
		mcVersion->m_name = mcVersion->m_descriptor = versionID;
		mcVersion->timestamp = versionTime.toMSecsSinceEpoch();
		mcVersion->download_url = dlUrl;
		mcVersion->is_latest = is_latest;
		mcVersion->is_snapshot = is_snapshot;
		mcVersion->type = versionType;
		tempList.append(mcVersion);
	}
	m_list->updateListData(tempList);

	emitSucceeded();
	return;
}
Пример #7
0
void LegacyUpdate::ModTheJar()
{
	LegacyInstance *inst = (LegacyInstance *)m_inst;

	if (!inst->shouldRebuild())
	{
		emitSucceeded();
		return;
	}

	// Get the mod list
	auto modList = inst->jarModList();

	QFileInfo runnableJar(inst->runnableJar());
	QFileInfo baseJar(inst->baseJar());
	bool base_is_custom = inst->shouldUseCustomBaseJar();

	// Nothing to do if there are no jar mods to install, no backup and just the mc jar
	if (base_is_custom)
	{
		// yes, this can happen if the instance only has the runnable jar and not the base jar
		// it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
		// because that's not something mmc4 guarantees
		if (runnableJar.isFile() && !baseJar.exists() && modList->empty())
		{
			inst->setShouldRebuild(false);
			emitSucceeded();
			return;
		}

		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 "
					   "be used on next run.");
			inst->setShouldRebuild(true);
			inst->setShouldUpdate(true);
			inst->setShouldUseCustomBaseJar(false);
			return;
		}
	}

	if (!baseJar.exists())
	{
		emitFailed("The base jar " + baseJar.filePath() + " does not exist");
		return;
	}

	if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
	{
		emitFailed("Failed to delete old minecraft.jar");
		return;
	}

	// TaskStep(); // STEP 1
	setStatus(tr("Installing mods: Opening minecraft.jar ..."));

	QuaZip zipOut(runnableJar.filePath());
	if (!zipOut.open(QuaZip::mdCreate))
	{
		QFile::remove(runnableJar.filePath());
		emitFailed("Failed to open the minecraft.jar for modding");
		return;
	}
	// Files already added to the jar.
	// These files will be skipped.
	QSet<QString> addedFiles;

	// Modify the jar
	setStatus(tr("Installing mods: Adding mod files..."));
	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))
			{
				zipOut.close();
				QFile::remove(runnableJar.filePath());
				emitFailed("Failed to add " + mod.filename().fileName() + " to the jar.");
				return;
			}
		}
		else if (mod.type() == Mod::MOD_SINGLEFILE)
		{
			auto filename = mod.filename();
			if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(),
										  filename.fileName()))
			{
				zipOut.close();
				QFile::remove(runnableJar.filePath());
				emitFailed("Failed to add " + filename.fileName() + " to the jar");
				return;
			}
			addedFiles.insert(filename.fileName());
			QLOG_INFO() << "Adding file " << filename.fileName() << " from "
						<< filename.absoluteFilePath();
		}
		else if (mod.type() == Mod::MOD_FOLDER)
		{
			auto filename = mod.filename();
			QString what_to_zip = filename.absoluteFilePath();
			QDir dir(what_to_zip);
			dir.cdUp();
			QString parent_dir = dir.absolutePath();
			if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, true, addedFiles))
			{
				zipOut.close();
				QFile::remove(runnableJar.filePath());
				emitFailed("Failed to add " + filename.fileName() + " to the jar");
				return;
			}
			QLOG_INFO() << "Adding folder " << filename.fileName() << " from "
						<< filename.absoluteFilePath();
		}
	}

	if (!MergeZipFiles(&zipOut, baseJar, addedFiles, LegacyUpdate::IgnoreMetainf))
	{
		zipOut.close();
		QFile::remove(runnableJar.filePath());
		emitFailed("Failed to insert minecraft.jar contents.");
		return;
	}

	// Recompress the jar
	zipOut.close();
	if (zipOut.getZipError() != 0)
	{
		QFile::remove(runnableJar.filePath());
		emitFailed("Failed to finalize minecraft.jar!");
		return;
	}
	inst->setShouldRebuild(false);
	// inst->UpdateVersion(true);
	emitSucceeded();
	return;
}
Пример #8
0
void YggdrasilTask::processReply()
{
	setStatus(getStateMessage(STATE_PROCESSING_RESPONSE));

	if (m_netReply->error() == QNetworkReply::SslHandshakeFailedError)
	{
		emitFailed(
			tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
			   "<ul>"
			   "<li>You use Windows XP and need to <a "
			   "href=\"http://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
			   "your root certificates</a></li>"
			   "<li>Some device on your network is interfering with SSL traffic. In that case, "
			   "you have bigger worries than Minecraft not starting.</li>"
			   "<li>Possibly something else. Check the MultiMC log file for details</li>"
			   "</ul>"));
		return;
	}

	// any network errors lead to offline mode right now
	if (m_netReply->error() >= QNetworkReply::ConnectionRefusedError &&
		m_netReply->error() <= QNetworkReply::UnknownNetworkError)
	{
		// 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;
	}

	// Try to parse the response regardless of the response code.
	// Sometimes the auth server will give more information and an error code.
	QJsonParseError jsonError;
	QByteArray replyData = m_netReply->readAll();
	QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError);
	// Check the response code.
	int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

	if (responseCode == 200)
	{
		// If the response code was 200, then there shouldn't be an error. Make sure
		// anyways.
		// Also, sometimes an empty reply indicates success. If there was no data received,
		// pass an empty json object to the processResponse function.
		if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
		{
			if (processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()))
			{
				emitSucceeded();
				return;
			}

			// errors happened anyway?
			emitFailed(m_error ? m_error->m_errorMessageVerbose
							   : tr("An unknown error occurred when processing the response "
									"from the authentication server."));
		}
		else
		{
			emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.")
						   .arg(jsonError.errorString())
						   .arg(jsonError.offset));
		}
		return;
	}

	// If the response code was not 200, then Yggdrasil may have given us information
	// about the error.
	// If we can parse the response, then get information from it. Otherwise just say
	// there was an unknown error.
	if (jsonError.error == QJsonParseError::NoError)
	{
		// We were able to parse the server's response. Woo!
		// Call processError. If a subclass has overridden it then they'll handle their
		// stuff there.
		QLOG_DEBUG() << "The request failed, but the server gave us an error message. "
						"Processing error.";
		emitFailed(processError(doc.object()));
	}
	else
	{
		// The server didn't say anything regarding the error. Give the user an unknown
		// error.
		QLOG_DEBUG() << "The request failed and the server gave no error message. "
						"Unknown error.";
		emitFailed(tr("An unknown error occurred when trying to communicate with the "
					  "authentication server: %1").arg(m_netReply->errorString()));
	}
}
Пример #9
0
void LoginTask::processNetReply(QNetworkReply *reply)
{
	if(netReply != reply)
		return;
	// Check for errors.
	switch (reply->error())
	{
	case QNetworkReply::NoError:
	{
		// Check the response code.
		int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
		
		if (responseCode == 200)
		{
			QString responseStr(reply->readAll());
			
			QStringList strings = responseStr.split(":");
			if (strings.count() >= 4)
			{
				bool parseSuccess;
				qint64 latestVersion = strings[0].toLongLong(&parseSuccess);
				if (parseSuccess)
				{
					// strings[1] is the download ticket. It isn't used anymore.
					QString username = strings[2];
					QString sessionID = strings[3];
					
					result = {username, sessionID, latestVersion};
					emitSucceeded();
				}
				else
				{
					emitFailed("Failed to parse Minecraft version string.");
				}
			}
			else
			{
				if (responseStr.toLower() == "bad login")
					emitFailed("Invalid username or password.");
				else if (responseStr.toLower() == "old version")
					emitFailed("Launcher outdated, please update.");
				else
					emitFailed("Login failed: " + responseStr);
			}
		}
		else if (responseCode == 503)
		{
			emitFailed("The login servers are currently unavailable. Check http://help.mojang.com/ for more info.");
		}
		else
		{
			emitFailed(QString("Login failed: Unknown HTTP error %1 occurred.").arg(QString::number(responseCode)));
		}
		break;
	}
		
	case QNetworkReply::OperationCanceledError:
		emitFailed("Login canceled.");
		break;
		
	default:
		emitFailed("Login failed: " + reply->errorString());
		break;
	}
}
Пример #10
0
bool ValidateTask::processResponse(QJsonObject responseData)
{
	// Assume that if processError wasn't called, then the request was successful.
	emitSucceeded();
	return true;
}
Пример #11
0
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<QString> 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();
	}
}
Пример #12
0
void OneSixUpdate::jarlibFinished()
{
	emitSucceeded();
}