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(); } }
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(); }
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()); }
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(); }
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(); }
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; }
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; }
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())); } }
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; } }
bool ValidateTask::processResponse(QJsonObject responseData) { // Assume that if processError wasn't called, then the request was successful. emitSucceeded(); return true; }
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(); } }
void OneSixUpdate::jarlibFinished() { emitSucceeded(); }