From a1a06cc89f7f1d904a1b71d330d6129b866ff29b Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 07:33:32 +0100 Subject: Derpstances. Everything renamed. Launching does not yet work. --- logic/BaseInstance.h | 2 +- logic/DerpFTBInstance.cpp | 123 +++++++++++ logic/DerpFTBInstance.h | 22 ++ logic/DerpInstance.cpp | 373 +++++++++++++++++++++++++++++++ logic/DerpInstance.h | 73 ++++++ logic/DerpInstance_p.h | 27 +++ logic/DerpLibrary.cpp | 268 ++++++++++++++++++++++ logic/DerpLibrary.h | 132 +++++++++++ logic/DerpRule.cpp | 89 ++++++++ logic/DerpRule.h | 98 +++++++++ logic/DerpUpdate.cpp | 377 +++++++++++++++++++++++++++++++ logic/DerpUpdate.h | 63 ++++++ logic/DerpVersion.cpp | 164 ++++++++++++++ logic/DerpVersion.h | 110 +++++++++ logic/DerpVersionBuilder.cpp | 279 +++++++++++++++++++++++ logic/DerpVersionBuilder.h | 43 ++++ logic/ForgeInstaller.cpp | 22 +- logic/ForgeInstaller.h | 6 +- logic/InstanceFactory.cpp | 34 +-- logic/LegacyInstance.h | 2 +- logic/LiteLoaderInstaller.cpp | 25 +-- logic/LiteLoaderInstaller.h | 8 +- logic/MinecraftVersion.h | 6 +- logic/NostalgiaInstance.cpp | 2 +- logic/NostalgiaInstance.h | 4 +- logic/OneSixFTBInstance.cpp | 125 ----------- logic/OneSixFTBInstance.h | 22 -- logic/OneSixInstance.cpp | 416 ----------------------------------- logic/OneSixInstance.h | 78 ------- logic/OneSixInstance_p.h | 30 --- logic/OneSixLibrary.cpp | 268 ---------------------- logic/OneSixLibrary.h | 132 ----------- logic/OneSixRule.cpp | 89 -------- logic/OneSixRule.h | 98 --------- logic/OneSixUpdate.cpp | 377 ------------------------------- logic/OneSixUpdate.h | 63 ------ logic/OneSixVersion.cpp | 50 ++--- logic/OneSixVersion.h | 14 +- logic/lists/MinecraftVersionList.cpp | 6 +- 39 files changed, 2334 insertions(+), 1786 deletions(-) create mode 100644 logic/DerpFTBInstance.cpp create mode 100644 logic/DerpFTBInstance.h create mode 100644 logic/DerpInstance.cpp create mode 100644 logic/DerpInstance.h create mode 100644 logic/DerpInstance_p.h create mode 100644 logic/DerpLibrary.cpp create mode 100644 logic/DerpLibrary.h create mode 100644 logic/DerpRule.cpp create mode 100644 logic/DerpRule.h create mode 100644 logic/DerpUpdate.cpp create mode 100644 logic/DerpUpdate.h create mode 100644 logic/DerpVersion.cpp create mode 100644 logic/DerpVersion.h create mode 100644 logic/DerpVersionBuilder.cpp create mode 100644 logic/DerpVersionBuilder.h delete mode 100644 logic/OneSixFTBInstance.cpp delete mode 100644 logic/OneSixFTBInstance.h delete mode 100644 logic/OneSixInstance.cpp delete mode 100644 logic/OneSixInstance.h delete mode 100644 logic/OneSixInstance_p.h delete mode 100644 logic/OneSixLibrary.cpp delete mode 100644 logic/OneSixLibrary.h delete mode 100644 logic/OneSixRule.cpp delete mode 100644 logic/OneSixRule.h delete mode 100644 logic/OneSixUpdate.cpp delete mode 100644 logic/OneSixUpdate.h (limited to 'logic') diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index a861e9b2..79640c84 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -27,7 +27,7 @@ class QDialog; class Task; class MinecraftProcess; -class OneSixUpdate; +class DerpUpdate; class InstanceList; class BaseInstancePrivate; diff --git a/logic/DerpFTBInstance.cpp b/logic/DerpFTBInstance.cpp new file mode 100644 index 00000000..f9aeeca0 --- /dev/null +++ b/logic/DerpFTBInstance.cpp @@ -0,0 +1,123 @@ +#include "DerpFTBInstance.h" + +#include "DerpVersion.h" +#include "DerpLibrary.h" +#include "tasks/SequentialTask.h" +#include "ForgeInstaller.h" +#include "lists/ForgeVersionList.h" +#include "MultiMC.h" + +class DerpFTBInstanceForge : public Task +{ + Q_OBJECT +public: + explicit DerpFTBInstanceForge(const QString &version, DerpFTBInstance *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) + { + emitFailed(QString("Couldn't find forge version ") + version ); + 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, &DerpFTBInstanceForge::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; + } + auto version = instance->getFullVersion(); + if (!forge.apply(version)) + { + emitFailed(tr("Couldn't install Forge")); + return; + } + emitSucceeded(); + } + +private: + DerpFTBInstance *instance; + QString version; + ForgeVersionPtr forgeVersion; + MetaEntryPtr entry; + NetJob *fjob; +}; + +DerpFTBInstance::DerpFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : + DerpInstance(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 DerpLibrary(match.captured())); + m_forge->finalize(); + } +} + +QString DerpFTBInstance::id() const +{ + return "FTB/" + BaseInstance::id(); +} + +QString DerpFTBInstance::getStatusbarDescription() +{ + return "Derp FTB: " + intendedVersionId(); +} +bool DerpFTBInstance::menuActionEnabled(QString action_name) const +{ + return false; +} + +std::shared_ptr DerpFTBInstance::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(DerpInstance::doUpdate(only_prepare)); + task->addTask(std::shared_ptr(new DerpFTBInstanceForge(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(DerpInstance::doUpdate(only_prepare)); + return task; +} + +#include "DerpFTBInstance.moc" diff --git a/logic/DerpFTBInstance.h b/logic/DerpFTBInstance.h new file mode 100644 index 00000000..c16998bf --- /dev/null +++ b/logic/DerpFTBInstance.h @@ -0,0 +1,22 @@ +#pragma once + +#include "DerpInstance.h" + +class DerpLibrary; + +class DerpFTBInstance : public DerpInstance +{ + Q_OBJECT +public: + explicit DerpFTBInstance(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; + + virtual QString id() const; + +private: + std::shared_ptr m_forge; +}; diff --git a/logic/DerpInstance.cpp b/logic/DerpInstance.cpp new file mode 100644 index 00000000..31ed7c95 --- /dev/null +++ b/logic/DerpInstance.cpp @@ -0,0 +1,373 @@ +/* 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 "DerpInstance.h" + +#include + +#include "DerpInstance_p.h" +#include "DerpUpdate.h" +#include "DerpVersion.h" +#include "pathutils.h" +#include "logger/QsLog.h" +#include "assets/AssetsUtils.h" +#include "MultiMC.h" +#include "icons/IconList.h" +#include "MinecraftProcess.h" +#include "gui/dialogs/DerpModEditDialog.h" + +DerpInstance::DerpInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) + : BaseInstance(new DerpInstancePrivate(), rootDir, settings, parent) +{ + I_D(DerpInstance); + d->m_settings->registerSetting("IntendedVersion", ""); + d->m_settings->registerSetting("ShouldUpdate", false); + d->version.reset(new DerpVersion(this, this)); + reloadFullVersion(); +} + + +std::shared_ptr DerpInstance::doUpdate(bool only_prepare) +{ + return std::shared_ptr(new DerpUpdate(this, only_prepare)); +} + +QString replaceTokensIn(QString text, QMap with) +{ + QString result; + QRegExp token_regexp("\\$\\{(.+)\\}"); + token_regexp.setMinimal(true); + QStringList list; + int tail = 0; + int head = 0; + while ((head = token_regexp.indexIn(text, head)) != -1) + { + result.append(text.mid(tail, head - tail)); + QString key = token_regexp.cap(1); + auto iter = with.find(key); + if (iter != with.end()) + { + result.append(*iter); + } + head += token_regexp.matchedLength(); + tail = head; + } + result.append(text.mid(tail)); + return result; +} + +QDir DerpInstance::reconstructAssets(std::shared_ptr version) +{ + QDir assetsDir = QDir("assets/"); + QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); + QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects")); + QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual")); + + QString indexPath = PathCombine(indexDir.path(), version->assets + ".json"); + QFile indexFile(indexPath); + QDir virtualRoot(PathCombine(virtualDir.path(), version->assets)); + + if (!indexFile.exists()) + { + QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets"; + return virtualRoot; + } + + QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path() + << objectDir.path() << virtualDir.path() << virtualRoot.path(); + + AssetsIndex index; + bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index); + + if (loadAssetsIndex && index.isVirtual) + { + QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path(); + + for (QString map : index.objects.keys()) + { + AssetObject asset_object = index.objects.value(map); + QString target_path = PathCombine(virtualRoot.path(), map); + QFile target(target_path); + + QString tlk = asset_object.hash.left(2); + + QString original_path = + PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash); + QFile original(original_path); + if(!original.exists()) + continue; + if (!target.exists()) + { + QFileInfo info(target_path); + QDir target_dir = info.dir(); + // QLOG_DEBUG() << target_dir; + if (!target_dir.exists()) + QDir("").mkpath(target_dir.path()); + + bool couldCopy = original.copy(target_path); + QLOG_DEBUG() << " Copying" << original_path << "to" << target_path + << QString::number(couldCopy); // << original.errorString(); + } + } + + // TODO: Write last used time to virtualRoot/.lastused + } + + return virtualRoot; +} + +QStringList DerpInstance::processMinecraftArgs(MojangAccountPtr account) +{ + I_D(DerpInstance); + auto version = d->version; + QString args_pattern = version->minecraftArguments; + + QMap token_mapping; + // yggdrasil! + token_mapping["auth_username"] = account->username(); + token_mapping["auth_session"] = account->sessionId(); + token_mapping["auth_access_token"] = account->accessToken(); + token_mapping["auth_player_name"] = account->currentProfile()->name; + token_mapping["auth_uuid"] = account->currentProfile()->id; + + // this is for offline?: + /* + map["auth_player_name"] = "Player"; + map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; + */ + + // these do nothing and are stupid. + token_mapping["profile_name"] = name(); + token_mapping["version_name"] = version->id; + + QString absRootDir = QDir(minecraftRoot()).absolutePath(); + token_mapping["game_directory"] = absRootDir; + QString absAssetsDir = QDir("assets/").absolutePath(); + token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath(); + + auto user = account->user(); + QJsonObject userAttrs; + for (auto key : user.properties.keys()) + { + auto array = QJsonArray::fromStringList(user.properties.values(key)); + userAttrs.insert(key, array); + } + QJsonDocument value(userAttrs); + + token_mapping["user_properties"] = value.toJson(QJsonDocument::Compact); + token_mapping["user_type"] = account->currentProfile()->legacy ? "legacy" : "mojang"; + // 1.7.3+ assets tokens + token_mapping["assets_root"] = absAssetsDir; + token_mapping["assets_index_name"] = version->assets; + + QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); + for (int i = 0; i < parts.length(); i++) + { + parts[i] = replaceTokensIn(parts[i], token_mapping); + } + return parts; +} + +MinecraftProcess *DerpInstance::prepareForLaunch(MojangAccountPtr account) +{ + I_D(DerpInstance); + + QIcon icon = MMC->icons()->getIcon(iconKey()); + auto pixmap = icon.pixmap(128, 128); + pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); + + auto version = d->version; + if (!version) + return nullptr; + QString launchScript; + { + auto libs = version->getActiveNormalLibs(); + for (auto lib : libs) + { + QFileInfo fi(QString("libraries/") + lib->storagePath()); + launchScript += "cp " + fi.absoluteFilePath() + "\n"; + } + QString targetstr = "versions/" + version->id + "/" + version->id + ".jar"; + QFileInfo fi(targetstr); + launchScript += "cp " + fi.absoluteFilePath() + "\n"; + } + launchScript += "mainClass " + version->mainClass + "\n"; + + for (auto param : processMinecraftArgs(account)) + { + launchScript += "param " + param + "\n"; + } + + // Set the width and height for 1.6 instances + bool maximize = settings().get("LaunchMaximized").toBool(); + if (maximize) + { + // this is probably a BAD idea + // launchScript += "param --fullscreen\n"; + } + else + { + launchScript += + "param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n"; + launchScript += + "param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n"; + } + QDir natives_dir(PathCombine(instanceRoot(), "natives/")); + launchScript += "windowTitle " + windowTitle() + "\n"; + launchScript += "natives " + natives_dir.absolutePath() + "\n"; + launchScript += "launch onesix"; + + qDebug() << launchScript; + + // create the process and set its parameters + MinecraftProcess *proc = new MinecraftProcess(this); + proc->setWorkdir(minecraftRoot()); + proc->setLaunchScript(launchScript); + // proc->setNativeFolder(natives_dir.absolutePath()); + return proc; +} + +void DerpInstance::cleanupAfterRun() +{ + QString target_dir = PathCombine(instanceRoot(), "natives/"); + QDir dir(target_dir); + dir.removeRecursively(); +} + +std::shared_ptr DerpInstance::loaderModList() +{ + I_D(DerpInstance); + if (!d->loader_mod_list) + { + d->loader_mod_list.reset(new ModList(loaderModsDir())); + } + d->loader_mod_list->update(); + return d->loader_mod_list; +} + +std::shared_ptr DerpInstance::resourcePackList() +{ + I_D(DerpInstance); + if (!d->resource_pack_list) + { + d->resource_pack_list.reset(new ModList(resourcePacksDir())); + } + d->resource_pack_list->update(); + return d->resource_pack_list; +} + +QDialog *DerpInstance::createModEditDialog(QWidget *parent) +{ + return new DerpModEditDialog(this, parent); +} + +bool DerpInstance::setIntendedVersionId(QString version) +{ + settings().set("IntendedVersion", version); + setShouldUpdate(true); + auto pathOrig = PathCombine(instanceRoot(), "version.json"); + QFile::remove(pathOrig); + reloadFullVersion(); + return true; +} + +QString DerpInstance::intendedVersionId() const +{ + return settings().get("IntendedVersion").toString(); +} + +void DerpInstance::setShouldUpdate(bool val) +{ + settings().set("ShouldUpdate", val); +} + +bool DerpInstance::shouldUpdate() const +{ + QVariant var = settings().get("ShouldUpdate"); + if (!var.isValid() || var.toBool() == false) + { + return intendedVersionId() != currentVersionId(); + } + return true; +} + +bool DerpInstance::versionIsCustom() +{ + QDir patches(PathCombine(instanceRoot(), "patches/")); + return QFile::exists(PathCombine(instanceRoot(), "custom.json")) + || (patches.exists() && patches.count() >= 0); +} + +QString DerpInstance::currentVersionId() const +{ + return intendedVersionId(); +} + +bool DerpInstance::reloadFullVersion(QWidget *widgetParent) +{ + I_D(DerpInstance); + + bool ret = d->version->reload(widgetParent); + emit versionReloaded(); + return ret; +} + +std::shared_ptr DerpInstance::getFullVersion() +{ + I_D(DerpInstance); + return d->version; +} + +QString DerpInstance::defaultBaseJar() const +{ + return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; +} + +QString DerpInstance::defaultCustomBaseJar() const +{ + return PathCombine(instanceRoot(), "custom.jar"); +} + +bool DerpInstance::menuActionEnabled(QString action_name) const +{ + if (action_name == "actionChangeInstLWJGLVersion") + return false; + return true; +} + +QString DerpInstance::getStatusbarDescription() +{ + QString descr = "Derp : " + intendedVersionId(); + if (versionIsCustom()) + { + descr + " (custom)"; + } + return descr; +} + +QString DerpInstance::loaderModsDir() const +{ + return PathCombine(minecraftRoot(), "mods"); +} + +QString DerpInstance::resourcePacksDir() const +{ + return PathCombine(minecraftRoot(), "resourcepacks"); +} + +QString DerpInstance::instanceConfigFolder() const +{ + return PathCombine(minecraftRoot(), "config"); +} diff --git a/logic/DerpInstance.h b/logic/DerpInstance.h new file mode 100644 index 00000000..37d3df52 --- /dev/null +++ b/logic/DerpInstance.h @@ -0,0 +1,73 @@ +/* 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 "BaseInstance.h" + +#include "DerpVersion.h" +#include "ModList.h" + +class DerpInstance : public BaseInstance +{ + Q_OBJECT +public: + explicit DerpInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + + ////// Mod Lists ////// + std::shared_ptr loaderModList(); + std::shared_ptr resourcePackList(); + + ////// Directories ////// + QString resourcePacksDir() const; + QString loaderModsDir() const; + virtual QString instanceConfigFolder() const override; + + virtual std::shared_ptr doUpdate(bool only_prepare) override; + virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; + + virtual void cleanupAfterRun() override; + + virtual QString intendedVersionId() const override; + virtual bool setIntendedVersionId(QString version) override; + + virtual QString currentVersionId() const override; + + virtual bool shouldUpdate() const override; + virtual void setShouldUpdate(bool val) override; + + virtual QDialog *createModEditDialog(QWidget *parent) override; + + /// reload the full version json files. return true on success! + bool reloadFullVersion(QWidget *widgetParent = 0); + /// get the current full version info + std::shared_ptr getFullVersion(); + /// is the current version original, or custom? + virtual bool versionIsCustom() override; + + virtual QString defaultBaseJar() const override; + virtual QString defaultCustomBaseJar() const override; + + virtual bool menuActionEnabled(QString action_name) const override; + virtual QString getStatusbarDescription() override; + +signals: + void versionReloaded(); + +private: + QStringList processMinecraftArgs(MojangAccountPtr account); + QDir reconstructAssets(std::shared_ptr version); +}; diff --git a/logic/DerpInstance_p.h b/logic/DerpInstance_p.h new file mode 100644 index 00000000..41f7b62d --- /dev/null +++ b/logic/DerpInstance_p.h @@ -0,0 +1,27 @@ +/* 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 "BaseInstance_p.h" +#include "DerpVersion.h" +#include "ModList.h" + +struct DerpInstancePrivate : public BaseInstancePrivate +{ + std::shared_ptr version; + std::shared_ptr loader_mod_list; + std::shared_ptr resource_pack_list; +}; diff --git a/logic/DerpLibrary.cpp b/logic/DerpLibrary.cpp new file mode 100644 index 00000000..ba4d516b --- /dev/null +++ b/logic/DerpLibrary.cpp @@ -0,0 +1,268 @@ +/* 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 + +#include "DerpLibrary.h" +#include "DerpRule.h" +#include "OpSys.h" +#include "logic/net/URLConstants.h" +#include +#include +#include "logger/QsLog.h" + +void DerpLibrary::finalize() +{ + QStringList parts = m_name.split(':'); + QString relative = parts[0]; + relative.replace('.', '/'); + relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; + + if (!m_is_native) + relative += ".jar"; + else + { + if (m_native_suffixes.contains(currentSystem)) + { + relative += "-" + m_native_suffixes[currentSystem] + ".jar"; + } + else + { + // really, bad. + relative += ".jar"; + } + } + + m_decentname = parts[1]; + m_decentversion = parts[2]; + m_storage_path = relative; + m_download_url = m_base_url + relative; + + if (m_rules.empty()) + { + m_is_active = true; + } + else + { + RuleAction result = Disallow; + for (auto rule : m_rules) + { + RuleAction temp = rule->apply(this); + if (temp != Defer) + result = temp; + } + m_is_active = (result == Allow); + } + if (m_is_native) + { + m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); + m_decenttype = "Native"; + } + else + { + m_decenttype = "Java"; + } +} + +void DerpLibrary::setName(const QString &name) +{ + m_name = name; +} +void DerpLibrary::setBaseUrl(const QString &base_url) +{ + m_base_url = base_url; +} +void DerpLibrary::setIsNative() +{ + m_is_native = true; +} +void DerpLibrary::addNative(OpSys os, const QString &suffix) +{ + m_is_native = true; + m_native_suffixes[os] = suffix; +} +void DerpLibrary::setRules(QList> rules) +{ + m_rules = rules; +} +bool DerpLibrary::isActive() const +{ + return m_is_active; +} +bool DerpLibrary::isNative() const +{ + return m_is_native; +} +QString DerpLibrary::downloadUrl() const +{ + if (m_absolute_url.size()) + return m_absolute_url; + return m_download_url; +} +QString DerpLibrary::storagePath() const +{ + return m_storage_path; +} + +void DerpLibrary::setAbsoluteUrl(const QString &absolute_url) +{ + m_absolute_url = absolute_url; +} + +QString DerpLibrary::absoluteUrl() const +{ + return m_absolute_url; +} + +void DerpLibrary::setHint(const QString &hint) +{ + m_hint = hint; +} + +QString DerpLibrary::hint() const +{ + return m_hint; +} + +bool DerpLibrary::filesExist() +{ + QString storage = storagePath(); + if (storage.contains("${arch}")) + { + QString cooked_storage = storage; + cooked_storage.replace("${arch}", "32"); + QFileInfo info32(PathCombine("libraries", cooked_storage)); + if (!info32.exists()) + { + return false; + } + cooked_storage = storage; + cooked_storage.replace("${arch}", "64"); + QFileInfo info64(PathCombine("libraries", cooked_storage)); + if (!info64.exists()) + { + return false; + } + } + else + { + QFileInfo info(PathCombine("libraries", storage)); + if (!info.exists()) + { + return false; + } + } + return true; +} + +bool DerpLibrary::extractTo(QString target_dir) +{ + QString storage = storagePath(); + if (storage.contains("${arch}")) + { + QString cooked_storage = storage; + cooked_storage.replace("${arch}", "32"); + QString origin = PathCombine("libraries", cooked_storage); + QString target_dir_cooked = PathCombine(target_dir, "32"); + if(!ensureFolderPathExists(target_dir_cooked)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; + return false; + } + if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) + .isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + origin; + return false; + } + cooked_storage = storage; + cooked_storage.replace("${arch}", "64"); + origin = PathCombine("libraries", cooked_storage); + target_dir_cooked = PathCombine(target_dir, "64"); + if(!ensureFolderPathExists(target_dir_cooked)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; + return false; + } + if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) + .isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + origin; + return false; + } + } + else + { + if(!ensureFolderPathExists(target_dir)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir; + return false; + } + QString path = PathCombine("libraries", storage); + if (JlCompress::extractWithExceptions(path, target_dir, extract_excludes).isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + path; + return false; + } + } + return true; +} + +QJsonObject DerpLibrary::toJson() +{ + QJsonObject libRoot; + libRoot.insert("name", m_name); + if (m_absolute_url.size()) + libRoot.insert("MMC-absoluteUrl", m_absolute_url); + if (m_hint.size()) + libRoot.insert("MMC-hint", m_hint); + if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && + m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && + m_base_url != "https://" + URLConstants::LIBRARY_BASE) + libRoot.insert("url", m_base_url); + if (isNative() && m_native_suffixes.size()) + { + QJsonObject nativeList; + auto iter = m_native_suffixes.begin(); + while (iter != m_native_suffixes.end()) + { + nativeList.insert(OpSys_toString(iter.key()), iter.value()); + iter++; + } + libRoot.insert("natives", nativeList); + } + if (isNative() && extract_excludes.size()) + { + QJsonArray excludes; + QJsonObject extract; + for (auto exclude : extract_excludes) + { + excludes.append(exclude); + } + extract.insert("exclude", excludes); + libRoot.insert("extract", extract); + } + if (m_rules.size()) + { + QJsonArray allRules; + for (auto &rule : m_rules) + { + QJsonObject ruleObj = rule->toJson(); + allRules.append(ruleObj); + } + libRoot.insert("rules", allRules); + } + return libRoot; +} diff --git a/logic/DerpLibrary.h b/logic/DerpLibrary.h new file mode 100644 index 00000000..d1cee843 --- /dev/null +++ b/logic/DerpLibrary.h @@ -0,0 +1,132 @@ +/* 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 "logic/net/URLConstants.h" +#include "OpSys.h" + +class Rule; + +class DerpLibrary +{ +private: + // basic values used internally (so far) + QString m_name; + QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; + QList> m_rules; + + // custom values + /// absolute URL. takes precedence over m_download_path, if defined + QString m_absolute_url; + /// download hint - how to actually get the library + QString m_hint; + + // derived values used for real things + /// a decent name fit for display + QString m_decentname; + /// a decent version fit for display + QString m_decentversion; + /// a decent type fit for display + QString m_decenttype; + /// where to store the lib locally + QString m_storage_path; + /// where to download the lib from + QString m_download_url; + /// is this lib actually active on the current OS? + bool m_is_active = false; + /// is the library a native? + bool m_is_native = false; + /// native suffixes per OS + QMap m_native_suffixes; + +public: + QStringList extract_excludes; + +public: + /// Constructor + DerpLibrary(const QString &name) + { + m_name = name; + } + + /// Returns the raw name field + QString rawName() const + { + return m_name; + } + + QJsonObject toJson(); + + /** + * finalize the library, processing the input values into derived values and state + * + * This SHALL be called after all the values are parsed or after any further change. + */ + void finalize(); + + /// Set the library composite name + void setName(const QString &name); + /// get a decent-looking name + QString name() const + { + return m_decentname; + } + /// get a decent-looking version + QString version() const + { + return m_decentversion; + } + /// what kind of library is it? (for display) + QString type() const + { + return m_decenttype; + } + /// Set the url base for downloads + void setBaseUrl(const QString &base_url); + + /// Call this to mark the library as 'native' (it's a zip archive with DLLs) + void setIsNative(); + /// Attach a name suffix to the specified OS native + void addNative(OpSys os, const QString &suffix); + /// Set the load rules + void setRules(QList> rules); + + /// Returns true if the library should be loaded (or extracted, in case of natives) + bool isActive() const; + /// Returns true if the library is native + bool isNative() const; + /// Get the URL to download the library from + QString downloadUrl() const; + /// Get the relative path where the library should be saved + QString storagePath() const; + + /// set an absolute URL for the library. This is an MMC extension. + void setAbsoluteUrl(const QString &absolute_url); + QString absoluteUrl() const; + + /// set a hint about how to treat the library. This is an MMC extension. + void setHint(const QString &hint); + QString hint() const; + + bool extractTo(QString target_dir); + bool filesExist(); +}; diff --git a/logic/DerpRule.cpp b/logic/DerpRule.cpp new file mode 100644 index 00000000..d4cf1ba3 --- /dev/null +++ b/logic/DerpRule.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 +#include + +#include "DerpRule.h" + +QList> rulesFromJsonV4(QJsonObject &objectWithRules) +{ + QList> rules; + auto rulesVal = objectWithRules.value("rules"); + if (!rulesVal.isArray()) + return rules; + + QJsonArray ruleList = rulesVal.toArray(); + for (auto ruleVal : ruleList) + { + std::shared_ptr rule; + if (!ruleVal.isObject()) + continue; + auto ruleObj = ruleVal.toObject(); + auto actionVal = ruleObj.value("action"); + if (!actionVal.isString()) + continue; + auto action = RuleAction_fromString(actionVal.toString()); + if (action == Defer) + continue; + + auto osVal = ruleObj.value("os"); + if (!osVal.isObject()) + { + // add a new implicit action rule + rules.append(ImplicitRule::create(action)); + continue; + } + + auto osObj = osVal.toObject(); + auto osNameVal = osObj.value("name"); + if (!osNameVal.isString()) + continue; + OpSys requiredOs = OpSys_fromString(osNameVal.toString()); + QString versionRegex = osObj.value("version").toString(); + // add a new OS rule + rules.append(OsRule::create(action, requiredOs, versionRegex)); + } + return rules; +} + +QJsonObject ImplicitRule::toJson() +{ + QJsonObject ruleObj; + ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); + return ruleObj; +} + +QJsonObject OsRule::toJson() +{ + QJsonObject ruleObj; + ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); + QJsonObject osObj; + { + osObj.insert("name", OpSys_toString(m_system)); + osObj.insert("version", m_version_regexp); + } + ruleObj.insert("os", osObj); + return ruleObj; +} + +RuleAction RuleAction_fromString(QString name) +{ + if (name == "allow") + return Allow; + if (name == "disallow") + return Disallow; + return Defer; +} diff --git a/logic/DerpRule.h b/logic/DerpRule.h new file mode 100644 index 00000000..7895ea98 --- /dev/null +++ b/logic/DerpRule.h @@ -0,0 +1,98 @@ +/* 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 "logic/DerpLibrary.h" + +enum RuleAction +{ + Allow, + Disallow, + Defer +}; + +RuleAction RuleAction_fromString(QString); +QList> rulesFromJsonV4(QJsonObject &objectWithRules); + +class Rule +{ +protected: + RuleAction m_result; + virtual bool applies(DerpLibrary *parent) = 0; + +public: + Rule(RuleAction result) : m_result(result) + { + } + virtual ~Rule() {}; + virtual QJsonObject toJson() = 0; + RuleAction apply(DerpLibrary *parent) + { + if (applies(parent)) + return m_result; + else + return Defer; + } + ; +}; + +class OsRule : public Rule +{ +private: + // the OS + OpSys m_system; + // the OS version regexp + QString m_version_regexp; + +protected: + virtual bool applies(DerpLibrary *) + { + return (m_system == currentSystem); + } + OsRule(RuleAction result, OpSys system, QString version_regexp) + : Rule(result), m_system(system), m_version_regexp(version_regexp) + { + } + +public: + virtual QJsonObject toJson(); + static std::shared_ptr create(RuleAction result, OpSys system, + QString version_regexp) + { + return std::shared_ptr(new OsRule(result, system, version_regexp)); + } +}; + +class ImplicitRule : public Rule +{ +protected: + virtual bool applies(DerpLibrary *) + { + return true; + } + ImplicitRule(RuleAction result) : Rule(result) + { + } + +public: + virtual QJsonObject toJson(); + static std::shared_ptr create(RuleAction result) + { + return std::shared_ptr(new ImplicitRule(result)); + } +}; diff --git a/logic/DerpUpdate.cpp b/logic/DerpUpdate.cpp new file mode 100644 index 00000000..e1600d28 --- /dev/null +++ b/logic/DerpUpdate.cpp @@ -0,0 +1,377 @@ +/* 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 "MultiMC.h" +#include "DerpUpdate.h" + +#include + +#include +#include +#include +#include + +#include "BaseInstance.h" +#include "lists/MinecraftVersionList.h" +#include "DerpVersion.h" +#include "DerpLibrary.h" +#include "DerpInstance.h" +#include "net/ForgeMirrors.h" +#include "net/URLConstants.h" +#include "assets/AssetsUtils.h" + +#include "pathutils.h" +#include + +DerpUpdate::DerpUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) + : Task(parent), m_inst(inst), m_only_prepare(only_prepare) +{ +} + +void DerpUpdate::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; + } + + if (m_only_prepare) + { + prepareForLaunch(); + return; + } + + if (m_inst->shouldUpdate()) + { + // Get a pointer to the version object that corresponds to the instance's version. + targetVersion = std::dynamic_pointer_cast( + MMC->minecraftlist()->findVersion(intendedVersion)); + if (targetVersion == nullptr) + { + // don't do anything if it was invalid + emitFailed("The specified Minecraft version is invalid. Choose a different one."); + return; + } + versionFileStart(); + } + else + { + jarlibStart(); + } +} + +void DerpUpdate::versionFileStart() +{ + QLOG_INFO() << m_inst->name() << ": getting version file."; + 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"); + job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); + specificVersionDownloadJob.reset(job); + connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished())); + connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed())); + connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + specificVersionDownloadJob->start(); +} + +void DerpUpdate::versionFileFinished() +{ + NetActionPtr DlJob = specificVersionDownloadJob->first(); + DerpInstance *inst = (DerpInstance *)m_inst; + + QString version_id = targetVersion->descriptor(); + QString inst_dir = m_inst->instanceRoot(); + // save the version file in $instanceId/version.json + { + QString version1 = PathCombine(inst_dir, "/version.json"); + ensureFilePathExists(version1); + // FIXME: detect errors here, download to a temp file, swap + QSaveFile vfile1(version1); + if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) + { + emitFailed("Can't open " + version1 + " for writing."); + return; + } + auto data = std::dynamic_pointer_cast(DlJob)->m_data; + qint64 actual = 0; + if ((actual = vfile1.write(data)) != data.size()) + { + emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " + + data.size() + '.'); + return; + } + if (!vfile1.commit()) + { + emitFailed("Can't commit changes to " + version1); + return; + } + } + + // the version is downloaded safely. update is 'done' at this point + m_inst->setShouldUpdate(false); + + // delete any custom version inside the instance (it's no longer relevant, we did an update) + QString custom = PathCombine(inst_dir, "/custom.json"); + QFile finfo(custom); + if (finfo.exists()) + { + finfo.remove(); + } + inst->reloadFullVersion(); + + jarlibStart(); +} + +void DerpUpdate::versionFileFailed() +{ + emitFailed("Failed to download the version description. Try again."); +} + +void DerpUpdate::assetIndexStart() +{ + setStatus(tr("Updating assets index...")); + DerpInstance *inst = (DerpInstance *)m_inst; + std::shared_ptr version = inst->getFullVersion(); + QString assetName = version->assets; + QUrl indexUrl = "http://" + URLConstants::AWS_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 DerpUpdate::assetIndexFinished() +{ + AssetsIndex index; + + DerpInstance *inst = (DerpInstance *)m_inst; + std::shared_ptr 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 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://" + URLConstants::RESOURCE_BASE + objectName), + objectFile.filePath()); + objectDL->m_total_progress = object.size; + dls.append(objectDL); + } + } + if (dls.size()) + { + setStatus(tr("Getting the assets files from Mojang...")); + 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 DerpUpdate::assetIndexFailed() +{ + emitFailed("Failed to download the assets index!"); +} + +void DerpUpdate::assetsFinished() +{ + prepareForLaunch(); +} + +void DerpUpdate::assetsFailed() +{ + emitFailed("Failed to download assets!"); +} + +void DerpUpdate::jarlibStart() +{ + setStatus(tr("Getting the library files from Mojang...")); + QLOG_INFO() << m_inst->name() << ": downloading libraries"; + DerpInstance *inst = (DerpInstance *)m_inst; + bool successful = inst->reloadFullVersion(); + if (!successful) + { + emitFailed("Failed to load the version description file. It might be " + "corrupted, missing or simply too new."); + return; + } + + // Build a list of URLs that will need to be downloaded. + std::shared_ptr version = inst->getFullVersion(); + // minecraft.jar for this version + { + QString version_id = version->id; + QString localPath = version_id + "/" + version_id + ".jar"; + QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; + + auto job = new NetJob("Libraries for instance " + inst->name()); + + auto metacache = MMC->metacache(); + auto entry = metacache->resolveEntry("versions", localPath); + job->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); + + jarlibDownloadJob.reset(job); + } + + auto libs = version->getActiveNativeLibs(); + libs.append(version->getActiveNormalLibs()); + + auto metacache = MMC->metacache(); + QList ForgeLibs; + for (auto lib : libs) + { + if (lib->hint() == "local") + continue; + + QString raw_storage = lib->storagePath(); + QString raw_dl = lib->downloadUrl(); + + auto f = [&](QString storage, QString dl) + { + auto entry = metacache->resolveEntry("libraries", storage); + if (entry->stale) + { + if (lib->hint() == "forge-pack-xz") + { + ForgeLibs.append(ForgeXzDownload::make(storage, entry)); + } + else + { + jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); + } + } + }; + if (raw_storage.contains("${arch}")) + { + QString cooked_storage = raw_storage; + QString cooked_dl = raw_dl; + f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); + cooked_storage = raw_storage; + cooked_dl = raw_dl; + f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); + } + else + { + f(raw_storage, raw_dl); + } + } + // TODO: think about how to propagate this from the original json file... or IF AT ALL + QString forgeMirrorList = "http://files.minecraftforge.net/mirror-brand.list"; + if (!ForgeLibs.empty()) + { + jarlibDownloadJob->addNetAction( + ForgeMirrors::make(ForgeLibs, jarlibDownloadJob, forgeMirrorList)); + } + + connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); + connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed())); + connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + + jarlibDownloadJob->start(); +} + +void DerpUpdate::jarlibFinished() +{ + assetIndexStart(); +} + +void DerpUpdate::jarlibFailed() +{ + QStringList failed = jarlibDownloadJob->getFailedFiles(); + QString failed_all = failed.join("\n"); + emitFailed("Failed to download the following files:\n" + failed_all + + "\n\nPlease try again."); +} + +void DerpUpdate::prepareForLaunch() +{ + setStatus(tr("Preparing for launch...")); + QLOG_INFO() << m_inst->name() << ": preparing for launch"; + auto derp_inst = (DerpInstance *)m_inst; + + // delete any leftovers, if they are present. + derp_inst->cleanupAfterRun(); + + QString natives_dir_raw = PathCombine(derp_inst->instanceRoot(), "natives/"); + auto version = derp_inst->getFullVersion(); + if (!version) + { + emitFailed("The version information for this instance is not complete. Try re-creating " + "it or changing the version."); + return; + } + /* + * emitFailed("Could not create the native library folder:\n" + natives_dir_raw + + "\nMake sure MultiMC has appropriate permissions and there is enough + space " + "on the storage device."); + */ + 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(); +} diff --git a/logic/DerpUpdate.h b/logic/DerpUpdate.h new file mode 100644 index 00000000..475f6c35 --- /dev/null +++ b/logic/DerpUpdate.h @@ -0,0 +1,63 @@ +/* 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 "logic/net/NetJob.h" +#include "logic/tasks/Task.h" + +class MinecraftVersion; +class BaseInstance; + +class DerpUpdate : public Task +{ + Q_OBJECT +public: + explicit DerpUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); + virtual void executeTask(); + +private +slots: + void versionFileStart(); + void versionFileFinished(); + void versionFileFailed(); + + void jarlibStart(); + void jarlibFinished(); + void jarlibFailed(); + + void assetIndexStart(); + void assetIndexFinished(); + void assetIndexFailed(); + + void assetsFinished(); + void assetsFailed(); + + // extract the appropriate libraries + void prepareForLaunch(); + +private: + NetJobPtr specificVersionDownloadJob; + NetJobPtr jarlibDownloadJob; + + // target version, determined during this task + std::shared_ptr targetVersion; + BaseInstance *m_inst = nullptr; + bool m_only_prepare = false; +}; diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp new file mode 100644 index 00000000..f13ac620 --- /dev/null +++ b/logic/DerpVersion.cpp @@ -0,0 +1,164 @@ +/* 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 "DerpVersion.h" + +#include + +#include "DerpVersionBuilder.h" + +DerpVersion::DerpVersion(DerpInstance *instance, QObject *parent) + : QAbstractListModel(parent), m_instance(instance) +{ +} + +bool DerpVersion::reload(QWidget *widgetParent) +{ + return DerpVersionBuilder::build(this, m_instance, widgetParent); +} + +QList > DerpVersion::getActiveNormalLibs() +{ + QList > output; + for (auto lib : libraries) + { + if (lib->isActive() && !lib->isNative()) + { + output.append(lib); + } + } + return output; +} + +QList > DerpVersion::getActiveNativeLibs() +{ + QList > output; + for (auto lib : libraries) + { + if (lib->isActive() && lib->isNative()) + { + output.append(lib); + } + } + return output; +} + +QVariant DerpVersion::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + int column = index.column(); + + if (row < 0 || row >= libraries.size()) + return QVariant(); + + if (role == Qt::DisplayRole) + { + switch (column) + { + case 0: + return libraries[row]->name(); + case 1: + return libraries[row]->type(); + case 2: + return libraries[row]->version(); + default: + return QVariant(); + } + } + return QVariant(); +} + +Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + int row = index.row(); + if (libraries[row]->isActive()) + { + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; + } + else + { + return Qt::ItemNeverHasChildren; + } + // return QAbstractListModel::flags(index); +} + +QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + switch (section) + { + case 0: + return QString("Name"); + case 1: + return QString("Type"); + case 2: + return QString("Version"); + default: + return QString(); + } +} + +int DerpVersion::rowCount(const QModelIndex &parent) const +{ + return libraries.size(); +} + +int DerpVersion::columnCount(const QModelIndex &parent) const +{ + return 3; +} + +QDebug operator<<(QDebug &dbg, const DerpVersion *version) +{ + dbg.nospace() << "DerpVersion(" + << "\n\tid=" << version->id + << "\n\ttime=" << version->time + << "\n\treleaseTime=" << version->releaseTime + << "\n\ttype=" << version->type + << "\n\tassets=" << version->assets + << "\n\tprocessArguments=" << version->processArguments + << "\n\tminecraftArguments=" << version->minecraftArguments + << "\n\tminimumLauncherVersion=" << version->minimumLauncherVersion + << "\n\tmainClass=" << version->mainClass + << "\n\tlibraries="; + for (auto lib : version->libraries) + { + dbg.nospace() << "\n\t\t" << lib.get(); + } + dbg.nospace() << "\n)"; + return dbg.maybeSpace(); +} +QDebug operator<<(QDebug &dbg, const DerpLibrary *library) +{ + dbg.nospace() << "DerpLibrary(" + << "\n\t\t\trawName=" << library->rawName() + << "\n\t\t\tname=" << library->name() + << "\n\t\t\tversion=" << library->version() + << "\n\t\t\ttype=" << library->type() + << "\n\t\t\tisActive=" << library->isActive() + << "\n\t\t\tisNative=" << library->isNative() + << "\n\t\t\tdownloadUrl=" << library->downloadUrl() + << "\n\t\t\tstoragePath=" << library->storagePath() + << "\n\t\t\tabsolutePath=" << library->absoluteUrl() + << "\n\t\t\thint=" << library->hint(); + dbg.nospace() << "\n\t\t)"; + return dbg.maybeSpace(); +} diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h new file mode 100644 index 00000000..cadfa850 --- /dev/null +++ b/logic/DerpVersion.h @@ -0,0 +1,110 @@ +/* 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 "DerpLibrary.h" + +class DerpInstance; + +class DerpVersion : public QAbstractListModel +{ + Q_OBJECT +public: + explicit DerpVersion(DerpInstance *instance, QObject *parent = 0); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex &parent) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + bool reload(QWidget *widgetParent); + +public: + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); + + // data members +public: + /// the ID - determines which jar to use! ACTUALLY IMPORTANT! + QString id; + /// Last updated time - as a string + QString time; + /// Release time - as a string + QString releaseTime; + /// Release type - "release" or "snapshot" + QString type; + /// Assets type - "legacy" or a version ID + QString assets; + /** + * DEPRECATED: Old versions of the new vanilla launcher used this + * ex: "username_session_version" + */ + QString processArguments; + /** + * arguments that should be used for launching minecraft + * + * ex: "--username ${auth_player_name} --session ${auth_session} + * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" + */ + QString minecraftArguments; + /** + * the minimum launcher version required by this version ... current is 4 (at point of + * writing) + */ + int minimumLauncherVersion = 0xDEADBEEF; + /** + * The main class to load first + */ + QString mainClass; + + /// the list of libs - both active and inactive, native and java + QList> libraries; + + /* + FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. + + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "osx", + "version": "^10\\.5\\.\\d$" + } + } + ], + "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX + 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" + } + */ + // QList rules; + +private: + DerpInstance *m_instance; +}; + +QDebug operator<<(QDebug &dbg, const DerpVersion *version); +QDebug operator<<(QDebug &dbg, const DerpLibrary *library); diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp new file mode 100644 index 00000000..d8091f32 --- /dev/null +++ b/logic/DerpVersionBuilder.cpp @@ -0,0 +1,279 @@ +/* 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 "DerpVersionBuilder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DerpVersion.h" +#include "DerpInstance.h" +#include "DerpRule.h" + +DerpVersionBuilder::DerpVersionBuilder() +{ + +} + +bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent) +{ + DerpVersionBuilder builder; + builder.m_version = version; + builder.m_instance = instance; + builder.m_widgetParent = widgetParent; + return builder.build(); +} + +bool DerpVersionBuilder::build() +{ + clear(); + + QDir root(m_instance->instanceRoot()); + QDir patches(root.absoluteFilePath("patches/")); + + // version.json -> patches/*.json -> custom.json + + // version.json + { + QJsonObject obj; + if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) + { + return false; + } + if (!apply(obj)) + { + return false; + } + } + + // patches/ + { + // load all, put into map for ordering, apply in the right order + + QMap objects; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + { + QJsonObject obj; + if (!read(info, &obj)) + { + return false; + } + if (!obj.contains("order") || !obj.value("order").isDouble()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath())); + return false; + } + objects.insert(obj.value("order").toDouble(), obj); + } + for (auto object : objects.values()) + { + if (!apply(object)) + { + return false; + } + } + } + + // custom.json + { + if (QFile::exists(root.absoluteFilePath("custom.json"))) + { + QJsonObject obj; + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) + { + return false; + } + if (!apply(obj)) + { + return false; + } + } + } + + return true; +} + +void DerpVersionBuilder::clear() +{ + m_version->id.clear(); + m_version->time.clear(); + m_version->releaseTime.clear(); + m_version->type.clear(); + m_version->assets.clear(); + m_version->processArguments.clear(); + m_version->minecraftArguments.clear(); + m_version->minimumLauncherVersion = 0xDEADBEAF; + m_version->mainClass.clear(); + m_version->libraries.clear(); +} + +void applyString(const QJsonObject &obj, const QString &key, QString &out) +{ + if (obj.contains(key) && obj.value(key).isString()) + { + out = obj.value(key).toString(); + } +} +void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(DerpLibrary::*func)(const QString &val)) +{ + if (obj.contains(key) && obj.value(key).isString()) + { + (lib.get()->*func)(obj.value(key).toString()); + } +} +bool DerpVersionBuilder::apply(const QJsonObject &object) +{ + applyString(object, "id", m_version->id); + applyString(object, "mainClass", m_version->mainClass); + applyString(object, "processArguments", m_version->processArguments); + { + const QString toCompare = m_version->processArguments.toLower(); + if (toCompare == "legacy") + { + m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; + } + else if (toCompare == "username_session") + { + m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; + } + else if (toCompare == "username_session_version") + { + m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; + } + } + applyString(object, "minecraftArguments", m_version->minecraftArguments); + applyString(object, "type", m_version->type); + applyString(object, "releaseTime", m_version->releaseTime); + applyString(object, "time", m_version->time); + applyString(object, "assets", m_version->assets); + { + if (m_version->assets.isEmpty()) + { + m_version->assets = "legacy"; + } + } + if (object.contains("minimumLauncherVersion")) + { + auto minLauncherVersionVal = object.value("minimumLauncherVersion"); + if (minLauncherVersionVal.isDouble()) + { + m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble(); + } + } + + // libraries + { + auto librariesValue = object.value("libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } + for (auto libVal : librariesValue.toArray()) + { + if (!libVal.isObject()) + { + continue; + } + + QJsonObject libObj = libVal.toObject(); + + // Library name + auto nameVal = libObj.value("name"); + if (!nameVal.isString()) + { + continue; + } + std::shared_ptr library(new DerpLibrary(nameVal.toString())); + + applyString(libObj, "url", library, &DerpLibrary::setBaseUrl); + applyString(libObj, "MMC-hint", library, &DerpLibrary::setHint); + applyString(libObj, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); + applyString(libObj, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); + + auto extractVal = libObj.value("extract"); + if (extractVal.isObject()) + { + QStringList excludes; + auto extractObj = extractVal.toObject(); + auto excludesVal = extractObj.value("exclude"); + if (excludesVal.isArray()) + { + auto excludesList = excludesVal.toArray(); + for (auto excludeVal : excludesList) + { + if (excludeVal.isString()) + { + excludes.append(excludeVal.toString()); + } + } + library->extract_excludes = excludes; + } + } + + auto nativesVal = libObj.value("natives"); + if (nativesVal.isObject()) + { + library->setIsNative(); + auto nativesObj = nativesVal.toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + auto osType = OpSys_fromString(it.key()); + if (osType == Os_Other) + { + continue; + } + if (!it.value().isString()) + { + continue; + } + library->addNative(osType, it.value().toString()); + } + } + + library->setRules(rulesFromJsonV4(libObj)); + library->finalize(); + m_version->libraries.append(library); + } + } + + return true; +} + +bool DerpVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) +{ + QFile file(fileInfo.absoluteFilePath()); + if (!file.open(QFile::ReadOnly)) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); + return false; + } + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); + if (error.error != QJsonParseError::NoError) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to parse %1: %2 at %3").arg(file.fileName(), error.errorString()).arg(error.offset)); + return false; + } + *out = doc.object(); + return true; +} diff --git a/logic/DerpVersionBuilder.h b/logic/DerpVersionBuilder.h new file mode 100644 index 00000000..739065db --- /dev/null +++ b/logic/DerpVersionBuilder.h @@ -0,0 +1,43 @@ +/* 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 + +class DerpVersion; +class DerpInstance; +class QWidget; +class QJsonObject; +class QFileInfo; + +class DerpVersionBuilder +{ + DerpVersionBuilder(); +public: + static bool build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent); + +private: + DerpVersion *m_version; + DerpInstance *m_instance; + QWidget *m_widgetParent; + + bool build(); + + void clear(); + bool apply(const QJsonObject &object); + + bool read(const QFileInfo &fileInfo, QJsonObject *out); +}; diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 8d4c5b41..29735a7d 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -14,8 +14,8 @@ */ #include "ForgeInstaller.h" -#include "OneSixVersion.h" -#include "OneSixLibrary.h" +#include "DerpVersion.h" +#include "DerpLibrary.h" #include "net/HttpMetaCache.h" #include #include @@ -23,9 +23,15 @@ #include #include "MultiMC.h" +#include +#include +#include + +// DERPFIX + ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { - std::shared_ptr newVersion; + std::shared_ptr newVersion; m_universal_url = universal_url; QuaZip zip(filename); @@ -58,7 +64,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - newVersion = OneSixVersion::fromJson(versionInfoVal.toObject()); + // DERPFIX newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } @@ -68,7 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) internalPath = installObj.value("filePath").toString(); // where do we put the library? decode the mojang path - OneSixLibrary lib(libraryName); + DerpLibrary lib(libraryName); lib.finalize(); auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath()); @@ -103,11 +109,10 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); } -bool ForgeInstaller::apply(std::shared_ptr to) +bool ForgeInstaller::apply(std::shared_ptr to) { if (!m_forge_version) return false; - to->externalUpdateStart(); int sliding_insert_window = 0; { // for each library in the version we are adding (except for the blacklisted) @@ -150,6 +155,5 @@ bool ForgeInstaller::apply(std::shared_ptr to) to->minecraftArguments = m_forge_version->minecraftArguments; to->processArguments = m_forge_version->processArguments; } - to->externalUpdateFinish(); - return to->toOriginalFile(); + return true; } diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index 0b9f9c77..65cad87c 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -17,18 +17,18 @@ #include #include -class OneSixVersion; +class DerpVersion; class ForgeInstaller { public: ForgeInstaller(QString filename, QString universal_url); - bool apply(std::shared_ptr to); + bool apply(std::shared_ptr to); private: // the version, read from the installer - std::shared_ptr m_forge_version; + std::shared_ptr m_forge_version; QString internalPath; QString finalPath; QString realVersionId; diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 1f1a5879..c50ec4e4 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -21,9 +21,10 @@ #include "BaseInstance.h" #include "LegacyInstance.h" #include "LegacyFTBInstance.h" -#include "OneSixInstance.h" -#include "OneSixFTBInstance.h" +#include "DerpInstance.h" +#include "DerpFTBInstance.h" #include "NostalgiaInstance.h" +#include "DerpInstance.h" #include "BaseVersion.h" #include "MinecraftVersion.h" @@ -50,13 +51,13 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst QString inst_type = m_settings->get("InstanceType").toString(); // FIXME: replace with a map lookup, where instance classes register their types - if (inst_type == "Legacy") + if (inst_type == "Derp" || inst_type == "OneSix") { - inst = new LegacyInstance(instDir, m_settings, this); + inst = new DerpInstance(instDir, m_settings, this); } - else if (inst_type == "OneSix") + else if (inst_type == "Legacy") { - inst = new OneSixInstance(instDir, m_settings, this); + inst = new LegacyInstance(instDir, m_settings, this); } else if (inst_type == "Nostalgia") { @@ -66,9 +67,9 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new LegacyFTBInstance(instDir, m_settings, this); } - else if (inst_type == "OneSixFTB") + else if (inst_type == "OneSixFTB" || inst_type == "DerpFTB") { - inst = new OneSixFTBInstance(instDir, m_settings, this); + inst = new DerpFTBInstance(instDir, m_settings, this); } else { @@ -101,14 +102,15 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& switch (mcVer->type) { case MinecraftVersion::Legacy: + // TODO derp 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); + case MinecraftVersion::Derp: + m_settings->set("InstanceType", "Derp"); + inst = new DerpInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -135,9 +137,9 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; - case MinecraftVersion::OneSix: - m_settings->set("InstanceType", "OneSixFTB"); - inst = new OneSixFTBInstance(instDir, m_settings, this); + case MinecraftVersion::Derp: + m_settings->set("InstanceType", "DerpFTB"); + inst = new DerpFTBInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -174,8 +176,8 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); - if(inst_type == "OneSixFTB") - m_settings->set("InstanceType", "OneSix"); + if(inst_type == "OneSixFTB" || inst_type == "DerpFTB") + m_settings->set("InstanceType", "Derp"); if(inst_type == "LegacyFTB") m_settings->set("InstanceType", "Legacy"); diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 1e7d9eb6..1ff17cbd 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -68,7 +68,7 @@ public: virtual QString intendedVersionId() const override; virtual bool setIntendedVersionId(QString version) override; // the `version' of Legacy instances is defined by the launcher code. - // in contrast with OneSix, where `version' is described in a json file + // in contrast with Derp, where `version' is described in a json file virtual bool versionIsCustom() override { return false; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 07fffff3..8a120eab 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -15,8 +15,8 @@ #include "LiteLoaderInstaller.h" -#include "OneSixVersion.h" -#include "OneSixLibrary.h" +#include "DerpVersion.h" +#include "DerpLibrary.h" QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; @@ -36,9 +36,9 @@ bool LiteLoaderInstaller::canApply() const return m_launcherWrapperVersionMapping.contains(m_mcVersion); } -bool LiteLoaderInstaller::apply(std::shared_ptr to) +bool LiteLoaderInstaller::apply(std::shared_ptr to) { - to->externalUpdateStart(); + // DERPFIX applyLaunchwrapper(to); applyLiteLoader(to); @@ -51,15 +51,14 @@ bool LiteLoaderInstaller::apply(std::shared_ptr to) " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"); } - to->externalUpdateFinish(); - return to->toOriginalFile(); + return true; } -void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) +void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) { const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion]; - QMutableListIterator> it(to->libraries); + QMutableListIterator> it(to->libraries); while (it.hasNext()) { it.next(); @@ -76,15 +75,15 @@ void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) } } - std::shared_ptr lib(new OneSixLibrary( + std::shared_ptr lib(new DerpLibrary( "net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion])); lib->finalize(); to->libraries.prepend(lib); } -void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) +void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) { - QMutableListIterator> it(to->libraries); + QMutableListIterator> it(to->libraries); while (it.hasNext()) { it.next(); @@ -94,8 +93,8 @@ void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) } } - std::shared_ptr lib( - new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion)); + std::shared_ptr lib( + new DerpLibrary("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 44b306d6..d7b8ce77 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -18,7 +18,7 @@ #include #include -class OneSixVersion; +class DerpVersion; class LiteLoaderInstaller { @@ -27,13 +27,13 @@ public: bool canApply() const; - bool apply(std::shared_ptr to); + bool apply(std::shared_ptr to); private: QString m_mcVersion; - void applyLaunchwrapper(std::shared_ptr to); - void applyLiteLoader(std::shared_ptr to); + void applyLaunchwrapper(std::shared_ptr to); + void applyLiteLoader(std::shared_ptr to); static QMap m_launcherWrapperVersionMapping; }; diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h index 504381a8..dd849ce9 100644 --- a/logic/MinecraftVersion.h +++ b/logic/MinecraftVersion.h @@ -32,7 +32,7 @@ struct MinecraftVersion : public BaseVersion /// This version's type. Used internally to identify what kind of version this is. enum VersionType { - OneSix, + Derp, Legacy, Nostalgia } type; @@ -66,8 +66,8 @@ struct MinecraftVersion : public BaseVersion } switch (type) { - case OneSix: - pre_final.append("OneSix"); + case Derp: + pre_final.append("Derp"); break; case Legacy: pre_final.append("Legacy"); diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp index 2e23ee71..f27954a7 100644 --- a/logic/NostalgiaInstance.cpp +++ b/logic/NostalgiaInstance.cpp @@ -17,7 +17,7 @@ NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) - : OneSixInstance(rootDir, settings, parent) + : DerpInstance(rootDir, settings, parent) { } diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h index a26f7f0a..6a6b91a1 100644 --- a/logic/NostalgiaInstance.h +++ b/logic/NostalgiaInstance.h @@ -15,9 +15,9 @@ #pragma once -#include "OneSixInstance.h" +#include "DerpInstance.h" -class NostalgiaInstance : public OneSixInstance +class NostalgiaInstance : public DerpInstance { Q_OBJECT public: diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp deleted file mode 100644 index e50a5b53..00000000 --- a/logic/OneSixFTBInstance.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#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) - { - emitFailed(QString("Couldn't find forge version ") + version ); - 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; - } - instance->revertCustomVersion(); - instance->customizeVersion(); - auto version = instance->getFullVersion(); - if (!forge.apply(version)) - { - 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::id() const -{ - return "FTB/" + BaseInstance::id(); -} - -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))); - //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; -} - -#include "OneSixFTBInstance.moc" diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h deleted file mode 100644 index dc028819..00000000 --- a/logic/OneSixFTBInstance.h +++ /dev/null @@ -1,22 +0,0 @@ -#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; - - virtual QString id() const; - -private: - std::shared_ptr m_forge; -}; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp deleted file mode 100644 index 3cfc1c76..00000000 --- a/logic/OneSixInstance.cpp +++ /dev/null @@ -1,416 +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 "MultiMC.h" -#include "OneSixInstance.h" -#include "OneSixInstance_p.h" -#include "OneSixUpdate.h" -#include "MinecraftProcess.h" -#include "OneSixVersion.h" -#include "JavaChecker.h" -#include "logic/icons/IconList.h" - -#include -#include -#include -#include -#include "gui/dialogs/OneSixModEditDialog.h" -#include "logger/QsLog.h" -#include "logic/assets/AssetsUtils.h" -#include - -OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj, - QObject *parent) - : BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent) -{ - I_D(OneSixInstance); - d->m_settings->registerSetting("IntendedVersion", ""); - d->m_settings->registerSetting("ShouldUpdate", false); - reloadFullVersion(); -} - -std::shared_ptr OneSixInstance::doUpdate(bool only_prepare) -{ - return std::shared_ptr(new OneSixUpdate(this, only_prepare)); -} - -QString replaceTokensIn(QString text, QMap with) -{ - QString result; - QRegExp token_regexp("\\$\\{(.+)\\}"); - token_regexp.setMinimal(true); - QStringList list; - int tail = 0; - int head = 0; - while ((head = token_regexp.indexIn(text, head)) != -1) - { - result.append(text.mid(tail, head - tail)); - QString key = token_regexp.cap(1); - auto iter = with.find(key); - if (iter != with.end()) - { - result.append(*iter); - } - head += token_regexp.matchedLength(); - tail = head; - } - result.append(text.mid(tail)); - return result; -} - -QDir OneSixInstance::reconstructAssets(std::shared_ptr version) -{ - QDir assetsDir = QDir("assets/"); - QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); - QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects")); - QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual")); - - QString indexPath = PathCombine(indexDir.path(), version->assets + ".json"); - QFile indexFile(indexPath); - QDir virtualRoot(PathCombine(virtualDir.path(), version->assets)); - - if (!indexFile.exists()) - { - QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets"; - return virtualRoot; - } - - QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path() - << objectDir.path() << virtualDir.path() << virtualRoot.path(); - - AssetsIndex index; - bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index); - - if (loadAssetsIndex && index.isVirtual) - { - QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path(); - - for (QString map : index.objects.keys()) - { - AssetObject asset_object = index.objects.value(map); - QString target_path = PathCombine(virtualRoot.path(), map); - QFile target(target_path); - - QString tlk = asset_object.hash.left(2); - - QString original_path = - PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash); - QFile original(original_path); - if(!original.exists()) - continue; - if (!target.exists()) - { - QFileInfo info(target_path); - QDir target_dir = info.dir(); - // QLOG_DEBUG() << target_dir; - if (!target_dir.exists()) - QDir("").mkpath(target_dir.path()); - - bool couldCopy = original.copy(target_path); - QLOG_DEBUG() << " Copying" << original_path << "to" << target_path - << QString::number(couldCopy); // << original.errorString(); - } - } - - // TODO: Write last used time to virtualRoot/.lastused - } - - return virtualRoot; -} - -QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) -{ - I_D(OneSixInstance); - auto version = d->version; - QString args_pattern = version->minecraftArguments; - - QMap token_mapping; - // yggdrasil! - token_mapping["auth_username"] = account->username(); - token_mapping["auth_session"] = account->sessionId(); - token_mapping["auth_access_token"] = account->accessToken(); - token_mapping["auth_player_name"] = account->currentProfile()->name; - token_mapping["auth_uuid"] = account->currentProfile()->id; - - // this is for offline?: - /* - map["auth_player_name"] = "Player"; - map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; - */ - - // these do nothing and are stupid. - token_mapping["profile_name"] = name(); - token_mapping["version_name"] = version->id; - - QString absRootDir = QDir(minecraftRoot()).absolutePath(); - token_mapping["game_directory"] = absRootDir; - QString absAssetsDir = QDir("assets/").absolutePath(); - token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath(); - - auto user = account->user(); - QJsonObject userAttrs; - for (auto key : user.properties.keys()) - { - auto array = QJsonArray::fromStringList(user.properties.values(key)); - userAttrs.insert(key, array); - } - QJsonDocument value(userAttrs); - - token_mapping["user_properties"] = value.toJson(QJsonDocument::Compact); - token_mapping["user_type"] = account->currentProfile()->legacy ? "legacy" : "mojang"; - // 1.7.3+ assets tokens - token_mapping["assets_root"] = absAssetsDir; - token_mapping["assets_index_name"] = version->assets; - - QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); - for (int i = 0; i < parts.length(); i++) - { - parts[i] = replaceTokensIn(parts[i], token_mapping); - } - return parts; -} - -MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) -{ - I_D(OneSixInstance); - - QIcon icon = MMC->icons()->getIcon(iconKey()); - auto pixmap = icon.pixmap(128, 128); - pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); - - auto version = d->version; - if (!version) - return nullptr; - QString launchScript; - { - auto libs = version->getActiveNormalLibs(); - for (auto lib : libs) - { - QFileInfo fi(QString("libraries/") + lib->storagePath()); - launchScript += "cp " + fi.absoluteFilePath() + "\n"; - } - QString targetstr = "versions/" + version->id + "/" + version->id + ".jar"; - QFileInfo fi(targetstr); - launchScript += "cp " + fi.absoluteFilePath() + "\n"; - } - launchScript += "mainClass " + version->mainClass + "\n"; - - for (auto param : processMinecraftArgs(account)) - { - launchScript += "param " + param + "\n"; - } - - // Set the width and height for 1.6 instances - bool maximize = settings().get("LaunchMaximized").toBool(); - if (maximize) - { - // this is probably a BAD idea - // launchScript += "param --fullscreen\n"; - } - else - { - launchScript += - "param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n"; - launchScript += - "param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n"; - } - QDir natives_dir(PathCombine(instanceRoot(), "natives/")); - launchScript += "windowTitle " + windowTitle() + "\n"; - launchScript += "natives " + natives_dir.absolutePath() + "\n"; - launchScript += "launch onesix\n"; - - // create the process and set its parameters - MinecraftProcess *proc = new MinecraftProcess(this); - proc->setWorkdir(minecraftRoot()); - proc->setLaunchScript(launchScript); - // proc->setNativeFolder(natives_dir.absolutePath()); - return proc; -} - -void OneSixInstance::cleanupAfterRun() -{ - QString target_dir = PathCombine(instanceRoot(), "natives/"); - QDir dir(target_dir); - dir.removeRecursively(); -} - -std::shared_ptr OneSixInstance::loaderModList() -{ - I_D(OneSixInstance); - if (!d->loader_mod_list) - { - d->loader_mod_list.reset(new ModList(loaderModsDir())); - } - d->loader_mod_list->update(); - return d->loader_mod_list; -} - -std::shared_ptr OneSixInstance::resourcePackList() -{ - I_D(OneSixInstance); - if (!d->resource_pack_list) - { - d->resource_pack_list.reset(new ModList(resourcePacksDir())); - } - d->resource_pack_list->update(); - return d->resource_pack_list; -} - -QDialog *OneSixInstance::createModEditDialog(QWidget *parent) -{ - return new OneSixModEditDialog(this, parent); -} - -bool OneSixInstance::setIntendedVersionId(QString version) -{ - settings().set("IntendedVersion", version); - setShouldUpdate(true); - auto pathCustom = PathCombine(instanceRoot(), "custom.json"); - auto pathOrig = PathCombine(instanceRoot(), "version.json"); - QFile::remove(pathCustom); - QFile::remove(pathOrig); - reloadFullVersion(); - return true; -} - -QString OneSixInstance::intendedVersionId() const -{ - return settings().get("IntendedVersion").toString(); -} - -void OneSixInstance::setShouldUpdate(bool val) -{ - settings().set("ShouldUpdate", val); -} - -bool OneSixInstance::shouldUpdate() const -{ - QVariant var = settings().get("ShouldUpdate"); - if (!var.isValid() || var.toBool() == false) - { - return intendedVersionId() != currentVersionId(); - } - return true; -} - -bool OneSixInstance::versionIsCustom() -{ - QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); - QFile versionfile(verpath_custom); - return versionfile.exists(); -} - -QString OneSixInstance::currentVersionId() const -{ - return intendedVersionId(); -} - -bool OneSixInstance::customizeVersion() -{ - if (!versionIsCustom()) - { - auto pathCustom = PathCombine(instanceRoot(), "custom.json"); - auto pathOrig = PathCombine(instanceRoot(), "version.json"); - QFile::copy(pathOrig, pathCustom); - return reloadFullVersion(); - } - else - return true; -} - -bool OneSixInstance::revertCustomVersion() -{ - if (versionIsCustom()) - { - auto path = PathCombine(instanceRoot(), "custom.json"); - QFile::remove(path); - return reloadFullVersion(); - } - else - return true; -} - -bool OneSixInstance::reloadFullVersion() -{ - I_D(OneSixInstance); - - QString verpath = PathCombine(instanceRoot(), "version.json"); - { - QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); - QFile versionfile(verpath_custom); - if (versionfile.exists()) - verpath = verpath_custom; - } - - auto version = OneSixVersion::fromFile(verpath); - if (version) - { - d->version = version; - return true; - } - else - { - d->version.reset(); - return false; - } -} - -std::shared_ptr OneSixInstance::getFullVersion() -{ - I_D(OneSixInstance); - return d->version; -} - -QString OneSixInstance::defaultBaseJar() const -{ - return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; -} - -QString OneSixInstance::defaultCustomBaseJar() const -{ - return PathCombine(instanceRoot(), "custom.jar"); -} - -bool OneSixInstance::menuActionEnabled(QString action_name) const -{ - if (action_name == "actionChangeInstLWJGLVersion") - return false; - return true; -} - -QString OneSixInstance::getStatusbarDescription() -{ - QString descr = "One Six : " + intendedVersionId(); - if (versionIsCustom()) - { - descr + " (custom)"; - } - return descr; -} - -QString OneSixInstance::loaderModsDir() const -{ - return PathCombine(minecraftRoot(), "mods"); -} - -QString OneSixInstance::resourcePacksDir() const -{ - return PathCombine(minecraftRoot(), "resourcepacks"); -} - -QString OneSixInstance::instanceConfigFolder() const -{ - return PathCombine(minecraftRoot(), "config"); -} diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h deleted file mode 100644 index f869e345..00000000 --- a/logic/OneSixInstance.h +++ /dev/null @@ -1,78 +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 "BaseInstance.h" - -class OneSixVersion; -class Task; -class ModList; - -class OneSixInstance : public BaseInstance -{ - Q_OBJECT -public: - explicit OneSixInstance(const QString &rootDir, SettingsObject *settings, - QObject *parent = 0); - - ////// Mod Lists ////// - std::shared_ptr loaderModList(); - std::shared_ptr resourcePackList(); - - ////// Directories ////// - QString resourcePacksDir() const; - QString loaderModsDir() const; - virtual QString instanceConfigFolder() const override; - - virtual std::shared_ptr doUpdate(bool only_prepare) override; - virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; - - virtual void cleanupAfterRun() override; - - virtual QString intendedVersionId() const override; - virtual bool setIntendedVersionId(QString version) override; - - virtual QString currentVersionId() const override; - - virtual bool shouldUpdate() const override; - virtual void setShouldUpdate(bool val) override; - - virtual QDialog *createModEditDialog(QWidget *parent) override; - - /// reload the full version json file. return true on success! - bool reloadFullVersion(); - /// get the current full version info - std::shared_ptr getFullVersion(); - /// revert the current custom version back to base - bool revertCustomVersion(); - /// customize the current base version - bool customizeVersion(); - /// is the current version original, or custom? - virtual bool versionIsCustom() override; - - virtual QString defaultBaseJar() const override; - virtual QString defaultCustomBaseJar() const override; - - virtual bool menuActionEnabled(QString action_name) const override; - virtual QString getStatusbarDescription() override; - -private: - QStringList processMinecraftArgs(MojangAccountPtr account); - QDir reconstructAssets(std::shared_ptr version); -}; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h deleted file mode 100644 index 6b7ea431..00000000 --- a/logic/OneSixInstance_p.h +++ /dev/null @@ -1,30 +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 "logic/BaseInstance_p.h" -#include "logic/OneSixVersion.h" -#include "logic/OneSixLibrary.h" -#include "logic/ModList.h" - -struct OneSixInstancePrivate : public BaseInstancePrivate -{ - std::shared_ptr version; - std::shared_ptr loader_mod_list; - std::shared_ptr resource_pack_list; -}; \ No newline at end of file diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp deleted file mode 100644 index 7b80d5e7..00000000 --- a/logic/OneSixLibrary.cpp +++ /dev/null @@ -1,268 +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 - -#include "OneSixLibrary.h" -#include "OneSixRule.h" -#include "OpSys.h" -#include "logic/net/URLConstants.h" -#include -#include -#include "logger/QsLog.h" - -void OneSixLibrary::finalize() -{ - QStringList parts = m_name.split(':'); - QString relative = parts[0]; - relative.replace('.', '/'); - relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; - - if (!m_is_native) - relative += ".jar"; - else - { - if (m_native_suffixes.contains(currentSystem)) - { - relative += "-" + m_native_suffixes[currentSystem] + ".jar"; - } - else - { - // really, bad. - relative += ".jar"; - } - } - - m_decentname = parts[1]; - m_decentversion = parts[2]; - m_storage_path = relative; - m_download_url = m_base_url + relative; - - if (m_rules.empty()) - { - m_is_active = true; - } - else - { - RuleAction result = Disallow; - for (auto rule : m_rules) - { - RuleAction temp = rule->apply(this); - if (temp != Defer) - result = temp; - } - m_is_active = (result == Allow); - } - if (m_is_native) - { - m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); - m_decenttype = "Native"; - } - else - { - m_decenttype = "Java"; - } -} - -void OneSixLibrary::setName(QString name) -{ - m_name = name; -} -void OneSixLibrary::setBaseUrl(QString base_url) -{ - m_base_url = base_url; -} -void OneSixLibrary::setIsNative() -{ - m_is_native = true; -} -void OneSixLibrary::addNative(OpSys os, QString suffix) -{ - m_is_native = true; - m_native_suffixes[os] = suffix; -} -void OneSixLibrary::setRules(QList> rules) -{ - m_rules = rules; -} -bool OneSixLibrary::isActive() -{ - return m_is_active; -} -bool OneSixLibrary::isNative() -{ - return m_is_native; -} -QString OneSixLibrary::downloadUrl() -{ - if (m_absolute_url.size()) - return m_absolute_url; - return m_download_url; -} -QString OneSixLibrary::storagePath() -{ - return m_storage_path; -} - -void OneSixLibrary::setAbsoluteUrl(QString absolute_url) -{ - m_absolute_url = absolute_url; -} - -QString OneSixLibrary::absoluteUrl() -{ - return m_absolute_url; -} - -void OneSixLibrary::setHint(QString hint) -{ - m_hint = hint; -} - -QString OneSixLibrary::hint() -{ - return m_hint; -} - -bool OneSixLibrary::filesExist() -{ - QString storage = storagePath(); - if (storage.contains("${arch}")) - { - QString cooked_storage = storage; - cooked_storage.replace("${arch}", "32"); - QFileInfo info32(PathCombine("libraries", cooked_storage)); - if (!info32.exists()) - { - return false; - } - cooked_storage = storage; - cooked_storage.replace("${arch}", "64"); - QFileInfo info64(PathCombine("libraries", cooked_storage)); - if (!info64.exists()) - { - return false; - } - } - else - { - QFileInfo info(PathCombine("libraries", storage)); - if (!info.exists()) - { - return false; - } - } - return true; -} - -bool OneSixLibrary::extractTo(QString target_dir) -{ - QString storage = storagePath(); - if (storage.contains("${arch}")) - { - QString cooked_storage = storage; - cooked_storage.replace("${arch}", "32"); - QString origin = PathCombine("libraries", cooked_storage); - QString target_dir_cooked = PathCombine(target_dir, "32"); - if(!ensureFolderPathExists(target_dir_cooked)) - { - QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; - return false; - } - if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) - .isEmpty()) - { - QLOG_ERROR() << "Couldn't extract " + origin; - return false; - } - cooked_storage = storage; - cooked_storage.replace("${arch}", "64"); - origin = PathCombine("libraries", cooked_storage); - target_dir_cooked = PathCombine(target_dir, "64"); - if(!ensureFolderPathExists(target_dir_cooked)) - { - QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; - return false; - } - if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) - .isEmpty()) - { - QLOG_ERROR() << "Couldn't extract " + origin; - return false; - } - } - else - { - if(!ensureFolderPathExists(target_dir)) - { - QLOG_ERROR() << "Couldn't create folder " + target_dir; - return false; - } - QString path = PathCombine("libraries", storage); - if (JlCompress::extractWithExceptions(path, target_dir, extract_excludes).isEmpty()) - { - QLOG_ERROR() << "Couldn't extract " + path; - return false; - } - } - return true; -} - -QJsonObject OneSixLibrary::toJson() -{ - QJsonObject libRoot; - libRoot.insert("name", m_name); - if (m_absolute_url.size()) - libRoot.insert("MMC-absoluteUrl", m_absolute_url); - if (m_hint.size()) - libRoot.insert("MMC-hint", m_hint); - if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - m_base_url != "https://" + URLConstants::LIBRARY_BASE) - libRoot.insert("url", m_base_url); - if (isNative() && m_native_suffixes.size()) - { - QJsonObject nativeList; - auto iter = m_native_suffixes.begin(); - while (iter != m_native_suffixes.end()) - { - nativeList.insert(OpSys_toString(iter.key()), iter.value()); - iter++; - } - libRoot.insert("natives", nativeList); - } - if (isNative() && extract_excludes.size()) - { - QJsonArray excludes; - QJsonObject extract; - for (auto exclude : extract_excludes) - { - excludes.append(exclude); - } - extract.insert("exclude", excludes); - libRoot.insert("extract", extract); - } - if (m_rules.size()) - { - QJsonArray allRules; - for (auto &rule : m_rules) - { - QJsonObject ruleObj = rule->toJson(); - allRules.append(ruleObj); - } - libRoot.insert("rules", allRules); - } - return libRoot; -} diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h deleted file mode 100644 index 227cdbef..00000000 --- a/logic/OneSixLibrary.h +++ /dev/null @@ -1,132 +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 -#include -#include - -#include "logic/net/URLConstants.h" -#include "OpSys.h" - -class Rule; - -class OneSixLibrary -{ -private: - // basic values used internally (so far) - QString m_name; - QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; - QList> m_rules; - - // custom values - /// absolute URL. takes precedence over m_download_path, if defined - QString m_absolute_url; - /// download hint - how to actually get the library - QString m_hint; - - // derived values used for real things - /// a decent name fit for display - QString m_decentname; - /// a decent version fit for display - QString m_decentversion; - /// a decent type fit for display - QString m_decenttype; - /// where to store the lib locally - QString m_storage_path; - /// where to download the lib from - QString m_download_url; - /// is this lib actually active on the current OS? - bool m_is_active = false; - /// is the library a native? - bool m_is_native = false; - /// native suffixes per OS - QMap m_native_suffixes; - -public: - QStringList extract_excludes; - -public: - /// Constructor - OneSixLibrary(QString name) - { - m_name = name; - } - - /// Returns the raw name field - QString rawName() const - { - return m_name; - } - - QJsonObject toJson(); - - /** - * finalize the library, processing the input values into derived values and state - * - * This SHALL be called after all the values are parsed or after any further change. - */ - void finalize(); - - /// Set the library composite name - void setName(QString name); - /// get a decent-looking name - QString name() - { - return m_decentname; - } - /// get a decent-looking version - QString version() - { - return m_decentversion; - } - /// what kind of library is it? (for display) - QString type() - { - return m_decenttype; - } - /// Set the url base for downloads - void setBaseUrl(QString base_url); - - /// Call this to mark the library as 'native' (it's a zip archive with DLLs) - void setIsNative(); - /// Attach a name suffix to the specified OS native - void addNative(OpSys os, QString suffix); - /// Set the load rules - void setRules(QList> rules); - - /// Returns true if the library should be loaded (or extracted, in case of natives) - bool isActive(); - /// Returns true if the library is native - bool isNative(); - /// Get the URL to download the library from - QString downloadUrl(); - /// Get the relative path where the library should be saved - QString storagePath(); - - /// set an absolute URL for the library. This is an MMC extension. - void setAbsoluteUrl(QString absolute_url); - QString absoluteUrl(); - - /// set a hint about how to treat the library. This is an MMC extension. - void setHint(QString hint); - QString hint(); - - bool extractTo(QString target_dir); - bool filesExist(); -}; diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp deleted file mode 100644 index 392b1dd1..00000000 --- a/logic/OneSixRule.cpp +++ /dev/null @@ -1,89 +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 -#include - -#include "OneSixRule.h" - -QList> rulesFromJsonV4(QJsonObject &objectWithRules) -{ - QList> rules; - auto rulesVal = objectWithRules.value("rules"); - if (!rulesVal.isArray()) - return rules; - - QJsonArray ruleList = rulesVal.toArray(); - for (auto ruleVal : ruleList) - { - std::shared_ptr rule; - if (!ruleVal.isObject()) - continue; - auto ruleObj = ruleVal.toObject(); - auto actionVal = ruleObj.value("action"); - if (!actionVal.isString()) - continue; - auto action = RuleAction_fromString(actionVal.toString()); - if (action == Defer) - continue; - - auto osVal = ruleObj.value("os"); - if (!osVal.isObject()) - { - // add a new implicit action rule - rules.append(ImplicitRule::create(action)); - continue; - } - - auto osObj = osVal.toObject(); - auto osNameVal = osObj.value("name"); - if (!osNameVal.isString()) - continue; - OpSys requiredOs = OpSys_fromString(osNameVal.toString()); - QString versionRegex = osObj.value("version").toString(); - // add a new OS rule - rules.append(OsRule::create(action, requiredOs, versionRegex)); - } - return rules; -} - -QJsonObject ImplicitRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - return ruleObj; -} - -QJsonObject OsRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - QJsonObject osObj; - { - osObj.insert("name", OpSys_toString(m_system)); - osObj.insert("version", m_version_regexp); - } - ruleObj.insert("os", osObj); - return ruleObj; -} - -RuleAction RuleAction_fromString(QString name) -{ - if (name == "allow") - return Allow; - if (name == "disallow") - return Disallow; - return Defer; -} \ No newline at end of file diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h deleted file mode 100644 index 5a13cbd9..00000000 --- a/logic/OneSixRule.h +++ /dev/null @@ -1,98 +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 "logic/OneSixLibrary.h" - -enum RuleAction -{ - Allow, - Disallow, - Defer -}; - -RuleAction RuleAction_fromString(QString); -QList> rulesFromJsonV4(QJsonObject &objectWithRules); - -class Rule -{ -protected: - RuleAction m_result; - virtual bool applies(OneSixLibrary *parent) = 0; - -public: - Rule(RuleAction result) : m_result(result) - { - } - virtual ~Rule() {}; - virtual QJsonObject toJson() = 0; - RuleAction apply(OneSixLibrary *parent) - { - if (applies(parent)) - return m_result; - else - return Defer; - } - ; -}; - -class OsRule : public Rule -{ -private: - // the OS - OpSys m_system; - // the OS version regexp - QString m_version_regexp; - -protected: - virtual bool applies(OneSixLibrary *) - { - return (m_system == currentSystem); - } - OsRule(RuleAction result, OpSys system, QString version_regexp) - : Rule(result), m_system(system), m_version_regexp(version_regexp) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result, OpSys system, - QString version_regexp) - { - return std::shared_ptr(new OsRule(result, system, version_regexp)); - } -}; - -class ImplicitRule : public Rule -{ -protected: - virtual bool applies(OneSixLibrary *) - { - return true; - } - ImplicitRule(RuleAction result) : Rule(result) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result) - { - return std::shared_ptr(new ImplicitRule(result)); - } -}; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp deleted file mode 100644 index 0119ab07..00000000 --- a/logic/OneSixUpdate.cpp +++ /dev/null @@ -1,377 +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 "MultiMC.h" -#include "OneSixUpdate.h" - -#include - -#include -#include -#include -#include - -#include "BaseInstance.h" -#include "lists/MinecraftVersionList.h" -#include "OneSixVersion.h" -#include "OneSixLibrary.h" -#include "OneSixInstance.h" -#include "net/ForgeMirrors.h" -#include "net/URLConstants.h" -#include "assets/AssetsUtils.h" - -#include "pathutils.h" -#include - -OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) - : Task(parent), m_inst(inst), m_only_prepare(only_prepare) -{ -} - -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; - } - - if (m_only_prepare) - { - prepareForLaunch(); - return; - } - - if (m_inst->shouldUpdate()) - { - // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = std::dynamic_pointer_cast( - MMC->minecraftlist()->findVersion(intendedVersion)); - if (targetVersion == nullptr) - { - // don't do anything if it was invalid - emitFailed("The specified Minecraft version is invalid. Choose a different one."); - return; - } - versionFileStart(); - } - else - { - jarlibStart(); - } -} - -void OneSixUpdate::versionFileStart() -{ - QLOG_INFO() << m_inst->name() << ": getting version file."; - 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"); - job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); - specificVersionDownloadJob.reset(job); - connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished())); - connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); - specificVersionDownloadJob->start(); -} - -void OneSixUpdate::versionFileFinished() -{ - NetActionPtr DlJob = specificVersionDownloadJob->first(); - OneSixInstance *inst = (OneSixInstance *)m_inst; - - QString version_id = targetVersion->descriptor(); - QString inst_dir = m_inst->instanceRoot(); - // save the version file in $instanceId/version.json - { - QString version1 = PathCombine(inst_dir, "/version.json"); - ensureFilePathExists(version1); - // FIXME: detect errors here, download to a temp file, swap - QSaveFile vfile1(version1); - if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) - { - emitFailed("Can't open " + version1 + " for writing."); - return; - } - auto data = std::dynamic_pointer_cast(DlJob)->m_data; - qint64 actual = 0; - if ((actual = vfile1.write(data)) != data.size()) - { - emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " + - data.size() + '.'); - return; - } - if (!vfile1.commit()) - { - emitFailed("Can't commit changes to " + version1); - return; - } - } - - // the version is downloaded safely. update is 'done' at this point - m_inst->setShouldUpdate(false); - - // delete any custom version inside the instance (it's no longer relevant, we did an update) - QString custom = PathCombine(inst_dir, "/custom.json"); - QFile finfo(custom); - if (finfo.exists()) - { - finfo.remove(); - } - inst->reloadFullVersion(); - - jarlibStart(); -} - -void OneSixUpdate::versionFileFailed() -{ - emitFailed("Failed to download the version description. Try again."); -} - -void OneSixUpdate::assetIndexStart() -{ - setStatus(tr("Updating assets index...")); - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); - QString assetName = version->assets; - QUrl indexUrl = "http://" + URLConstants::AWS_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 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 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://" + URLConstants::RESOURCE_BASE + objectName), - objectFile.filePath()); - objectDL->m_total_progress = object.size; - dls.append(objectDL); - } - } - if (dls.size()) - { - setStatus(tr("Getting the assets files from Mojang...")); - 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(tr("Getting the library files from Mojang...")); - QLOG_INFO() << m_inst->name() << ": downloading libraries"; - OneSixInstance *inst = (OneSixInstance *)m_inst; - bool successful = inst->reloadFullVersion(); - if (!successful) - { - emitFailed("Failed to load the version description file. It might be " - "corrupted, missing or simply too new."); - return; - } - - // Build a list of URLs that will need to be downloaded. - std::shared_ptr version = inst->getFullVersion(); - // minecraft.jar for this version - { - QString version_id = version->id; - QString localPath = version_id + "/" + version_id + ".jar"; - QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; - - auto job = new NetJob("Libraries for instance " + inst->name()); - - auto metacache = MMC->metacache(); - auto entry = metacache->resolveEntry("versions", localPath); - job->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); - - jarlibDownloadJob.reset(job); - } - - auto libs = version->getActiveNativeLibs(); - libs.append(version->getActiveNormalLibs()); - - auto metacache = MMC->metacache(); - QList ForgeLibs; - for (auto lib : libs) - { - if (lib->hint() == "local") - continue; - - QString raw_storage = lib->storagePath(); - QString raw_dl = lib->downloadUrl(); - - auto f = [&](QString storage, QString dl) - { - auto entry = metacache->resolveEntry("libraries", storage); - if (entry->stale) - { - if (lib->hint() == "forge-pack-xz") - { - ForgeLibs.append(ForgeXzDownload::make(storage, entry)); - } - else - { - jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); - } - } - }; - if (raw_storage.contains("${arch}")) - { - QString cooked_storage = raw_storage; - QString cooked_dl = raw_dl; - f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); - cooked_storage = raw_storage; - cooked_dl = raw_dl; - f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); - } - else - { - f(raw_storage, raw_dl); - } - } - // TODO: think about how to propagate this from the original json file... or IF AT ALL - QString forgeMirrorList = "http://files.minecraftforge.net/mirror-brand.list"; - if (!ForgeLibs.empty()) - { - jarlibDownloadJob->addNetAction( - ForgeMirrors::make(ForgeLibs, jarlibDownloadJob, forgeMirrorList)); - } - - connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); - connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed())); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); - - jarlibDownloadJob->start(); -} - -void OneSixUpdate::jarlibFinished() -{ - assetIndexStart(); -} - -void OneSixUpdate::jarlibFailed() -{ - QStringList failed = jarlibDownloadJob->getFailedFiles(); - QString failed_all = failed.join("\n"); - emitFailed("Failed to download the following files:\n" + failed_all + - "\n\nPlease try again."); -} - -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; - } - /* - * emitFailed("Could not create the native library folder:\n" + natives_dir_raw + - "\nMake sure MultiMC has appropriate permissions and there is enough - space " - "on the storage device."); - */ - 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(); -} diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h deleted file mode 100644 index bc717a94..00000000 --- a/logic/OneSixUpdate.h +++ /dev/null @@ -1,63 +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 - -#include "logic/net/NetJob.h" -#include "logic/tasks/Task.h" - -class MinecraftVersion; -class BaseInstance; - -class OneSixUpdate : public Task -{ - Q_OBJECT -public: - explicit OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); - virtual void executeTask(); - -private -slots: - void versionFileStart(); - void versionFileFinished(); - void versionFileFailed(); - - void jarlibStart(); - void jarlibFinished(); - void jarlibFailed(); - - void assetIndexStart(); - void assetIndexFinished(); - void assetIndexFailed(); - - void assetsFinished(); - void assetsFailed(); - - // extract the appropriate libraries - void prepareForLaunch(); - -private: - NetJobPtr specificVersionDownloadJob; - NetJobPtr jarlibDownloadJob; - - // target version, determined during this task - std::shared_ptr targetVersion; - BaseInstance *m_inst = nullptr; - bool m_only_prepare = false; -}; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 8ae685f0..3571be9b 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -13,14 +13,14 @@ * limitations under the License. */ -#include "logic/OneSixVersion.h" -#include "logic/OneSixLibrary.h" -#include "logic/OneSixRule.h" +#include "logic/DerpVersion.h" +#include "logic/DerpLibrary.h" +#include "logic/DerpRule.h" #include "logger/QsLog.h" -std::shared_ptr fromJsonV4(QJsonObject root, - std::shared_ptr fullVersion) +std::shared_ptr fromJsonV4(QJsonObject root, + std::shared_ptr fullVersion) { fullVersion->id = root.value("id").toString(); @@ -93,7 +93,7 @@ std::shared_ptr fromJsonV4(QJsonObject root, auto nameVal = libObj.value("name"); if (!nameVal.isString()) continue; - std::shared_ptr library(new OneSixLibrary(nameVal.toString())); + std::shared_ptr library(new DerpLibrary(nameVal.toString())); auto urlVal = libObj.value("url"); if (urlVal.isString()) @@ -158,9 +158,9 @@ std::shared_ptr fromJsonV4(QJsonObject root, return fullVersion; } -std::shared_ptr OneSixVersion::fromJson(QJsonObject root) +std::shared_ptr DerpVersion::fromJson(QJsonObject root) { - std::shared_ptr readVersion(new OneSixVersion()); + std::shared_ptr readVersion(new DerpVersion()); int launcher_ver = readVersion->minimumLauncherVersion = root.value("minimumLauncherVersion").toDouble(); @@ -169,16 +169,16 @@ std::shared_ptr OneSixVersion::fromJson(QJsonObject root) return fromJsonV4(root, readVersion); else { - return std::shared_ptr(); + return std::shared_ptr(); } } -std::shared_ptr OneSixVersion::fromFile(QString filepath) +std::shared_ptr DerpVersion::fromFile(QString filepath) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { - return std::shared_ptr(); + return std::shared_ptr(); } auto data = file.readAll(); @@ -187,12 +187,12 @@ std::shared_ptr OneSixVersion::fromFile(QString filepath) if (jsonError.error != QJsonParseError::NoError) { - return std::shared_ptr(); + return std::shared_ptr(); } if (!jsonDoc.isObject()) { - return std::shared_ptr(); + return std::shared_ptr(); } QJsonObject root = jsonDoc.object(); auto version = fromJson(root); @@ -201,7 +201,7 @@ std::shared_ptr OneSixVersion::fromFile(QString filepath) return version; } -bool OneSixVersion::toOriginalFile() +bool DerpVersion::toOriginalFile() { if (original_file.isEmpty()) return false; @@ -232,9 +232,9 @@ bool OneSixVersion::toOriginalFile() return file.commit(); } -QList> OneSixVersion::getActiveNormalLibs() +QList> DerpVersion::getActiveNormalLibs() { - QList> output; + QList> output; for (auto lib : libraries) { if (lib->isActive() && !lib->isNative()) @@ -245,9 +245,9 @@ QList> OneSixVersion::getActiveNormalLibs() return output; } -QList> OneSixVersion::getActiveNativeLibs() +QList> DerpVersion::getActiveNativeLibs() { - QList> output; + QList> output; for (auto lib : libraries) { if (lib->isActive() && lib->isNative()) @@ -258,17 +258,17 @@ QList> OneSixVersion::getActiveNativeLibs() return output; } -void OneSixVersion::externalUpdateStart() +void DerpVersion::externalUpdateStart() { beginResetModel(); } -void OneSixVersion::externalUpdateFinish() +void DerpVersion::externalUpdateFinish() { endResetModel(); } -QVariant OneSixVersion::data(const QModelIndex &index, int role) const +QVariant DerpVersion::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); @@ -296,7 +296,7 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const return QVariant(); } -Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const +Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; @@ -312,7 +312,7 @@ Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const // return QAbstractListModel::flags(index); } -QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const +QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); @@ -329,12 +329,12 @@ QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int } } -int OneSixVersion::rowCount(const QModelIndex &parent) const +int DerpVersion::rowCount(const QModelIndex &parent) const { return libraries.size(); } -int OneSixVersion::columnCount(const QModelIndex &parent) const +int DerpVersion::columnCount(const QModelIndex &parent) const { return 3; } diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 036f3d53..e8f744aa 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -17,9 +17,9 @@ #include #include -class OneSixLibrary; +class DerpLibrary; -class OneSixVersion : public QAbstractListModel +class DerpVersion : public QAbstractListModel { // Things required to implement the Qt list model public: @@ -33,12 +33,12 @@ public: // serialization/deserialization public: bool toOriginalFile(); - static std::shared_ptr fromJson(QJsonObject root); - static std::shared_ptr fromFile(QString filepath); + static std::shared_ptr fromJson(QJsonObject root); + static std::shared_ptr fromFile(QString filepath); public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); // called when something starts/stops messing with the object // FIXME: these are ugly in every possible way. void externalUpdateStart(); @@ -81,7 +81,7 @@ public: QString mainClass; /// the list of libs - both active and inactive, native and java - QList> libraries; + QList> libraries; /* FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 91f86df0..29894b5a 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -234,18 +234,18 @@ void MCVListLoadTask::list_downloaded() } // Parse the type. MinecraftVersion::VersionType versionType; - // OneSix or Legacy. use filter to determine type + // Derp or Legacy. use filter to determine type if (versionTypeStr == "release") { versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy - : MinecraftVersion::OneSix; + : MinecraftVersion::Derp; is_latest = (versionID == latestReleaseID); is_snapshot = false; } else if (versionTypeStr == "snapshot") // It's a snapshot... yay { versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy - : MinecraftVersion::OneSix; + : MinecraftVersion::Derp; is_latest = (versionID == latestSnapshotID); is_snapshot = true; } -- cgit v1.2.3 From 13ac46bc185e861f9772f85adf757890dbb80a57 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 14:06:32 +0100 Subject: Fix launching --- logic/DerpInstance.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'logic') diff --git a/logic/DerpInstance.cpp b/logic/DerpInstance.cpp index 31ed7c95..440c90a4 100644 --- a/logic/DerpInstance.cpp +++ b/logic/DerpInstance.cpp @@ -227,9 +227,7 @@ MinecraftProcess *DerpInstance::prepareForLaunch(MojangAccountPtr account) QDir natives_dir(PathCombine(instanceRoot(), "natives/")); launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "natives " + natives_dir.absolutePath() + "\n"; - launchScript += "launch onesix"; - - qDebug() << launchScript; + launchScript += "launch onesix\n"; // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); -- cgit v1.2.3 From 0a592ab99bc68ad918f74206edf2a77cc257683c Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 15:20:48 +0100 Subject: Work towards liteloader support. Fix creating new instance --- logic/DerpInstance.cpp | 22 ++++++-- logic/DerpInstance.h | 2 + logic/DerpVersion.cpp | 15 ++++++ logic/DerpVersion.h | 1 + logic/DerpVersionBuilder.cpp | 18 ++----- logic/LiteLoaderInstaller.cpp | 117 ++++++++++++++++++++++++------------------ logic/LiteLoaderInstaller.h | 18 ++++--- 7 files changed, 116 insertions(+), 77 deletions(-) (limited to 'logic') diff --git a/logic/DerpInstance.cpp b/logic/DerpInstance.cpp index 672ba6c4..e6b92b1f 100644 --- a/logic/DerpInstance.cpp +++ b/logic/DerpInstance.cpp @@ -35,10 +35,16 @@ DerpInstance::DerpInstance(const QString &rootDir, SettingsObject *settings, QOb d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); d->version.reset(new DerpVersion(this, this)); - reloadFullVersion(); + if (QDir(instanceRoot()).exists("version.json")) + { + reloadFullVersion(); + } + else + { + clearFullVersion(); + } } - std::shared_ptr DerpInstance::doUpdate(bool only_prepare) { return std::shared_ptr(new DerpUpdate(this, only_prepare)); @@ -280,9 +286,8 @@ bool DerpInstance::setIntendedVersionId(QString version) { settings().set("IntendedVersion", version); setShouldUpdate(true); - auto pathOrig = PathCombine(instanceRoot(), "version.json"); - QFile::remove(pathOrig); - reloadFullVersion(); + QFile::remove(PathCombine(instanceRoot(), "version.json")); + clearFullVersion(); return true; } @@ -327,6 +332,13 @@ bool DerpInstance::reloadFullVersion(QWidget *widgetParent) return ret; } +void DerpInstance::clearFullVersion() +{ + I_D(DerpInstance); + d->version->clear(); + emit versionReloaded(); +} + std::shared_ptr DerpInstance::getFullVersion() { I_D(DerpInstance); diff --git a/logic/DerpInstance.h b/logic/DerpInstance.h index 37d3df52..46f953c0 100644 --- a/logic/DerpInstance.h +++ b/logic/DerpInstance.h @@ -53,6 +53,8 @@ public: /// reload the full version json files. return true on success! bool reloadFullVersion(QWidget *widgetParent = 0); + /// clears all version information in preparation for an update + void clearFullVersion(); /// get the current full version info std::shared_ptr getFullVersion(); /// is the current version original, or custom? diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp index f13ac620..cabb2b24 100644 --- a/logic/DerpVersion.cpp +++ b/logic/DerpVersion.cpp @@ -22,6 +22,7 @@ DerpVersion::DerpVersion(DerpInstance *instance, QObject *parent) : QAbstractListModel(parent), m_instance(instance) { + clear(); } bool DerpVersion::reload(QWidget *widgetParent) @@ -29,6 +30,20 @@ bool DerpVersion::reload(QWidget *widgetParent) return DerpVersionBuilder::build(this, m_instance, widgetParent); } +void DerpVersion::clear() +{ + id.clear(); + time.clear(); + releaseTime.clear(); + type.clear(); + assets.clear(); + processArguments.clear(); + minecraftArguments.clear(); + minimumLauncherVersion = 0xDEADBEAF; + mainClass.clear(); + libraries.clear(); +} + QList > DerpVersion::getActiveNormalLibs() { QList > output; diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h index cadfa850..f2132ad8 100644 --- a/logic/DerpVersion.h +++ b/logic/DerpVersion.h @@ -39,6 +39,7 @@ public: virtual Qt::ItemFlags flags(const QModelIndex &index) const; bool reload(QWidget *widgetParent); + void clear(); public: QList> getActiveNormalLibs(); diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp index d8091f32..b0da8205 100644 --- a/logic/DerpVersionBuilder.cpp +++ b/logic/DerpVersionBuilder.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "DerpVersion.h" #include "DerpInstance.h" @@ -45,7 +46,7 @@ bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWi bool DerpVersionBuilder::build() { - clear(); + m_version->clear(); QDir root(m_instance->instanceRoot()); QDir patches(root.absoluteFilePath("patches/")); @@ -112,20 +113,6 @@ bool DerpVersionBuilder::build() return true; } -void DerpVersionBuilder::clear() -{ - m_version->id.clear(); - m_version->time.clear(); - m_version->releaseTime.clear(); - m_version->type.clear(); - m_version->assets.clear(); - m_version->processArguments.clear(); - m_version->minecraftArguments.clear(); - m_version->minimumLauncherVersion = 0xDEADBEAF; - m_version->mainClass.clear(); - m_version->libraries.clear(); -} - void applyString(const QJsonObject &obj, const QString &key, QString &out) { if (obj.contains(key) && obj.value(key).isString()) @@ -181,6 +168,7 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) } // libraries + if (object.contains("libraries")) { auto librariesValue = object.value("libraries"); if (!librariesValue.isArray()) diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 8a120eab..8aed47e7 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -15,12 +15,18 @@ #include "LiteLoaderInstaller.h" +#include +#include + +#include "logger/QsLog.h" + #include "DerpVersion.h" #include "DerpLibrary.h" +#include "DerpInstance.h" QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; -LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion) +LiteLoaderInstaller::LiteLoaderInstaller() { if (m_launcherWrapperVersionMapping.isEmpty()) { @@ -31,71 +37,82 @@ LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion } } -bool LiteLoaderInstaller::canApply() const +bool LiteLoaderInstaller::canApply(DerpInstance *instance) const { - return m_launcherWrapperVersionMapping.contains(m_mcVersion); + return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId()); } -bool LiteLoaderInstaller::apply(std::shared_ptr to) +bool LiteLoaderInstaller::isApplied(DerpInstance *on) { - // DERPFIX + return QFile::exists(filename(on->instanceRoot())); +} - applyLaunchwrapper(to); - applyLiteLoader(to); +bool LiteLoaderInstaller::add(DerpInstance *to) +{ + if (!patchesDir(to->instanceRoot()).exists()) + { + QDir(to->instanceRoot()).mkdir("patches"); + } - to->mainClass = "net.minecraft.launchwrapper.Launch"; - if (!to->minecraftArguments.contains( - " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker")) + if (isApplied(to)) { - to->minecraftArguments.append( - " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"); + if (!remove(to)) + { + return false; + } } - return true; -} + QJsonObject obj; -void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) -{ - const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion]; + obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch")); + obj.insert("+minecraftArguments", QString(" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker")); + obj.insert("order", 10); + + QJsonArray libraries; - QMutableListIterator> it(to->libraries); - while (it.hasNext()) + // launchwrapper { - it.next(); - if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:")) - { - if (it.value()->version() >= intendedVersion) - { - return; - } - else - { - it.remove(); - } - } + DerpLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); + launchwrapperLib.finalize(); + QJsonObject lwLibObj = launchwrapperLib.toJson(); + lwLibObj.insert("insert", QString("beginning")); + libraries.append(lwLibObj); } - std::shared_ptr lib(new DerpLibrary( - "net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion])); - lib->finalize(); - to->libraries.prepend(lib); -} + // liteloader + { + DerpLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId()); + liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/"); + liteloaderLib.finalize(); + QJsonObject llLibObj = liteloaderLib.toJson(); + llLibObj.insert("insert", QString("beginning")); + libraries.append(llLibObj); + } -void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) -{ - QMutableListIterator> it(to->libraries); - while (it.hasNext()) + obj.insert("+libraries", libraries); + + QFile file(filename(to->instanceRoot())); + if (!file.open(QFile::WriteOnly)) { - it.next(); - if (it.value()->rawName().startsWith("com.mumfrey:liteloader:")) - { - it.remove(); - } + QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); + return false; } + file.write(QJsonDocument(obj).toJson()); + file.close(); - std::shared_ptr lib( - new DerpLibrary("com.mumfrey:liteloader:" + m_mcVersion)); - lib->setBaseUrl("http://dl.liteloader.com/versions/"); - lib->finalize(); - to->libraries.prepend(lib); + return true; +} + +bool LiteLoaderInstaller::remove(DerpInstance *from) +{ + return QFile::remove(filename(from->instanceRoot())); +} + +QString LiteLoaderInstaller::filename(const QString &root) const +{ + return patchesDir(root).absoluteFilePath(id() + ".json"); +} +QDir LiteLoaderInstaller::patchesDir(const QString &root) const +{ + return QDir(root + "/patches/"); } diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index d7b8ce77..48ef4baf 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -19,21 +19,25 @@ #include class DerpVersion; +class DerpInstance; +class QDir; +// TODO base class class LiteLoaderInstaller { public: - LiteLoaderInstaller(const QString &mcVersion); + LiteLoaderInstaller(); - bool canApply() const; + bool canApply(DerpInstance *instance) const; + bool isApplied(DerpInstance *on); - bool apply(std::shared_ptr to); + bool add(DerpInstance *to); + bool remove(DerpInstance *from); private: - QString m_mcVersion; - - void applyLaunchwrapper(std::shared_ptr to); - void applyLiteLoader(std::shared_ptr to); + virtual QString id() const { return "com.mumfrey.liteloader"; } + QString filename(const QString &root) const; + QDir patchesDir(const QString &root) const; static QMap m_launcherWrapperVersionMapping; }; -- cgit v1.2.3 From c39d26f4453166603749826eee1a732c815047d0 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 22:15:50 +0100 Subject: Got liteloader working. Patching more or less works --- logic/DerpRule.cpp | 2 +- logic/DerpRule.h | 2 +- logic/DerpVersionBuilder.cpp | 246 +++++++++++++++++++++++++++++++++--------- logic/DerpVersionBuilder.h | 8 ++ logic/LiteLoaderInstaller.cpp | 4 +- 5 files changed, 208 insertions(+), 54 deletions(-) (limited to 'logic') diff --git a/logic/DerpRule.cpp b/logic/DerpRule.cpp index d4cf1ba3..5ed200e3 100644 --- a/logic/DerpRule.cpp +++ b/logic/DerpRule.cpp @@ -18,7 +18,7 @@ #include "DerpRule.h" -QList> rulesFromJsonV4(QJsonObject &objectWithRules) +QList> rulesFromJsonV4(const QJsonObject &objectWithRules) { QList> rules; auto rulesVal = objectWithRules.value("rules"); diff --git a/logic/DerpRule.h b/logic/DerpRule.h index 7895ea98..0254028c 100644 --- a/logic/DerpRule.h +++ b/logic/DerpRule.h @@ -27,7 +27,7 @@ enum RuleAction }; RuleAction RuleAction_fromString(QString); -QList> rulesFromJsonV4(QJsonObject &objectWithRules); +QList> rulesFromJsonV4(const QJsonObject &objectWithRules); class Rule { diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp index b0da8205..eb215cca 100644 --- a/logic/DerpVersionBuilder.cpp +++ b/logic/DerpVersionBuilder.cpp @@ -113,12 +113,23 @@ bool DerpVersionBuilder::build() return true; } -void applyString(const QJsonObject &obj, const QString &key, QString &out) +void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) { if (obj.contains(key) && obj.value(key).isString()) { out = obj.value(key).toString(); } + else if (!onlyOverride) + { + if (obj.contains("+" + key) && obj.value("+" + key).isString()) + { + out += obj.value("+" + key).toString(); + } + else if (obj.contains("-" + key) && obj.value("-" + key).isString()) + { + out.remove(obj.value("-" + key).toString()); + } + } } void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(DerpLibrary::*func)(const QString &val)) { @@ -131,7 +142,7 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) { applyString(object, "id", m_version->id); applyString(object, "mainClass", m_version->mainClass); - applyString(object, "processArguments", m_version->processArguments); + applyString(object, "processArguments", m_version->processArguments, false); { const QString toCompare = m_version->processArguments.toLower(); if (toCompare == "legacy") @@ -147,7 +158,7 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; } } - applyString(object, "minecraftArguments", m_version->minecraftArguments); + applyString(object, "minecraftArguments", m_version->minecraftArguments, false); applyString(object, "type", m_version->type); applyString(object, "releaseTime", m_version->releaseTime); applyString(object, "time", m_version->time); @@ -170,80 +181,215 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) // libraries if (object.contains("libraries")) { + m_version->libraries.clear(); auto librariesValue = object.value("libraries"); if (!librariesValue.isArray()) { QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); return false; } + auto array = librariesValue.toArray(); + for (auto libVal : array) + { + if (libVal.isObject()) + { + if (!applyLibrary(libVal.toObject(), Override)) + { + return false; + } + } + + } + } + + // +libraries + if (object.contains("+libraries")) + { + auto librariesValue = object.value("+libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } for (auto libVal : librariesValue.toArray()) { - if (!libVal.isObject()) + if (libVal.isObject()) { - continue; + applyLibrary(libVal.toObject(), Add); } - QJsonObject libObj = libVal.toObject(); + } + } - // Library name - auto nameVal = libObj.value("name"); - if (!nameVal.isString()) + // -libraries + if (object.contains("-libraries")) + { + auto librariesValue = object.value("-libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } + for (auto libVal : librariesValue.toArray()) + { + if (libVal.isObject()) { - continue; + applyLibrary(libVal.toObject(), Remove); } - std::shared_ptr library(new DerpLibrary(nameVal.toString())); - applyString(libObj, "url", library, &DerpLibrary::setBaseUrl); - applyString(libObj, "MMC-hint", library, &DerpLibrary::setHint); - applyString(libObj, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); - applyString(libObj, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); + } + } + + return true; +} + +int findLibrary(QList > haystack, const QString &needle) +{ + for (int i = 0; i < haystack.size(); ++i) + { + if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(haystack.at(i)->rawName()) != -1) + { + return i; + } + } + return -1; +} + +bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionBuilder::Type type) +{ + // Library name + auto nameVal = lib.value("name"); + if (!nameVal.isString()) + { + return false; + } + auto name = nameVal.toString(); - auto extractVal = libObj.value("extract"); - if (extractVal.isObject()) + if (type == Remove) + { + int index = findLibrary(m_version->libraries, name); + if (index >= 0) + { + m_version->libraries.removeAt(index); + } + return true; + } + + if (type == Add && !lib.contains("insert")) + { + return false; + } + + std::shared_ptr library; + + if (lib.value("insert").toString() == "apply" && type == Add) + { + library = m_version->libraries[findLibrary(m_version->libraries, name)]; + } + else + { + library.reset(new DerpLibrary(nameVal.toString())); + } + + applyString(lib, "url", library, &DerpLibrary::setBaseUrl); + applyString(lib, "MMC-hint", library, &DerpLibrary::setHint); + applyString(lib, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); + applyString(lib, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); + + auto extractVal = lib.value("extract"); + if (extractVal.isObject()) + { + QStringList excludes; + auto extractObj = extractVal.toObject(); + auto excludesVal = extractObj.value("exclude"); + if (excludesVal.isArray()) + { + auto excludesList = excludesVal.toArray(); + for (auto excludeVal : excludesList) { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) + if (excludeVal.isString()) { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) - { - if (excludeVal.isString()) - { - excludes.append(excludeVal.toString()); - } - } - library->extract_excludes = excludes; + excludes.append(excludeVal.toString()); } } + library->extract_excludes = excludes; + } + } - auto nativesVal = libObj.value("natives"); - if (nativesVal.isObject()) + auto nativesVal = lib.value("natives"); + if (nativesVal.isObject()) + { + library->setIsNative(); + auto nativesObj = nativesVal.toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + auto osType = OpSys_fromString(it.key()); + if (osType == Os_Other) { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - auto osType = OpSys_fromString(it.key()); - if (osType == Os_Other) - { - continue; - } - if (!it.value().isString()) - { - continue; - } - library->addNative(osType, it.value().toString()); - } + continue; + } + if (!it.value().isString()) + { + continue; } + library->addNative(osType, it.value().toString()); + } + } - library->setRules(rulesFromJsonV4(libObj)); - library->finalize(); + if (lib.contains("rules")) + { + library->setRules(rulesFromJsonV4(lib)); + } + library->finalize(); + if (type == Override) + { + qDebug() << "appending" << library->rawName(); + m_version->libraries.append(library); + } + else if (lib.value("insert").toString() != "apply") + { + if (lib.value("insert").toString() == "append") + { m_version->libraries.append(library); } + else if (lib.value("insert").toString() == "prepend") + { + m_version->libraries.prepend(library); + } + else if (lib.value("insert").isObject()) + { + QJsonObject insertObj = lib.value("insert").toObject(); + if (insertObj.isEmpty()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty")); + return false; + } + const QString key = insertObj.keys().first(); + const QString value = insertObj.value(key).toString(); + const int index = findLibrary(m_version->libraries, value); + if (index >= 0) + { + if (key == "before") + { + m_version->libraries.insert(index, library); + } + else if (key == "after") + { + m_version->libraries.insert(index + 1, library); + } + else + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); + return false; + } + } + } + else + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); + return false; + } } - return true; } diff --git a/logic/DerpVersionBuilder.h b/logic/DerpVersionBuilder.h index 739065db..5354cb0e 100644 --- a/logic/DerpVersionBuilder.h +++ b/logic/DerpVersionBuilder.h @@ -34,10 +34,18 @@ private: DerpInstance *m_instance; QWidget *m_widgetParent; + enum Type + { + Override, + Add, + Remove + }; + bool build(); void clear(); bool apply(const QJsonObject &object); + bool applyLibrary(const QJsonObject &lib, const Type type); bool read(const QFileInfo &fileInfo, QJsonObject *out); }; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 8aed47e7..d582f03a 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -75,7 +75,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) DerpLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); launchwrapperLib.finalize(); QJsonObject lwLibObj = launchwrapperLib.toJson(); - lwLibObj.insert("insert", QString("beginning")); + lwLibObj.insert("insert", QString("prepend")); libraries.append(lwLibObj); } @@ -85,7 +85,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/"); liteloaderLib.finalize(); QJsonObject llLibObj = liteloaderLib.toJson(); - llLibObj.insert("insert", QString("beginning")); + llLibObj.insert("insert", QString("prepend")); libraries.append(llLibObj); } -- cgit v1.2.3 From 156bc8f27656c115bf1b023cd4ebc5f629df3887 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 23 Jan 2014 21:31:41 +0100 Subject: Forge works now too, and so does forge+liteloader --- logic/BaseInstaller.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++ logic/BaseInstaller.h | 39 +++++++++++++++++++++++++ logic/DerpFTBInstance.cpp | 2 +- logic/DerpVersion.cpp | 47 +++++++++++++++++++----------- logic/DerpVersion.h | 4 +++ logic/DerpVersionBuilder.cpp | 37 ++++++++++++++++++++++-- logic/DerpVersionBuilder.h | 2 ++ logic/ForgeInstaller.cpp | 48 +++++++++++++++++++++++-------- logic/ForgeInstaller.h | 9 ++++-- logic/LiteLoaderInstaller.cpp | 32 ++------------------- logic/LiteLoaderInstaller.h | 22 +++++---------- 11 files changed, 232 insertions(+), 76 deletions(-) create mode 100644 logic/BaseInstaller.cpp create mode 100644 logic/BaseInstaller.h (limited to 'logic') diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp new file mode 100644 index 00000000..74a8e909 --- /dev/null +++ b/logic/BaseInstaller.cpp @@ -0,0 +1,66 @@ +/* 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 "BaseInstaller.h" + +#include + +#include "DerpVersion.h" +#include "DerpLibrary.h" +#include "DerpInstance.h" + +#include "cmdutils.h" + +BaseInstaller::BaseInstaller() +{ + +} + +bool BaseInstaller::isApplied(DerpInstance *on) +{ + return QFile::exists(filename(on->instanceRoot())); +} + +bool BaseInstaller::add(DerpInstance *to) +{ + if (!patchesDir(to->instanceRoot()).exists()) + { + QDir(to->instanceRoot()).mkdir("patches"); + } + + if (isApplied(to)) + { + if (!remove(to)) + { + return false; + } + } + + return true; +} + +bool BaseInstaller::remove(DerpInstance *from) +{ + return QFile::remove(filename(from->instanceRoot())); +} + +QString BaseInstaller::filename(const QString &root) const +{ + return patchesDir(root).absoluteFilePath(id() + ".json"); +} +QDir BaseInstaller::patchesDir(const QString &root) const +{ + return QDir(root + "/patches/"); +} diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h new file mode 100644 index 00000000..08f78bfc --- /dev/null +++ b/logic/BaseInstaller.h @@ -0,0 +1,39 @@ +/* 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 + +class DerpInstance; +class QDir; +class QString; + +class BaseInstaller +{ +public: + BaseInstaller(); + + virtual bool canApply(DerpInstance *instance) const { return true; } + bool isApplied(DerpInstance *on); + + virtual bool add(DerpInstance *to); + virtual bool remove(DerpInstance *from); + +protected: + virtual QString id() const = 0; + QString filename(const QString &root) const; + QDir patchesDir(const QString &root) const; +}; diff --git a/logic/DerpFTBInstance.cpp b/logic/DerpFTBInstance.cpp index f9aeeca0..9b687461 100644 --- a/logic/DerpFTBInstance.cpp +++ b/logic/DerpFTBInstance.cpp @@ -61,7 +61,7 @@ slots: return; } auto version = instance->getFullVersion(); - if (!forge.apply(version)) + if (!forge.add(instance)) { emitFailed(tr("Couldn't install Forge")); return; diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp index cabb2b24..f4a8dd97 100644 --- a/logic/DerpVersion.cpp +++ b/logic/DerpVersion.cpp @@ -44,6 +44,26 @@ void DerpVersion::clear() libraries.clear(); } +void DerpVersion::dump() const +{ + qDebug().nospace() << "DerpVersion(" + << "\n\tid=" << id + << "\n\ttime=" << time + << "\n\treleaseTime=" << releaseTime + << "\n\ttype=" << type + << "\n\tassets=" << assets + << "\n\tprocessArguments=" << processArguments + << "\n\tminecraftArguments=" << minecraftArguments + << "\n\tminimumLauncherVersion=" << minimumLauncherVersion + << "\n\tmainClass=" << mainClass + << "\n\tlibraries="; + for (auto lib : libraries) + { + qDebug().nospace() << "\n\t\t" << lib.get(); + } + qDebug().nospace() << "\n)"; +} + QList > DerpVersion::getActiveNormalLibs() { QList > output; @@ -70,6 +90,16 @@ QList > DerpVersion::getActiveNativeLibs() return output; } +std::shared_ptr DerpVersion::fromJson(const QJsonObject &obj) +{ + std::shared_ptr version(new DerpVersion(0)); + if (DerpVersionBuilder::read(version.get(), obj)) + { + return version; + } + return 0; +} + QVariant DerpVersion::data(const QModelIndex &index, int role) const { if (!index.isValid()) @@ -143,22 +173,7 @@ int DerpVersion::columnCount(const QModelIndex &parent) const QDebug operator<<(QDebug &dbg, const DerpVersion *version) { - dbg.nospace() << "DerpVersion(" - << "\n\tid=" << version->id - << "\n\ttime=" << version->time - << "\n\treleaseTime=" << version->releaseTime - << "\n\ttype=" << version->type - << "\n\tassets=" << version->assets - << "\n\tprocessArguments=" << version->processArguments - << "\n\tminecraftArguments=" << version->minecraftArguments - << "\n\tminimumLauncherVersion=" << version->minimumLauncherVersion - << "\n\tmainClass=" << version->mainClass - << "\n\tlibraries="; - for (auto lib : version->libraries) - { - dbg.nospace() << "\n\t\t" << lib.get(); - } - dbg.nospace() << "\n)"; + version->dump(); return dbg.maybeSpace(); } QDebug operator<<(QDebug &dbg, const DerpLibrary *library) diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h index f2132ad8..4481d146 100644 --- a/logic/DerpVersion.h +++ b/logic/DerpVersion.h @@ -41,10 +41,14 @@ public: bool reload(QWidget *widgetParent); void clear(); + void dump() const; + public: QList> getActiveNormalLibs(); QList> getActiveNativeLibs(); + static std::shared_ptr fromJson(const QJsonObject &obj); + // data members public: /// the ID - determines which jar to use! ACTUALLY IMPORTANT! diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp index eb215cca..20f43404 100644 --- a/logic/DerpVersionBuilder.cpp +++ b/logic/DerpVersionBuilder.cpp @@ -29,6 +29,7 @@ #include "DerpVersion.h" #include "DerpInstance.h" #include "DerpRule.h" +#include "logger/QsLog.h" DerpVersionBuilder::DerpVersionBuilder() { @@ -44,6 +45,15 @@ bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWi return builder.build(); } +bool DerpVersionBuilder::read(DerpVersion *version, const QJsonObject &obj) +{ + DerpVersionBuilder builder; + builder.m_version = version; + builder.m_instance = 0; + builder.m_widgetParent = 0; + return builder.read(obj); +} + bool DerpVersionBuilder::build() { m_version->clear(); @@ -55,6 +65,7 @@ bool DerpVersionBuilder::build() // version.json { + QLOG_INFO() << "Reading version.json"; QJsonObject obj; if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) { @@ -73,6 +84,7 @@ bool DerpVersionBuilder::build() QMap objects; for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { + QLOG_INFO() << "Reading" << info.fileName(); QJsonObject obj; if (!read(info, &obj)) { @@ -87,6 +99,7 @@ bool DerpVersionBuilder::build() } for (auto object : objects.values()) { + qDebug() << "Applying object with order" << objects.key(object); if (!apply(object)) { return false; @@ -98,6 +111,7 @@ bool DerpVersionBuilder::build() { if (QFile::exists(root.absoluteFilePath("custom.json"))) { + QLOG_INFO() << "Reading custom.json"; QJsonObject obj; if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) { @@ -113,6 +127,13 @@ bool DerpVersionBuilder::build() return true; } +bool DerpVersionBuilder::read(const QJsonObject &obj) +{ + m_version->clear(); + + return apply(obj); +} + void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) { if (obj.contains(key) && obj.value(key).isString()) @@ -142,7 +163,9 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) { applyString(object, "id", m_version->id); applyString(object, "mainClass", m_version->mainClass); + applyString(object, "minecraftArguments", m_version->minecraftArguments, false); applyString(object, "processArguments", m_version->processArguments, false); + if (m_version->minecraftArguments.isEmpty()) { const QString toCompare = m_version->processArguments.toLower(); if (toCompare == "legacy") @@ -158,7 +181,6 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; } } - applyString(object, "minecraftArguments", m_version->minecraftArguments, false); applyString(object, "type", m_version->type); applyString(object, "releaseTime", m_version->releaseTime); applyString(object, "time", m_version->time); @@ -282,6 +304,18 @@ bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionB std::shared_ptr library; + if (lib.value("insert").toString() != "apply" && type == Add) + { + QMutableListIterator > it(m_version->libraries); + while (it.hasNext()) + { + if (it.next()->rawName() == name) + { + it.remove(); + } + } + } + if (lib.value("insert").toString() == "apply" && type == Add) { library = m_version->libraries[findLibrary(m_version->libraries, name)]; @@ -343,7 +377,6 @@ bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionB library->finalize(); if (type == Override) { - qDebug() << "appending" << library->rawName(); m_version->libraries.append(library); } else if (lib.value("insert").toString() != "apply") diff --git a/logic/DerpVersionBuilder.h b/logic/DerpVersionBuilder.h index 5354cb0e..b94cd8e9 100644 --- a/logic/DerpVersionBuilder.h +++ b/logic/DerpVersionBuilder.h @@ -28,6 +28,7 @@ class DerpVersionBuilder DerpVersionBuilder(); public: static bool build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent); + static bool read(DerpVersion *version, const QJsonObject &obj); private: DerpVersion *m_version; @@ -42,6 +43,7 @@ private: }; bool build(); + bool read(const QJsonObject &obj); void clear(); bool apply(const QJsonObject &object); diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 29735a7d..35663ddd 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -22,13 +22,13 @@ #include #include #include "MultiMC.h" +#include "DerpInstance.h" #include +#include #include #include -// DERPFIX - ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { std::shared_ptr newVersion; @@ -64,7 +64,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - // DERPFIX newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); + newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } @@ -109,12 +109,22 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); } -bool ForgeInstaller::apply(std::shared_ptr to) +bool ForgeInstaller::add(DerpInstance *to) { + if (!BaseInstaller::add(to)) + { + return false; + } + + QJsonObject obj; + obj.insert("order", 5); + if (!m_forge_version) return false; int sliding_insert_window = 0; { + QJsonArray librariesPlus; + // for each library in the version we are adding (except for the blacklisted) QSet blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"}; for (auto lib : m_forge_version->libraries) @@ -133,27 +143,43 @@ bool ForgeInstaller::apply(std::shared_ptr to) if (blacklist.contains(libName)) continue; - // find an entry that matches this one + QJsonObject libObj = lib->toJson(); + bool found = false; - for (auto tolib : to->libraries) + // find an entry that matches this one + for (auto tolib : to->getFullVersion()->libraries) { if (tolib->name() != libName) continue; found = true; // replace lib - tolib = lib; + libObj.insert("insert", QString("apply")); break; } if (!found) { // add lib - to->libraries.insert(sliding_insert_window, lib); + QJsonObject insertObj; + insertObj.insert("before", to->getFullVersion()->libraries.at(sliding_insert_window)->rawName()); + libObj.insert("insert", insertObj); sliding_insert_window++; } + librariesPlus.append(libObj); } - to->mainClass = m_forge_version->mainClass; - to->minecraftArguments = m_forge_version->minecraftArguments; - to->processArguments = m_forge_version->processArguments; + obj.insert("+libraries", librariesPlus); + obj.insert("mainClass", m_forge_version->mainClass); + obj.insert("minecraftArguments", m_forge_version->minecraftArguments); + obj.insert("processArguments", m_forge_version->processArguments); + } + + QFile file(filename(to->instanceRoot())); + if (!file.open(QFile::WriteOnly)) + { + QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); + return false; } + file.write(QJsonDocument(obj).toJson()); + file.close(); + return true; } diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index 65cad87c..6490b5bf 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -14,17 +14,22 @@ */ #pragma once + +#include "BaseInstaller.h" + #include #include class DerpVersion; -class ForgeInstaller +class ForgeInstaller : public BaseInstaller { public: ForgeInstaller(QString filename, QString universal_url); - bool apply(std::shared_ptr to); + bool add(DerpInstance *to) override; + + QString id() const override { return "net.minecraftforge"; } private: // the version, read from the installer diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index d582f03a..71c7011f 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -27,6 +27,7 @@ QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; LiteLoaderInstaller::LiteLoaderInstaller() + : BaseInstaller() { if (m_launcherWrapperVersionMapping.isEmpty()) { @@ -42,24 +43,11 @@ bool LiteLoaderInstaller::canApply(DerpInstance *instance) const return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId()); } -bool LiteLoaderInstaller::isApplied(DerpInstance *on) -{ - return QFile::exists(filename(on->instanceRoot())); -} - bool LiteLoaderInstaller::add(DerpInstance *to) { - if (!patchesDir(to->instanceRoot()).exists()) - { - QDir(to->instanceRoot()).mkdir("patches"); - } - - if (isApplied(to)) + if (!BaseInstaller::add(to)) { - if (!remove(to)) - { - return false; - } + return false; } QJsonObject obj; @@ -102,17 +90,3 @@ bool LiteLoaderInstaller::add(DerpInstance *to) return true; } - -bool LiteLoaderInstaller::remove(DerpInstance *from) -{ - return QFile::remove(filename(from->instanceRoot())); -} - -QString LiteLoaderInstaller::filename(const QString &root) const -{ - return patchesDir(root).absoluteFilePath(id() + ".json"); -} -QDir LiteLoaderInstaller::patchesDir(const QString &root) const -{ - return QDir(root + "/patches/"); -} diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index 48ef4baf..c128ee22 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -14,30 +14,22 @@ */ #pragma once + +#include "BaseInstaller.h" + #include #include -#include -class DerpVersion; -class DerpInstance; -class QDir; - -// TODO base class -class LiteLoaderInstaller +class LiteLoaderInstaller : public BaseInstaller { public: LiteLoaderInstaller(); - bool canApply(DerpInstance *instance) const; - bool isApplied(DerpInstance *on); - - bool add(DerpInstance *to); - bool remove(DerpInstance *from); + bool canApply(DerpInstance *instance) const override; + bool add(DerpInstance *to) override; private: - virtual QString id() const { return "com.mumfrey.liteloader"; } - QString filename(const QString &root) const; - QDir patchesDir(const QString &root) const; + virtual QString id() const override { return "com.mumfrey.liteloader"; } static QMap m_launcherWrapperVersionMapping; }; -- cgit v1.2.3 From 7d5787025aa5cebf6778f71e79f248f10b8541c9 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Fri, 24 Jan 2014 18:12:02 +0100 Subject: Change naming from Derp -> OneSix until the new instance type supports legacy --- logic/BaseInstaller.cpp | 12 +- logic/BaseInstaller.h | 10 +- logic/BaseInstance.h | 2 +- logic/DerpFTBInstance.cpp | 123 ---------- logic/DerpFTBInstance.h | 22 -- logic/DerpInstance.cpp | 388 ------------------------------ logic/DerpInstance.h | 75 ------ logic/DerpInstance_p.h | 27 --- logic/DerpLibrary.cpp | 268 --------------------- logic/DerpLibrary.h | 132 ----------- logic/DerpRule.cpp | 89 ------- logic/DerpRule.h | 98 -------- logic/DerpUpdate.cpp | 372 ----------------------------- logic/DerpUpdate.h | 63 ----- logic/DerpVersion.cpp | 194 --------------- logic/DerpVersion.h | 115 --------- logic/DerpVersionBuilder.cpp | 446 ----------------------------------- logic/DerpVersionBuilder.h | 53 ----- logic/ForgeInstaller.cpp | 14 +- logic/ForgeInstaller.h | 6 +- logic/InstanceFactory.cpp | 32 +-- logic/LegacyInstance.h | 2 +- logic/LiteLoaderInstaller.cpp | 14 +- logic/LiteLoaderInstaller.h | 4 +- logic/MinecraftVersion.h | 6 +- logic/NostalgiaInstance.cpp | 2 +- logic/NostalgiaInstance.h | 4 +- logic/OneSixFTBInstance.cpp | 123 ++++++++++ logic/OneSixFTBInstance.h | 22 ++ logic/OneSixInstance.cpp | 388 ++++++++++++++++++++++++++++++ logic/OneSixInstance.h | 75 ++++++ logic/OneSixInstance_p.h | 27 +++ logic/OneSixLibrary.cpp | 268 +++++++++++++++++++++ logic/OneSixLibrary.h | 132 +++++++++++ logic/OneSixRule.cpp | 89 +++++++ logic/OneSixRule.h | 98 ++++++++ logic/OneSixUpdate.cpp | 372 +++++++++++++++++++++++++++++ logic/OneSixUpdate.h | 63 +++++ logic/OneSixVersion.cpp | 292 ++++++----------------- logic/OneSixVersion.h | 45 ++-- logic/OneSixVersionBuilder.cpp | 446 +++++++++++++++++++++++++++++++++++ logic/OneSixVersionBuilder.h | 53 +++++ logic/lists/MinecraftVersionList.cpp | 6 +- 43 files changed, 2313 insertions(+), 2759 deletions(-) delete mode 100644 logic/DerpFTBInstance.cpp delete mode 100644 logic/DerpFTBInstance.h delete mode 100644 logic/DerpInstance.cpp delete mode 100644 logic/DerpInstance.h delete mode 100644 logic/DerpInstance_p.h delete mode 100644 logic/DerpLibrary.cpp delete mode 100644 logic/DerpLibrary.h delete mode 100644 logic/DerpRule.cpp delete mode 100644 logic/DerpRule.h delete mode 100644 logic/DerpUpdate.cpp delete mode 100644 logic/DerpUpdate.h delete mode 100644 logic/DerpVersion.cpp delete mode 100644 logic/DerpVersion.h delete mode 100644 logic/DerpVersionBuilder.cpp delete mode 100644 logic/DerpVersionBuilder.h create mode 100644 logic/OneSixFTBInstance.cpp create mode 100644 logic/OneSixFTBInstance.h create mode 100644 logic/OneSixInstance.cpp create mode 100644 logic/OneSixInstance.h create mode 100644 logic/OneSixInstance_p.h create mode 100644 logic/OneSixLibrary.cpp create mode 100644 logic/OneSixLibrary.h create mode 100644 logic/OneSixRule.cpp create mode 100644 logic/OneSixRule.h create mode 100644 logic/OneSixUpdate.cpp create mode 100644 logic/OneSixUpdate.h create mode 100644 logic/OneSixVersionBuilder.cpp create mode 100644 logic/OneSixVersionBuilder.h (limited to 'logic') diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp index 74a8e909..92aa0c92 100644 --- a/logic/BaseInstaller.cpp +++ b/logic/BaseInstaller.cpp @@ -17,9 +17,9 @@ #include -#include "DerpVersion.h" -#include "DerpLibrary.h" -#include "DerpInstance.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "OneSixInstance.h" #include "cmdutils.h" @@ -28,12 +28,12 @@ BaseInstaller::BaseInstaller() } -bool BaseInstaller::isApplied(DerpInstance *on) +bool BaseInstaller::isApplied(OneSixInstance *on) { return QFile::exists(filename(on->instanceRoot())); } -bool BaseInstaller::add(DerpInstance *to) +bool BaseInstaller::add(OneSixInstance *to) { if (!patchesDir(to->instanceRoot()).exists()) { @@ -51,7 +51,7 @@ bool BaseInstaller::add(DerpInstance *to) return true; } -bool BaseInstaller::remove(DerpInstance *from) +bool BaseInstaller::remove(OneSixInstance *from) { return QFile::remove(filename(from->instanceRoot())); } diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h index 08f78bfc..df7eab89 100644 --- a/logic/BaseInstaller.h +++ b/logic/BaseInstaller.h @@ -17,7 +17,7 @@ #include -class DerpInstance; +class OneSixInstance; class QDir; class QString; @@ -26,11 +26,11 @@ class BaseInstaller public: BaseInstaller(); - virtual bool canApply(DerpInstance *instance) const { return true; } - bool isApplied(DerpInstance *on); + virtual bool canApply(OneSixInstance *instance) const { return true; } + bool isApplied(OneSixInstance *on); - virtual bool add(DerpInstance *to); - virtual bool remove(DerpInstance *from); + virtual bool add(OneSixInstance *to); + virtual bool remove(OneSixInstance *from); protected: virtual QString id() const = 0; diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index 79640c84..a861e9b2 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -27,7 +27,7 @@ class QDialog; class Task; class MinecraftProcess; -class DerpUpdate; +class OneSixUpdate; class InstanceList; class BaseInstancePrivate; diff --git a/logic/DerpFTBInstance.cpp b/logic/DerpFTBInstance.cpp deleted file mode 100644 index 9b687461..00000000 --- a/logic/DerpFTBInstance.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "DerpFTBInstance.h" - -#include "DerpVersion.h" -#include "DerpLibrary.h" -#include "tasks/SequentialTask.h" -#include "ForgeInstaller.h" -#include "lists/ForgeVersionList.h" -#include "MultiMC.h" - -class DerpFTBInstanceForge : public Task -{ - Q_OBJECT -public: - explicit DerpFTBInstanceForge(const QString &version, DerpFTBInstance *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) - { - emitFailed(QString("Couldn't find forge version ") + version ); - 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, &DerpFTBInstanceForge::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; - } - auto version = instance->getFullVersion(); - if (!forge.add(instance)) - { - emitFailed(tr("Couldn't install Forge")); - return; - } - emitSucceeded(); - } - -private: - DerpFTBInstance *instance; - QString version; - ForgeVersionPtr forgeVersion; - MetaEntryPtr entry; - NetJob *fjob; -}; - -DerpFTBInstance::DerpFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : - DerpInstance(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 DerpLibrary(match.captured())); - m_forge->finalize(); - } -} - -QString DerpFTBInstance::id() const -{ - return "FTB/" + BaseInstance::id(); -} - -QString DerpFTBInstance::getStatusbarDescription() -{ - return "Derp FTB: " + intendedVersionId(); -} -bool DerpFTBInstance::menuActionEnabled(QString action_name) const -{ - return false; -} - -std::shared_ptr DerpFTBInstance::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(DerpInstance::doUpdate(only_prepare)); - task->addTask(std::shared_ptr(new DerpFTBInstanceForge(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(DerpInstance::doUpdate(only_prepare)); - return task; -} - -#include "DerpFTBInstance.moc" diff --git a/logic/DerpFTBInstance.h b/logic/DerpFTBInstance.h deleted file mode 100644 index c16998bf..00000000 --- a/logic/DerpFTBInstance.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "DerpInstance.h" - -class DerpLibrary; - -class DerpFTBInstance : public DerpInstance -{ - Q_OBJECT -public: - explicit DerpFTBInstance(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; - - virtual QString id() const; - -private: - std::shared_ptr m_forge; -}; diff --git a/logic/DerpInstance.cpp b/logic/DerpInstance.cpp deleted file mode 100644 index e6b92b1f..00000000 --- a/logic/DerpInstance.cpp +++ /dev/null @@ -1,388 +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 "DerpInstance.h" - -#include - -#include "DerpInstance_p.h" -#include "DerpUpdate.h" -#include "DerpVersion.h" -#include "pathutils.h" -#include "logger/QsLog.h" -#include "assets/AssetsUtils.h" -#include "MultiMC.h" -#include "icons/IconList.h" -#include "MinecraftProcess.h" -#include "gui/dialogs/DerpModEditDialog.h" - -DerpInstance::DerpInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) - : BaseInstance(new DerpInstancePrivate(), rootDir, settings, parent) -{ - I_D(DerpInstance); - d->m_settings->registerSetting("IntendedVersion", ""); - d->m_settings->registerSetting("ShouldUpdate", false); - d->version.reset(new DerpVersion(this, this)); - if (QDir(instanceRoot()).exists("version.json")) - { - reloadFullVersion(); - } - else - { - clearFullVersion(); - } -} - -std::shared_ptr DerpInstance::doUpdate(bool only_prepare) -{ - return std::shared_ptr(new DerpUpdate(this, only_prepare)); -} - -QString replaceTokensIn(QString text, QMap with) -{ - QString result; - QRegExp token_regexp("\\$\\{(.+)\\}"); - token_regexp.setMinimal(true); - QStringList list; - int tail = 0; - int head = 0; - while ((head = token_regexp.indexIn(text, head)) != -1) - { - result.append(text.mid(tail, head - tail)); - QString key = token_regexp.cap(1); - auto iter = with.find(key); - if (iter != with.end()) - { - result.append(*iter); - } - head += token_regexp.matchedLength(); - tail = head; - } - result.append(text.mid(tail)); - return result; -} - -QDir DerpInstance::reconstructAssets(std::shared_ptr version) -{ - QDir assetsDir = QDir("assets/"); - QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); - QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects")); - QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual")); - - QString indexPath = PathCombine(indexDir.path(), version->assets + ".json"); - QFile indexFile(indexPath); - QDir virtualRoot(PathCombine(virtualDir.path(), version->assets)); - - if (!indexFile.exists()) - { - QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets"; - return virtualRoot; - } - - QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path() - << objectDir.path() << virtualDir.path() << virtualRoot.path(); - - AssetsIndex index; - bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index); - - if (loadAssetsIndex && index.isVirtual) - { - QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path(); - - for (QString map : index.objects.keys()) - { - AssetObject asset_object = index.objects.value(map); - QString target_path = PathCombine(virtualRoot.path(), map); - QFile target(target_path); - - QString tlk = asset_object.hash.left(2); - - QString original_path = - PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash); - QFile original(original_path); - if(!original.exists()) - continue; - if (!target.exists()) - { - QFileInfo info(target_path); - QDir target_dir = info.dir(); - // QLOG_DEBUG() << target_dir; - if (!target_dir.exists()) - QDir("").mkpath(target_dir.path()); - - bool couldCopy = original.copy(target_path); - QLOG_DEBUG() << " Copying" << original_path << "to" << target_path - << QString::number(couldCopy); // << original.errorString(); - } - } - - // TODO: Write last used time to virtualRoot/.lastused - } - - return virtualRoot; -} - -QStringList DerpInstance::processMinecraftArgs(MojangAccountPtr account) -{ - I_D(DerpInstance); - auto version = d->version; - QString args_pattern = version->minecraftArguments; - - QMap token_mapping; - // yggdrasil! - token_mapping["auth_username"] = account->username(); - token_mapping["auth_session"] = account->sessionId(); - token_mapping["auth_access_token"] = account->accessToken(); - token_mapping["auth_player_name"] = account->currentProfile()->name; - token_mapping["auth_uuid"] = account->currentProfile()->id; - - // this is for offline?: - /* - map["auth_player_name"] = "Player"; - map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; - */ - - // these do nothing and are stupid. - token_mapping["profile_name"] = name(); - token_mapping["version_name"] = version->id; - - QString absRootDir = QDir(minecraftRoot()).absolutePath(); - token_mapping["game_directory"] = absRootDir; - QString absAssetsDir = QDir("assets/").absolutePath(); - token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath(); - - auto user = account->user(); - QJsonObject userAttrs; - for (auto key : user.properties.keys()) - { - auto array = QJsonArray::fromStringList(user.properties.values(key)); - userAttrs.insert(key, array); - } - QJsonDocument value(userAttrs); - - token_mapping["user_properties"] = value.toJson(QJsonDocument::Compact); - token_mapping["user_type"] = account->currentProfile()->legacy ? "legacy" : "mojang"; - // 1.7.3+ assets tokens - token_mapping["assets_root"] = absAssetsDir; - token_mapping["assets_index_name"] = version->assets; - - QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); - for (int i = 0; i < parts.length(); i++) - { - parts[i] = replaceTokensIn(parts[i], token_mapping); - } - return parts; -} - -MinecraftProcess *DerpInstance::prepareForLaunch(MojangAccountPtr account) -{ - I_D(DerpInstance); - - QIcon icon = MMC->icons()->getIcon(iconKey()); - auto pixmap = icon.pixmap(128, 128); - pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); - - auto version = d->version; - if (!version) - return nullptr; - QString launchScript; - { - auto libs = version->getActiveNormalLibs(); - for (auto lib : libs) - { - QFileInfo fi(QString("libraries/") + lib->storagePath()); - launchScript += "cp " + fi.absoluteFilePath() + "\n"; - } - QString targetstr = "versions/" + version->id + "/" + version->id + ".jar"; - QFileInfo fi(targetstr); - launchScript += "cp " + fi.absoluteFilePath() + "\n"; - } - launchScript += "mainClass " + version->mainClass + "\n"; - - for (auto param : processMinecraftArgs(account)) - { - launchScript += "param " + param + "\n"; - } - - // Set the width and height for 1.6 instances - bool maximize = settings().get("LaunchMaximized").toBool(); - if (maximize) - { - // this is probably a BAD idea - // launchScript += "param --fullscreen\n"; - } - else - { - launchScript += - "param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n"; - launchScript += - "param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n"; - } - QDir natives_dir(PathCombine(instanceRoot(), "natives/")); - launchScript += "windowTitle " + windowTitle() + "\n"; - for(auto native: version->getActiveNativeLibs()) - { - QFileInfo finfo(PathCombine("libraries", native->storagePath())); - launchScript += "ext " + finfo.absoluteFilePath() + "\n"; - } - launchScript += "natives " + natives_dir.absolutePath() + "\n"; - launchScript += "launch onesix\n"; - - // create the process and set its parameters - MinecraftProcess *proc = new MinecraftProcess(this); - proc->setWorkdir(minecraftRoot()); - proc->setLaunchScript(launchScript); - // proc->setNativeFolder(natives_dir.absolutePath()); - return proc; -} - -void DerpInstance::cleanupAfterRun() -{ - QString target_dir = PathCombine(instanceRoot(), "natives/"); - QDir dir(target_dir); - dir.removeRecursively(); -} - -std::shared_ptr DerpInstance::loaderModList() -{ - I_D(DerpInstance); - if (!d->loader_mod_list) - { - d->loader_mod_list.reset(new ModList(loaderModsDir())); - } - d->loader_mod_list->update(); - return d->loader_mod_list; -} - -std::shared_ptr DerpInstance::resourcePackList() -{ - I_D(DerpInstance); - if (!d->resource_pack_list) - { - d->resource_pack_list.reset(new ModList(resourcePacksDir())); - } - d->resource_pack_list->update(); - return d->resource_pack_list; -} - -QDialog *DerpInstance::createModEditDialog(QWidget *parent) -{ - return new DerpModEditDialog(this, parent); -} - -bool DerpInstance::setIntendedVersionId(QString version) -{ - settings().set("IntendedVersion", version); - setShouldUpdate(true); - QFile::remove(PathCombine(instanceRoot(), "version.json")); - clearFullVersion(); - return true; -} - -QString DerpInstance::intendedVersionId() const -{ - return settings().get("IntendedVersion").toString(); -} - -void DerpInstance::setShouldUpdate(bool val) -{ - settings().set("ShouldUpdate", val); -} - -bool DerpInstance::shouldUpdate() const -{ - QVariant var = settings().get("ShouldUpdate"); - if (!var.isValid() || var.toBool() == false) - { - return intendedVersionId() != currentVersionId(); - } - return true; -} - -bool DerpInstance::versionIsCustom() -{ - QDir patches(PathCombine(instanceRoot(), "patches/")); - return QFile::exists(PathCombine(instanceRoot(), "custom.json")) - || (patches.exists() && patches.count() >= 0); -} - -QString DerpInstance::currentVersionId() const -{ - return intendedVersionId(); -} - -bool DerpInstance::reloadFullVersion(QWidget *widgetParent) -{ - I_D(DerpInstance); - - bool ret = d->version->reload(widgetParent); - emit versionReloaded(); - return ret; -} - -void DerpInstance::clearFullVersion() -{ - I_D(DerpInstance); - d->version->clear(); - emit versionReloaded(); -} - -std::shared_ptr DerpInstance::getFullVersion() -{ - I_D(DerpInstance); - return d->version; -} - -QString DerpInstance::defaultBaseJar() const -{ - return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; -} - -QString DerpInstance::defaultCustomBaseJar() const -{ - return PathCombine(instanceRoot(), "custom.jar"); -} - -bool DerpInstance::menuActionEnabled(QString action_name) const -{ - if (action_name == "actionChangeInstLWJGLVersion") - return false; - return true; -} - -QString DerpInstance::getStatusbarDescription() -{ - QString descr = "Derp : " + intendedVersionId(); - if (versionIsCustom()) - { - descr + " (custom)"; - } - return descr; -} - -QString DerpInstance::loaderModsDir() const -{ - return PathCombine(minecraftRoot(), "mods"); -} - -QString DerpInstance::resourcePacksDir() const -{ - return PathCombine(minecraftRoot(), "resourcepacks"); -} - -QString DerpInstance::instanceConfigFolder() const -{ - return PathCombine(minecraftRoot(), "config"); -} diff --git a/logic/DerpInstance.h b/logic/DerpInstance.h deleted file mode 100644 index 46f953c0..00000000 --- a/logic/DerpInstance.h +++ /dev/null @@ -1,75 +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 "BaseInstance.h" - -#include "DerpVersion.h" -#include "ModList.h" - -class DerpInstance : public BaseInstance -{ - Q_OBJECT -public: - explicit DerpInstance(const QString &rootDir, SettingsObject *settings, - QObject *parent = 0); - - ////// Mod Lists ////// - std::shared_ptr loaderModList(); - std::shared_ptr resourcePackList(); - - ////// Directories ////// - QString resourcePacksDir() const; - QString loaderModsDir() const; - virtual QString instanceConfigFolder() const override; - - virtual std::shared_ptr doUpdate(bool only_prepare) override; - virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; - - virtual void cleanupAfterRun() override; - - virtual QString intendedVersionId() const override; - virtual bool setIntendedVersionId(QString version) override; - - virtual QString currentVersionId() const override; - - virtual bool shouldUpdate() const override; - virtual void setShouldUpdate(bool val) override; - - virtual QDialog *createModEditDialog(QWidget *parent) override; - - /// reload the full version json files. return true on success! - bool reloadFullVersion(QWidget *widgetParent = 0); - /// clears all version information in preparation for an update - void clearFullVersion(); - /// get the current full version info - std::shared_ptr getFullVersion(); - /// is the current version original, or custom? - virtual bool versionIsCustom() override; - - virtual QString defaultBaseJar() const override; - virtual QString defaultCustomBaseJar() const override; - - virtual bool menuActionEnabled(QString action_name) const override; - virtual QString getStatusbarDescription() override; - -signals: - void versionReloaded(); - -private: - QStringList processMinecraftArgs(MojangAccountPtr account); - QDir reconstructAssets(std::shared_ptr version); -}; diff --git a/logic/DerpInstance_p.h b/logic/DerpInstance_p.h deleted file mode 100644 index 41f7b62d..00000000 --- a/logic/DerpInstance_p.h +++ /dev/null @@ -1,27 +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 "BaseInstance_p.h" -#include "DerpVersion.h" -#include "ModList.h" - -struct DerpInstancePrivate : public BaseInstancePrivate -{ - std::shared_ptr version; - std::shared_ptr loader_mod_list; - std::shared_ptr resource_pack_list; -}; diff --git a/logic/DerpLibrary.cpp b/logic/DerpLibrary.cpp deleted file mode 100644 index ba4d516b..00000000 --- a/logic/DerpLibrary.cpp +++ /dev/null @@ -1,268 +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 - -#include "DerpLibrary.h" -#include "DerpRule.h" -#include "OpSys.h" -#include "logic/net/URLConstants.h" -#include -#include -#include "logger/QsLog.h" - -void DerpLibrary::finalize() -{ - QStringList parts = m_name.split(':'); - QString relative = parts[0]; - relative.replace('.', '/'); - relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; - - if (!m_is_native) - relative += ".jar"; - else - { - if (m_native_suffixes.contains(currentSystem)) - { - relative += "-" + m_native_suffixes[currentSystem] + ".jar"; - } - else - { - // really, bad. - relative += ".jar"; - } - } - - m_decentname = parts[1]; - m_decentversion = parts[2]; - m_storage_path = relative; - m_download_url = m_base_url + relative; - - if (m_rules.empty()) - { - m_is_active = true; - } - else - { - RuleAction result = Disallow; - for (auto rule : m_rules) - { - RuleAction temp = rule->apply(this); - if (temp != Defer) - result = temp; - } - m_is_active = (result == Allow); - } - if (m_is_native) - { - m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); - m_decenttype = "Native"; - } - else - { - m_decenttype = "Java"; - } -} - -void DerpLibrary::setName(const QString &name) -{ - m_name = name; -} -void DerpLibrary::setBaseUrl(const QString &base_url) -{ - m_base_url = base_url; -} -void DerpLibrary::setIsNative() -{ - m_is_native = true; -} -void DerpLibrary::addNative(OpSys os, const QString &suffix) -{ - m_is_native = true; - m_native_suffixes[os] = suffix; -} -void DerpLibrary::setRules(QList> rules) -{ - m_rules = rules; -} -bool DerpLibrary::isActive() const -{ - return m_is_active; -} -bool DerpLibrary::isNative() const -{ - return m_is_native; -} -QString DerpLibrary::downloadUrl() const -{ - if (m_absolute_url.size()) - return m_absolute_url; - return m_download_url; -} -QString DerpLibrary::storagePath() const -{ - return m_storage_path; -} - -void DerpLibrary::setAbsoluteUrl(const QString &absolute_url) -{ - m_absolute_url = absolute_url; -} - -QString DerpLibrary::absoluteUrl() const -{ - return m_absolute_url; -} - -void DerpLibrary::setHint(const QString &hint) -{ - m_hint = hint; -} - -QString DerpLibrary::hint() const -{ - return m_hint; -} - -bool DerpLibrary::filesExist() -{ - QString storage = storagePath(); - if (storage.contains("${arch}")) - { - QString cooked_storage = storage; - cooked_storage.replace("${arch}", "32"); - QFileInfo info32(PathCombine("libraries", cooked_storage)); - if (!info32.exists()) - { - return false; - } - cooked_storage = storage; - cooked_storage.replace("${arch}", "64"); - QFileInfo info64(PathCombine("libraries", cooked_storage)); - if (!info64.exists()) - { - return false; - } - } - else - { - QFileInfo info(PathCombine("libraries", storage)); - if (!info.exists()) - { - return false; - } - } - return true; -} - -bool DerpLibrary::extractTo(QString target_dir) -{ - QString storage = storagePath(); - if (storage.contains("${arch}")) - { - QString cooked_storage = storage; - cooked_storage.replace("${arch}", "32"); - QString origin = PathCombine("libraries", cooked_storage); - QString target_dir_cooked = PathCombine(target_dir, "32"); - if(!ensureFolderPathExists(target_dir_cooked)) - { - QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; - return false; - } - if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) - .isEmpty()) - { - QLOG_ERROR() << "Couldn't extract " + origin; - return false; - } - cooked_storage = storage; - cooked_storage.replace("${arch}", "64"); - origin = PathCombine("libraries", cooked_storage); - target_dir_cooked = PathCombine(target_dir, "64"); - if(!ensureFolderPathExists(target_dir_cooked)) - { - QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; - return false; - } - if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) - .isEmpty()) - { - QLOG_ERROR() << "Couldn't extract " + origin; - return false; - } - } - else - { - if(!ensureFolderPathExists(target_dir)) - { - QLOG_ERROR() << "Couldn't create folder " + target_dir; - return false; - } - QString path = PathCombine("libraries", storage); - if (JlCompress::extractWithExceptions(path, target_dir, extract_excludes).isEmpty()) - { - QLOG_ERROR() << "Couldn't extract " + path; - return false; - } - } - return true; -} - -QJsonObject DerpLibrary::toJson() -{ - QJsonObject libRoot; - libRoot.insert("name", m_name); - if (m_absolute_url.size()) - libRoot.insert("MMC-absoluteUrl", m_absolute_url); - if (m_hint.size()) - libRoot.insert("MMC-hint", m_hint); - if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - m_base_url != "https://" + URLConstants::LIBRARY_BASE) - libRoot.insert("url", m_base_url); - if (isNative() && m_native_suffixes.size()) - { - QJsonObject nativeList; - auto iter = m_native_suffixes.begin(); - while (iter != m_native_suffixes.end()) - { - nativeList.insert(OpSys_toString(iter.key()), iter.value()); - iter++; - } - libRoot.insert("natives", nativeList); - } - if (isNative() && extract_excludes.size()) - { - QJsonArray excludes; - QJsonObject extract; - for (auto exclude : extract_excludes) - { - excludes.append(exclude); - } - extract.insert("exclude", excludes); - libRoot.insert("extract", extract); - } - if (m_rules.size()) - { - QJsonArray allRules; - for (auto &rule : m_rules) - { - QJsonObject ruleObj = rule->toJson(); - allRules.append(ruleObj); - } - libRoot.insert("rules", allRules); - } - return libRoot; -} diff --git a/logic/DerpLibrary.h b/logic/DerpLibrary.h deleted file mode 100644 index d1cee843..00000000 --- a/logic/DerpLibrary.h +++ /dev/null @@ -1,132 +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 -#include -#include - -#include "logic/net/URLConstants.h" -#include "OpSys.h" - -class Rule; - -class DerpLibrary -{ -private: - // basic values used internally (so far) - QString m_name; - QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; - QList> m_rules; - - // custom values - /// absolute URL. takes precedence over m_download_path, if defined - QString m_absolute_url; - /// download hint - how to actually get the library - QString m_hint; - - // derived values used for real things - /// a decent name fit for display - QString m_decentname; - /// a decent version fit for display - QString m_decentversion; - /// a decent type fit for display - QString m_decenttype; - /// where to store the lib locally - QString m_storage_path; - /// where to download the lib from - QString m_download_url; - /// is this lib actually active on the current OS? - bool m_is_active = false; - /// is the library a native? - bool m_is_native = false; - /// native suffixes per OS - QMap m_native_suffixes; - -public: - QStringList extract_excludes; - -public: - /// Constructor - DerpLibrary(const QString &name) - { - m_name = name; - } - - /// Returns the raw name field - QString rawName() const - { - return m_name; - } - - QJsonObject toJson(); - - /** - * finalize the library, processing the input values into derived values and state - * - * This SHALL be called after all the values are parsed or after any further change. - */ - void finalize(); - - /// Set the library composite name - void setName(const QString &name); - /// get a decent-looking name - QString name() const - { - return m_decentname; - } - /// get a decent-looking version - QString version() const - { - return m_decentversion; - } - /// what kind of library is it? (for display) - QString type() const - { - return m_decenttype; - } - /// Set the url base for downloads - void setBaseUrl(const QString &base_url); - - /// Call this to mark the library as 'native' (it's a zip archive with DLLs) - void setIsNative(); - /// Attach a name suffix to the specified OS native - void addNative(OpSys os, const QString &suffix); - /// Set the load rules - void setRules(QList> rules); - - /// Returns true if the library should be loaded (or extracted, in case of natives) - bool isActive() const; - /// Returns true if the library is native - bool isNative() const; - /// Get the URL to download the library from - QString downloadUrl() const; - /// Get the relative path where the library should be saved - QString storagePath() const; - - /// set an absolute URL for the library. This is an MMC extension. - void setAbsoluteUrl(const QString &absolute_url); - QString absoluteUrl() const; - - /// set a hint about how to treat the library. This is an MMC extension. - void setHint(const QString &hint); - QString hint() const; - - bool extractTo(QString target_dir); - bool filesExist(); -}; diff --git a/logic/DerpRule.cpp b/logic/DerpRule.cpp deleted file mode 100644 index 5ed200e3..00000000 --- a/logic/DerpRule.cpp +++ /dev/null @@ -1,89 +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 -#include - -#include "DerpRule.h" - -QList> rulesFromJsonV4(const QJsonObject &objectWithRules) -{ - QList> rules; - auto rulesVal = objectWithRules.value("rules"); - if (!rulesVal.isArray()) - return rules; - - QJsonArray ruleList = rulesVal.toArray(); - for (auto ruleVal : ruleList) - { - std::shared_ptr rule; - if (!ruleVal.isObject()) - continue; - auto ruleObj = ruleVal.toObject(); - auto actionVal = ruleObj.value("action"); - if (!actionVal.isString()) - continue; - auto action = RuleAction_fromString(actionVal.toString()); - if (action == Defer) - continue; - - auto osVal = ruleObj.value("os"); - if (!osVal.isObject()) - { - // add a new implicit action rule - rules.append(ImplicitRule::create(action)); - continue; - } - - auto osObj = osVal.toObject(); - auto osNameVal = osObj.value("name"); - if (!osNameVal.isString()) - continue; - OpSys requiredOs = OpSys_fromString(osNameVal.toString()); - QString versionRegex = osObj.value("version").toString(); - // add a new OS rule - rules.append(OsRule::create(action, requiredOs, versionRegex)); - } - return rules; -} - -QJsonObject ImplicitRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - return ruleObj; -} - -QJsonObject OsRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - QJsonObject osObj; - { - osObj.insert("name", OpSys_toString(m_system)); - osObj.insert("version", m_version_regexp); - } - ruleObj.insert("os", osObj); - return ruleObj; -} - -RuleAction RuleAction_fromString(QString name) -{ - if (name == "allow") - return Allow; - if (name == "disallow") - return Disallow; - return Defer; -} diff --git a/logic/DerpRule.h b/logic/DerpRule.h deleted file mode 100644 index 0254028c..00000000 --- a/logic/DerpRule.h +++ /dev/null @@ -1,98 +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 "logic/DerpLibrary.h" - -enum RuleAction -{ - Allow, - Disallow, - Defer -}; - -RuleAction RuleAction_fromString(QString); -QList> rulesFromJsonV4(const QJsonObject &objectWithRules); - -class Rule -{ -protected: - RuleAction m_result; - virtual bool applies(DerpLibrary *parent) = 0; - -public: - Rule(RuleAction result) : m_result(result) - { - } - virtual ~Rule() {}; - virtual QJsonObject toJson() = 0; - RuleAction apply(DerpLibrary *parent) - { - if (applies(parent)) - return m_result; - else - return Defer; - } - ; -}; - -class OsRule : public Rule -{ -private: - // the OS - OpSys m_system; - // the OS version regexp - QString m_version_regexp; - -protected: - virtual bool applies(DerpLibrary *) - { - return (m_system == currentSystem); - } - OsRule(RuleAction result, OpSys system, QString version_regexp) - : Rule(result), m_system(system), m_version_regexp(version_regexp) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result, OpSys system, - QString version_regexp) - { - return std::shared_ptr(new OsRule(result, system, version_regexp)); - } -}; - -class ImplicitRule : public Rule -{ -protected: - virtual bool applies(DerpLibrary *) - { - return true; - } - ImplicitRule(RuleAction result) : Rule(result) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result) - { - return std::shared_ptr(new ImplicitRule(result)); - } -}; diff --git a/logic/DerpUpdate.cpp b/logic/DerpUpdate.cpp deleted file mode 100644 index 5686e4ac..00000000 --- a/logic/DerpUpdate.cpp +++ /dev/null @@ -1,372 +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 "MultiMC.h" -#include "DerpUpdate.h" - -#include - -#include -#include -#include -#include - -#include "BaseInstance.h" -#include "lists/MinecraftVersionList.h" -#include "DerpVersion.h" -#include "DerpLibrary.h" -#include "DerpInstance.h" -#include "net/ForgeMirrors.h" -#include "net/URLConstants.h" -#include "assets/AssetsUtils.h" - -#include "pathutils.h" -#include - -DerpUpdate::DerpUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) - : Task(parent), m_inst(inst), m_only_prepare(only_prepare) -{ -} - -void DerpUpdate::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; - } - - if (m_only_prepare) - { - prepareForLaunch(); - return; - } - - if (m_inst->shouldUpdate()) - { - // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = std::dynamic_pointer_cast( - MMC->minecraftlist()->findVersion(intendedVersion)); - if (targetVersion == nullptr) - { - // don't do anything if it was invalid - emitFailed("The specified Minecraft version is invalid. Choose a different one."); - return; - } - versionFileStart(); - } - else - { - jarlibStart(); - } -} - -void DerpUpdate::versionFileStart() -{ - QLOG_INFO() << m_inst->name() << ": getting version file."; - 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"); - job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); - specificVersionDownloadJob.reset(job); - connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished())); - connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); - specificVersionDownloadJob->start(); -} - -void DerpUpdate::versionFileFinished() -{ - NetActionPtr DlJob = specificVersionDownloadJob->first(); - DerpInstance *inst = (DerpInstance *)m_inst; - - QString version_id = targetVersion->descriptor(); - QString inst_dir = m_inst->instanceRoot(); - // save the version file in $instanceId/version.json - { - QString version1 = PathCombine(inst_dir, "/version.json"); - ensureFilePathExists(version1); - // FIXME: detect errors here, download to a temp file, swap - QSaveFile vfile1(version1); - if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) - { - emitFailed("Can't open " + version1 + " for writing."); - return; - } - auto data = std::dynamic_pointer_cast(DlJob)->m_data; - qint64 actual = 0; - if ((actual = vfile1.write(data)) != data.size()) - { - emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " + - data.size() + '.'); - return; - } - if (!vfile1.commit()) - { - emitFailed("Can't commit changes to " + version1); - return; - } - } - - // the version is downloaded safely. update is 'done' at this point - m_inst->setShouldUpdate(false); - - // delete any custom version inside the instance (it's no longer relevant, we did an update) - QString custom = PathCombine(inst_dir, "/custom.json"); - QFile finfo(custom); - if (finfo.exists()) - { - finfo.remove(); - } - inst->reloadFullVersion(); - - jarlibStart(); -} - -void DerpUpdate::versionFileFailed() -{ - emitFailed("Failed to download the version description. Try again."); -} - -void DerpUpdate::assetIndexStart() -{ - setStatus(tr("Updating assets index...")); - DerpInstance *inst = (DerpInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); - QString assetName = version->assets; - QUrl indexUrl = "http://" + URLConstants::AWS_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 DerpUpdate::assetIndexFinished() -{ - AssetsIndex index; - - DerpInstance *inst = (DerpInstance *)m_inst; - std::shared_ptr 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 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://" + URLConstants::RESOURCE_BASE + objectName), - objectFile.filePath()); - objectDL->m_total_progress = object.size; - dls.append(objectDL); - } - } - if (dls.size()) - { - setStatus(tr("Getting the assets files from Mojang...")); - 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 DerpUpdate::assetIndexFailed() -{ - emitFailed("Failed to download the assets index!"); -} - -void DerpUpdate::assetsFinished() -{ - prepareForLaunch(); -} - -void DerpUpdate::assetsFailed() -{ - emitFailed("Failed to download assets!"); -} - -void DerpUpdate::jarlibStart() -{ - setStatus(tr("Getting the library files from Mojang...")); - QLOG_INFO() << m_inst->name() << ": downloading libraries"; - DerpInstance *inst = (DerpInstance *)m_inst; - bool successful = inst->reloadFullVersion(); - if (!successful) - { - emitFailed("Failed to load the version description file. It might be " - "corrupted, missing or simply too new."); - return; - } - - // Build a list of URLs that will need to be downloaded. - std::shared_ptr version = inst->getFullVersion(); - // minecraft.jar for this version - { - QString version_id = version->id; - QString localPath = version_id + "/" + version_id + ".jar"; - QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; - - auto job = new NetJob("Libraries for instance " + inst->name()); - - auto metacache = MMC->metacache(); - auto entry = metacache->resolveEntry("versions", localPath); - job->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); - - jarlibDownloadJob.reset(job); - } - - auto libs = version->getActiveNativeLibs(); - libs.append(version->getActiveNormalLibs()); - - auto metacache = MMC->metacache(); - QList ForgeLibs; - for (auto lib : libs) - { - if (lib->hint() == "local") - continue; - - QString raw_storage = lib->storagePath(); - QString raw_dl = lib->downloadUrl(); - - auto f = [&](QString storage, QString dl) - { - auto entry = metacache->resolveEntry("libraries", storage); - if (entry->stale) - { - if (lib->hint() == "forge-pack-xz") - { - ForgeLibs.append(ForgeXzDownload::make(storage, entry)); - } - else - { - jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); - } - } - }; - if (raw_storage.contains("${arch}")) - { - QString cooked_storage = raw_storage; - QString cooked_dl = raw_dl; - f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); - cooked_storage = raw_storage; - cooked_dl = raw_dl; - f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); - } - else - { - f(raw_storage, raw_dl); - } - } - // TODO: think about how to propagate this from the original json file... or IF AT ALL - QString forgeMirrorList = "http://files.minecraftforge.net/mirror-brand.list"; - if (!ForgeLibs.empty()) - { - jarlibDownloadJob->addNetAction( - ForgeMirrors::make(ForgeLibs, jarlibDownloadJob, forgeMirrorList)); - } - - connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); - connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed())); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); - - jarlibDownloadJob->start(); -} - -void DerpUpdate::jarlibFinished() -{ - assetIndexStart(); -} - -void DerpUpdate::jarlibFailed() -{ - QStringList failed = jarlibDownloadJob->getFailedFiles(); - QString failed_all = failed.join("\n"); - emitFailed("Failed to download the following files:\n" + failed_all + - "\n\nPlease try again."); -} - -void DerpUpdate::prepareForLaunch() -{ - setStatus(tr("Preparing for launch...")); - QLOG_INFO() << m_inst->name() << ": preparing for launch"; - auto derp_inst = (DerpInstance *)m_inst; - - // delete any leftovers, if they are present. - derp_inst->cleanupAfterRun(); - - QString natives_dir_raw = PathCombine(derp_inst->instanceRoot(), "natives/"); - auto version = derp_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(); -} diff --git a/logic/DerpUpdate.h b/logic/DerpUpdate.h deleted file mode 100644 index 475f6c35..00000000 --- a/logic/DerpUpdate.h +++ /dev/null @@ -1,63 +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 - -#include "logic/net/NetJob.h" -#include "logic/tasks/Task.h" - -class MinecraftVersion; -class BaseInstance; - -class DerpUpdate : public Task -{ - Q_OBJECT -public: - explicit DerpUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); - virtual void executeTask(); - -private -slots: - void versionFileStart(); - void versionFileFinished(); - void versionFileFailed(); - - void jarlibStart(); - void jarlibFinished(); - void jarlibFailed(); - - void assetIndexStart(); - void assetIndexFinished(); - void assetIndexFailed(); - - void assetsFinished(); - void assetsFailed(); - - // extract the appropriate libraries - void prepareForLaunch(); - -private: - NetJobPtr specificVersionDownloadJob; - NetJobPtr jarlibDownloadJob; - - // target version, determined during this task - std::shared_ptr targetVersion; - BaseInstance *m_inst = nullptr; - bool m_only_prepare = false; -}; diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp deleted file mode 100644 index f4a8dd97..00000000 --- a/logic/DerpVersion.cpp +++ /dev/null @@ -1,194 +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 "DerpVersion.h" - -#include - -#include "DerpVersionBuilder.h" - -DerpVersion::DerpVersion(DerpInstance *instance, QObject *parent) - : QAbstractListModel(parent), m_instance(instance) -{ - clear(); -} - -bool DerpVersion::reload(QWidget *widgetParent) -{ - return DerpVersionBuilder::build(this, m_instance, widgetParent); -} - -void DerpVersion::clear() -{ - id.clear(); - time.clear(); - releaseTime.clear(); - type.clear(); - assets.clear(); - processArguments.clear(); - minecraftArguments.clear(); - minimumLauncherVersion = 0xDEADBEAF; - mainClass.clear(); - libraries.clear(); -} - -void DerpVersion::dump() const -{ - qDebug().nospace() << "DerpVersion(" - << "\n\tid=" << id - << "\n\ttime=" << time - << "\n\treleaseTime=" << releaseTime - << "\n\ttype=" << type - << "\n\tassets=" << assets - << "\n\tprocessArguments=" << processArguments - << "\n\tminecraftArguments=" << minecraftArguments - << "\n\tminimumLauncherVersion=" << minimumLauncherVersion - << "\n\tmainClass=" << mainClass - << "\n\tlibraries="; - for (auto lib : libraries) - { - qDebug().nospace() << "\n\t\t" << lib.get(); - } - qDebug().nospace() << "\n)"; -} - -QList > DerpVersion::getActiveNormalLibs() -{ - QList > output; - for (auto lib : libraries) - { - if (lib->isActive() && !lib->isNative()) - { - output.append(lib); - } - } - return output; -} - -QList > DerpVersion::getActiveNativeLibs() -{ - QList > output; - for (auto lib : libraries) - { - if (lib->isActive() && lib->isNative()) - { - output.append(lib); - } - } - return output; -} - -std::shared_ptr DerpVersion::fromJson(const QJsonObject &obj) -{ - std::shared_ptr version(new DerpVersion(0)); - if (DerpVersionBuilder::read(version.get(), obj)) - { - return version; - } - return 0; -} - -QVariant DerpVersion::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= libraries.size()) - return QVariant(); - - if (role == Qt::DisplayRole) - { - switch (column) - { - case 0: - return libraries[row]->name(); - case 1: - return libraries[row]->type(); - case 2: - return libraries[row]->version(); - default: - return QVariant(); - } - } - return QVariant(); -} - -Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - int row = index.row(); - if (libraries[row]->isActive()) - { - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; - } - else - { - return Qt::ItemNeverHasChildren; - } - // return QAbstractListModel::flags(index); -} - -QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - switch (section) - { - case 0: - return QString("Name"); - case 1: - return QString("Type"); - case 2: - return QString("Version"); - default: - return QString(); - } -} - -int DerpVersion::rowCount(const QModelIndex &parent) const -{ - return libraries.size(); -} - -int DerpVersion::columnCount(const QModelIndex &parent) const -{ - return 3; -} - -QDebug operator<<(QDebug &dbg, const DerpVersion *version) -{ - version->dump(); - return dbg.maybeSpace(); -} -QDebug operator<<(QDebug &dbg, const DerpLibrary *library) -{ - dbg.nospace() << "DerpLibrary(" - << "\n\t\t\trawName=" << library->rawName() - << "\n\t\t\tname=" << library->name() - << "\n\t\t\tversion=" << library->version() - << "\n\t\t\ttype=" << library->type() - << "\n\t\t\tisActive=" << library->isActive() - << "\n\t\t\tisNative=" << library->isNative() - << "\n\t\t\tdownloadUrl=" << library->downloadUrl() - << "\n\t\t\tstoragePath=" << library->storagePath() - << "\n\t\t\tabsolutePath=" << library->absoluteUrl() - << "\n\t\t\thint=" << library->hint(); - dbg.nospace() << "\n\t\t)"; - return dbg.maybeSpace(); -} diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h deleted file mode 100644 index 4481d146..00000000 --- a/logic/DerpVersion.h +++ /dev/null @@ -1,115 +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 -#include - -#include "DerpLibrary.h" - -class DerpInstance; - -class DerpVersion : public QAbstractListModel -{ - Q_OBJECT -public: - explicit DerpVersion(DerpInstance *instance, QObject *parent = 0); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex &parent) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - bool reload(QWidget *widgetParent); - void clear(); - - void dump() const; - -public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); - - static std::shared_ptr fromJson(const QJsonObject &obj); - - // data members -public: - /// the ID - determines which jar to use! ACTUALLY IMPORTANT! - QString id; - /// Last updated time - as a string - QString time; - /// Release time - as a string - QString releaseTime; - /// Release type - "release" or "snapshot" - QString type; - /// Assets type - "legacy" or a version ID - QString assets; - /** - * DEPRECATED: Old versions of the new vanilla launcher used this - * ex: "username_session_version" - */ - QString processArguments; - /** - * arguments that should be used for launching minecraft - * - * ex: "--username ${auth_player_name} --session ${auth_session} - * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" - */ - QString minecraftArguments; - /** - * the minimum launcher version required by this version ... current is 4 (at point of - * writing) - */ - int minimumLauncherVersion = 0xDEADBEEF; - /** - * The main class to load first - */ - QString mainClass; - - /// the list of libs - both active and inactive, native and java - QList> libraries; - - /* - FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. - - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx", - "version": "^10\\.5\\.\\d$" - } - } - ], - "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX - 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" - } - */ - // QList rules; - -private: - DerpInstance *m_instance; -}; - -QDebug operator<<(QDebug &dbg, const DerpVersion *version); -QDebug operator<<(QDebug &dbg, const DerpLibrary *library); diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp deleted file mode 100644 index 20f43404..00000000 --- a/logic/DerpVersionBuilder.cpp +++ /dev/null @@ -1,446 +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 "DerpVersionBuilder.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "DerpVersion.h" -#include "DerpInstance.h" -#include "DerpRule.h" -#include "logger/QsLog.h" - -DerpVersionBuilder::DerpVersionBuilder() -{ - -} - -bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent) -{ - DerpVersionBuilder builder; - builder.m_version = version; - builder.m_instance = instance; - builder.m_widgetParent = widgetParent; - return builder.build(); -} - -bool DerpVersionBuilder::read(DerpVersion *version, const QJsonObject &obj) -{ - DerpVersionBuilder builder; - builder.m_version = version; - builder.m_instance = 0; - builder.m_widgetParent = 0; - return builder.read(obj); -} - -bool DerpVersionBuilder::build() -{ - m_version->clear(); - - QDir root(m_instance->instanceRoot()); - QDir patches(root.absoluteFilePath("patches/")); - - // version.json -> patches/*.json -> custom.json - - // version.json - { - QLOG_INFO() << "Reading version.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) - { - return false; - } - if (!apply(obj)) - { - return false; - } - } - - // patches/ - { - // load all, put into map for ordering, apply in the right order - - QMap objects; - for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) - { - QLOG_INFO() << "Reading" << info.fileName(); - QJsonObject obj; - if (!read(info, &obj)) - { - return false; - } - if (!obj.contains("order") || !obj.value("order").isDouble()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath())); - return false; - } - objects.insert(obj.value("order").toDouble(), obj); - } - for (auto object : objects.values()) - { - qDebug() << "Applying object with order" << objects.key(object); - if (!apply(object)) - { - return false; - } - } - } - - // custom.json - { - if (QFile::exists(root.absoluteFilePath("custom.json"))) - { - QLOG_INFO() << "Reading custom.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) - { - return false; - } - if (!apply(obj)) - { - return false; - } - } - } - - return true; -} - -bool DerpVersionBuilder::read(const QJsonObject &obj) -{ - m_version->clear(); - - return apply(obj); -} - -void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) -{ - if (obj.contains(key) && obj.value(key).isString()) - { - out = obj.value(key).toString(); - } - else if (!onlyOverride) - { - if (obj.contains("+" + key) && obj.value("+" + key).isString()) - { - out += obj.value("+" + key).toString(); - } - else if (obj.contains("-" + key) && obj.value("-" + key).isString()) - { - out.remove(obj.value("-" + key).toString()); - } - } -} -void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(DerpLibrary::*func)(const QString &val)) -{ - if (obj.contains(key) && obj.value(key).isString()) - { - (lib.get()->*func)(obj.value(key).toString()); - } -} -bool DerpVersionBuilder::apply(const QJsonObject &object) -{ - applyString(object, "id", m_version->id); - applyString(object, "mainClass", m_version->mainClass); - applyString(object, "minecraftArguments", m_version->minecraftArguments, false); - applyString(object, "processArguments", m_version->processArguments, false); - if (m_version->minecraftArguments.isEmpty()) - { - const QString toCompare = m_version->processArguments.toLower(); - if (toCompare == "legacy") - { - m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; - } - } - applyString(object, "type", m_version->type); - applyString(object, "releaseTime", m_version->releaseTime); - applyString(object, "time", m_version->time); - applyString(object, "assets", m_version->assets); - { - if (m_version->assets.isEmpty()) - { - m_version->assets = "legacy"; - } - } - if (object.contains("minimumLauncherVersion")) - { - auto minLauncherVersionVal = object.value("minimumLauncherVersion"); - if (minLauncherVersionVal.isDouble()) - { - m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble(); - } - } - - // libraries - if (object.contains("libraries")) - { - m_version->libraries.clear(); - auto librariesValue = object.value("libraries"); - if (!librariesValue.isArray()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; - } - auto array = librariesValue.toArray(); - for (auto libVal : array) - { - if (libVal.isObject()) - { - if (!applyLibrary(libVal.toObject(), Override)) - { - return false; - } - } - - } - } - - // +libraries - if (object.contains("+libraries")) - { - auto librariesValue = object.value("+libraries"); - if (!librariesValue.isArray()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; - } - for (auto libVal : librariesValue.toArray()) - { - if (libVal.isObject()) - { - applyLibrary(libVal.toObject(), Add); - } - - } - } - - // -libraries - if (object.contains("-libraries")) - { - auto librariesValue = object.value("-libraries"); - if (!librariesValue.isArray()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; - } - for (auto libVal : librariesValue.toArray()) - { - if (libVal.isObject()) - { - applyLibrary(libVal.toObject(), Remove); - } - - } - } - - return true; -} - -int findLibrary(QList > haystack, const QString &needle) -{ - for (int i = 0; i < haystack.size(); ++i) - { - if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(haystack.at(i)->rawName()) != -1) - { - return i; - } - } - return -1; -} - -bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionBuilder::Type type) -{ - // Library name - auto nameVal = lib.value("name"); - if (!nameVal.isString()) - { - return false; - } - auto name = nameVal.toString(); - - if (type == Remove) - { - int index = findLibrary(m_version->libraries, name); - if (index >= 0) - { - m_version->libraries.removeAt(index); - } - return true; - } - - if (type == Add && !lib.contains("insert")) - { - return false; - } - - std::shared_ptr library; - - if (lib.value("insert").toString() != "apply" && type == Add) - { - QMutableListIterator > it(m_version->libraries); - while (it.hasNext()) - { - if (it.next()->rawName() == name) - { - it.remove(); - } - } - } - - if (lib.value("insert").toString() == "apply" && type == Add) - { - library = m_version->libraries[findLibrary(m_version->libraries, name)]; - } - else - { - library.reset(new DerpLibrary(nameVal.toString())); - } - - applyString(lib, "url", library, &DerpLibrary::setBaseUrl); - applyString(lib, "MMC-hint", library, &DerpLibrary::setHint); - applyString(lib, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); - applyString(lib, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); - - auto extractVal = lib.value("extract"); - if (extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) - { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) - { - if (excludeVal.isString()) - { - excludes.append(excludeVal.toString()); - } - } - library->extract_excludes = excludes; - } - } - - auto nativesVal = lib.value("natives"); - if (nativesVal.isObject()) - { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - auto osType = OpSys_fromString(it.key()); - if (osType == Os_Other) - { - continue; - } - if (!it.value().isString()) - { - continue; - } - library->addNative(osType, it.value().toString()); - } - } - - if (lib.contains("rules")) - { - library->setRules(rulesFromJsonV4(lib)); - } - library->finalize(); - if (type == Override) - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() != "apply") - { - if (lib.value("insert").toString() == "append") - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() == "prepend") - { - m_version->libraries.prepend(library); - } - else if (lib.value("insert").isObject()) - { - QJsonObject insertObj = lib.value("insert").toObject(); - if (insertObj.isEmpty()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty")); - return false; - } - const QString key = insertObj.keys().first(); - const QString value = insertObj.value(key).toString(); - const int index = findLibrary(m_version->libraries, value); - if (index >= 0) - { - if (key == "before") - { - m_version->libraries.insert(index, library); - } - else if (key == "after") - { - m_version->libraries.insert(index + 1, library); - } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } - } - } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } - } - return true; -} - -bool DerpVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) -{ - QFile file(fileInfo.absoluteFilePath()); - if (!file.open(QFile::ReadOnly)) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); - return false; - } - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); - if (error.error != QJsonParseError::NoError) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to parse %1: %2 at %3").arg(file.fileName(), error.errorString()).arg(error.offset)); - return false; - } - *out = doc.object(); - return true; -} diff --git a/logic/DerpVersionBuilder.h b/logic/DerpVersionBuilder.h deleted file mode 100644 index b94cd8e9..00000000 --- a/logic/DerpVersionBuilder.h +++ /dev/null @@ -1,53 +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 - -class DerpVersion; -class DerpInstance; -class QWidget; -class QJsonObject; -class QFileInfo; - -class DerpVersionBuilder -{ - DerpVersionBuilder(); -public: - static bool build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent); - static bool read(DerpVersion *version, const QJsonObject &obj); - -private: - DerpVersion *m_version; - DerpInstance *m_instance; - QWidget *m_widgetParent; - - enum Type - { - Override, - Add, - Remove - }; - - bool build(); - bool read(const QJsonObject &obj); - - void clear(); - bool apply(const QJsonObject &object); - bool applyLibrary(const QJsonObject &lib, const Type type); - - bool read(const QFileInfo &fileInfo, QJsonObject *out); -}; diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 35663ddd..863c3dfd 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -14,15 +14,15 @@ */ #include "ForgeInstaller.h" -#include "DerpVersion.h" -#include "DerpLibrary.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" #include "net/HttpMetaCache.h" #include #include #include #include #include "MultiMC.h" -#include "DerpInstance.h" +#include "OneSixInstance.h" #include #include @@ -31,7 +31,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { - std::shared_ptr newVersion; + std::shared_ptr newVersion; m_universal_url = universal_url; QuaZip zip(filename); @@ -64,7 +64,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); + newVersion = OneSixVersion::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } @@ -74,7 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) internalPath = installObj.value("filePath").toString(); // where do we put the library? decode the mojang path - DerpLibrary lib(libraryName); + OneSixLibrary lib(libraryName); lib.finalize(); auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath()); @@ -109,7 +109,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); } -bool ForgeInstaller::add(DerpInstance *to) +bool ForgeInstaller::add(OneSixInstance *to) { if (!BaseInstaller::add(to)) { diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index 6490b5bf..eb8ecd79 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -20,20 +20,20 @@ #include #include -class DerpVersion; +class OneSixVersion; class ForgeInstaller : public BaseInstaller { public: ForgeInstaller(QString filename, QString universal_url); - bool add(DerpInstance *to) override; + bool add(OneSixInstance *to) override; QString id() const override { return "net.minecraftforge"; } private: // the version, read from the installer - std::shared_ptr m_forge_version; + std::shared_ptr m_forge_version; QString internalPath; QString finalPath; QString realVersionId; diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index c50ec4e4..730bcd69 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -21,10 +21,10 @@ #include "BaseInstance.h" #include "LegacyInstance.h" #include "LegacyFTBInstance.h" -#include "DerpInstance.h" -#include "DerpFTBInstance.h" +#include "OneSixInstance.h" +#include "OneSixFTBInstance.h" #include "NostalgiaInstance.h" -#include "DerpInstance.h" +#include "OneSixInstance.h" #include "BaseVersion.h" #include "MinecraftVersion.h" @@ -51,9 +51,9 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst QString inst_type = m_settings->get("InstanceType").toString(); // FIXME: replace with a map lookup, where instance classes register their types - if (inst_type == "Derp" || inst_type == "OneSix") + if (inst_type == "OneSix" || inst_type == "OneSix") { - inst = new DerpInstance(instDir, m_settings, this); + inst = new OneSixInstance(instDir, m_settings, this); } else if (inst_type == "Legacy") { @@ -67,9 +67,9 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new LegacyFTBInstance(instDir, m_settings, this); } - else if (inst_type == "OneSixFTB" || inst_type == "DerpFTB") + else if (inst_type == "OneSixFTB" || inst_type == "OneSixFTB") { - inst = new DerpFTBInstance(instDir, m_settings, this); + inst = new OneSixFTBInstance(instDir, m_settings, this); } else { @@ -102,15 +102,15 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& switch (mcVer->type) { case MinecraftVersion::Legacy: - // TODO derp + // TODO OneSix m_settings->set("InstanceType", "Legacy"); inst = new LegacyInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; - case MinecraftVersion::Derp: - m_settings->set("InstanceType", "Derp"); - inst = new DerpInstance(instDir, m_settings, this); + case MinecraftVersion::OneSix: + m_settings->set("InstanceType", "OneSix"); + inst = new OneSixInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -137,9 +137,9 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; - case MinecraftVersion::Derp: - m_settings->set("InstanceType", "DerpFTB"); - inst = new DerpFTBInstance(instDir, m_settings, this); + case MinecraftVersion::OneSix: + m_settings->set("InstanceType", "OneSixFTB"); + inst = new OneSixFTBInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -176,8 +176,8 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); - if(inst_type == "OneSixFTB" || inst_type == "DerpFTB") - m_settings->set("InstanceType", "Derp"); + if(inst_type == "OneSixFTB" || inst_type == "OneSixFTB") + m_settings->set("InstanceType", "OneSix"); if(inst_type == "LegacyFTB") m_settings->set("InstanceType", "Legacy"); diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 1ff17cbd..1e7d9eb6 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -68,7 +68,7 @@ public: virtual QString intendedVersionId() const override; virtual bool setIntendedVersionId(QString version) override; // the `version' of Legacy instances is defined by the launcher code. - // in contrast with Derp, where `version' is described in a json file + // in contrast with OneSix, where `version' is described in a json file virtual bool versionIsCustom() override { return false; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 71c7011f..1ce9d3f4 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -20,9 +20,9 @@ #include "logger/QsLog.h" -#include "DerpVersion.h" -#include "DerpLibrary.h" -#include "DerpInstance.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "OneSixInstance.h" QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; @@ -38,12 +38,12 @@ LiteLoaderInstaller::LiteLoaderInstaller() } } -bool LiteLoaderInstaller::canApply(DerpInstance *instance) const +bool LiteLoaderInstaller::canApply(OneSixInstance *instance) const { return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId()); } -bool LiteLoaderInstaller::add(DerpInstance *to) +bool LiteLoaderInstaller::add(OneSixInstance *to) { if (!BaseInstaller::add(to)) { @@ -60,7 +60,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) // launchwrapper { - DerpLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); + OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); launchwrapperLib.finalize(); QJsonObject lwLibObj = launchwrapperLib.toJson(); lwLibObj.insert("insert", QString("prepend")); @@ -69,7 +69,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) // liteloader { - DerpLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId()); + OneSixLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId()); liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/"); liteloaderLib.finalize(); QJsonObject llLibObj = liteloaderLib.toJson(); diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index c128ee22..5e01b16c 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -25,8 +25,8 @@ class LiteLoaderInstaller : public BaseInstaller public: LiteLoaderInstaller(); - bool canApply(DerpInstance *instance) const override; - bool add(DerpInstance *to) override; + bool canApply(OneSixInstance *instance) const override; + bool add(OneSixInstance *to) override; private: virtual QString id() const override { return "com.mumfrey.liteloader"; } diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h index dd849ce9..504381a8 100644 --- a/logic/MinecraftVersion.h +++ b/logic/MinecraftVersion.h @@ -32,7 +32,7 @@ struct MinecraftVersion : public BaseVersion /// This version's type. Used internally to identify what kind of version this is. enum VersionType { - Derp, + OneSix, Legacy, Nostalgia } type; @@ -66,8 +66,8 @@ struct MinecraftVersion : public BaseVersion } switch (type) { - case Derp: - pre_final.append("Derp"); + case OneSix: + pre_final.append("OneSix"); break; case Legacy: pre_final.append("Legacy"); diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp index f27954a7..2e23ee71 100644 --- a/logic/NostalgiaInstance.cpp +++ b/logic/NostalgiaInstance.cpp @@ -17,7 +17,7 @@ NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) - : DerpInstance(rootDir, settings, parent) + : OneSixInstance(rootDir, settings, parent) { } diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h index 6a6b91a1..a26f7f0a 100644 --- a/logic/NostalgiaInstance.h +++ b/logic/NostalgiaInstance.h @@ -15,9 +15,9 @@ #pragma once -#include "DerpInstance.h" +#include "OneSixInstance.h" -class NostalgiaInstance : public DerpInstance +class NostalgiaInstance : public OneSixInstance { Q_OBJECT public: diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp new file mode 100644 index 00000000..71bc1470 --- /dev/null +++ b/logic/OneSixFTBInstance.cpp @@ -0,0 +1,123 @@ +#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) + { + emitFailed(QString("Couldn't find forge version ") + version ); + 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; + } + auto version = instance->getFullVersion(); + if (!forge.add(instance)) + { + 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::id() const +{ + return "FTB/" + BaseInstance::id(); +} + +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))); + //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; +} + +#include "OneSixFTBInstance.moc" diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h new file mode 100644 index 00000000..dc028819 --- /dev/null +++ b/logic/OneSixFTBInstance.h @@ -0,0 +1,22 @@ +#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; + + virtual QString id() const; + +private: + std::shared_ptr m_forge; +}; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp new file mode 100644 index 00000000..c43a5187 --- /dev/null +++ b/logic/OneSixInstance.cpp @@ -0,0 +1,388 @@ +/* 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 "OneSixInstance.h" + +#include + +#include "OneSixInstance_p.h" +#include "OneSixUpdate.h" +#include "OneSixVersion.h" +#include "pathutils.h" +#include "logger/QsLog.h" +#include "assets/AssetsUtils.h" +#include "MultiMC.h" +#include "icons/IconList.h" +#include "MinecraftProcess.h" +#include "gui/dialogs/OneSixModEditDialog.h" + +OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) + : BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent) +{ + I_D(OneSixInstance); + d->m_settings->registerSetting("IntendedVersion", ""); + d->m_settings->registerSetting("ShouldUpdate", false); + d->version.reset(new OneSixVersion(this, this)); + if (QDir(instanceRoot()).exists("version.json")) + { + reloadFullVersion(); + } + else + { + clearFullVersion(); + } +} + +std::shared_ptr OneSixInstance::doUpdate(bool only_prepare) +{ + return std::shared_ptr(new OneSixUpdate(this, only_prepare)); +} + +QString replaceTokensIn(QString text, QMap with) +{ + QString result; + QRegExp token_regexp("\\$\\{(.+)\\}"); + token_regexp.setMinimal(true); + QStringList list; + int tail = 0; + int head = 0; + while ((head = token_regexp.indexIn(text, head)) != -1) + { + result.append(text.mid(tail, head - tail)); + QString key = token_regexp.cap(1); + auto iter = with.find(key); + if (iter != with.end()) + { + result.append(*iter); + } + head += token_regexp.matchedLength(); + tail = head; + } + result.append(text.mid(tail)); + return result; +} + +QDir OneSixInstance::reconstructAssets(std::shared_ptr version) +{ + QDir assetsDir = QDir("assets/"); + QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); + QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects")); + QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual")); + + QString indexPath = PathCombine(indexDir.path(), version->assets + ".json"); + QFile indexFile(indexPath); + QDir virtualRoot(PathCombine(virtualDir.path(), version->assets)); + + if (!indexFile.exists()) + { + QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets"; + return virtualRoot; + } + + QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path() + << objectDir.path() << virtualDir.path() << virtualRoot.path(); + + AssetsIndex index; + bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index); + + if (loadAssetsIndex && index.isVirtual) + { + QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path(); + + for (QString map : index.objects.keys()) + { + AssetObject asset_object = index.objects.value(map); + QString target_path = PathCombine(virtualRoot.path(), map); + QFile target(target_path); + + QString tlk = asset_object.hash.left(2); + + QString original_path = + PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash); + QFile original(original_path); + if(!original.exists()) + continue; + if (!target.exists()) + { + QFileInfo info(target_path); + QDir target_dir = info.dir(); + // QLOG_DEBUG() << target_dir; + if (!target_dir.exists()) + QDir("").mkpath(target_dir.path()); + + bool couldCopy = original.copy(target_path); + QLOG_DEBUG() << " Copying" << original_path << "to" << target_path + << QString::number(couldCopy); // << original.errorString(); + } + } + + // TODO: Write last used time to virtualRoot/.lastused + } + + return virtualRoot; +} + +QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) +{ + I_D(OneSixInstance); + auto version = d->version; + QString args_pattern = version->minecraftArguments; + + QMap token_mapping; + // yggdrasil! + token_mapping["auth_username"] = account->username(); + token_mapping["auth_session"] = account->sessionId(); + token_mapping["auth_access_token"] = account->accessToken(); + token_mapping["auth_player_name"] = account->currentProfile()->name; + token_mapping["auth_uuid"] = account->currentProfile()->id; + + // this is for offline?: + /* + map["auth_player_name"] = "Player"; + map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; + */ + + // these do nothing and are stupid. + token_mapping["profile_name"] = name(); + token_mapping["version_name"] = version->id; + + QString absRootDir = QDir(minecraftRoot()).absolutePath(); + token_mapping["game_directory"] = absRootDir; + QString absAssetsDir = QDir("assets/").absolutePath(); + token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath(); + + auto user = account->user(); + QJsonObject userAttrs; + for (auto key : user.properties.keys()) + { + auto array = QJsonArray::fromStringList(user.properties.values(key)); + userAttrs.insert(key, array); + } + QJsonDocument value(userAttrs); + + token_mapping["user_properties"] = value.toJson(QJsonDocument::Compact); + token_mapping["user_type"] = account->currentProfile()->legacy ? "legacy" : "mojang"; + // 1.7.3+ assets tokens + token_mapping["assets_root"] = absAssetsDir; + token_mapping["assets_index_name"] = version->assets; + + QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); + for (int i = 0; i < parts.length(); i++) + { + parts[i] = replaceTokensIn(parts[i], token_mapping); + } + return parts; +} + +MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) +{ + I_D(OneSixInstance); + + QIcon icon = MMC->icons()->getIcon(iconKey()); + auto pixmap = icon.pixmap(128, 128); + pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); + + auto version = d->version; + if (!version) + return nullptr; + QString launchScript; + { + auto libs = version->getActiveNormalLibs(); + for (auto lib : libs) + { + QFileInfo fi(QString("libraries/") + lib->storagePath()); + launchScript += "cp " + fi.absoluteFilePath() + "\n"; + } + QString targetstr = "versions/" + version->id + "/" + version->id + ".jar"; + QFileInfo fi(targetstr); + launchScript += "cp " + fi.absoluteFilePath() + "\n"; + } + launchScript += "mainClass " + version->mainClass + "\n"; + + for (auto param : processMinecraftArgs(account)) + { + launchScript += "param " + param + "\n"; + } + + // Set the width and height for 1.6 instances + bool maximize = settings().get("LaunchMaximized").toBool(); + if (maximize) + { + // this is probably a BAD idea + // launchScript += "param --fullscreen\n"; + } + else + { + launchScript += + "param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n"; + launchScript += + "param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n"; + } + QDir natives_dir(PathCombine(instanceRoot(), "natives/")); + launchScript += "windowTitle " + windowTitle() + "\n"; + for(auto native: version->getActiveNativeLibs()) + { + QFileInfo finfo(PathCombine("libraries", native->storagePath())); + launchScript += "ext " + finfo.absoluteFilePath() + "\n"; + } + launchScript += "natives " + natives_dir.absolutePath() + "\n"; + launchScript += "launch onesix\n"; + + // create the process and set its parameters + MinecraftProcess *proc = new MinecraftProcess(this); + proc->setWorkdir(minecraftRoot()); + proc->setLaunchScript(launchScript); + // proc->setNativeFolder(natives_dir.absolutePath()); + return proc; +} + +void OneSixInstance::cleanupAfterRun() +{ + QString target_dir = PathCombine(instanceRoot(), "natives/"); + QDir dir(target_dir); + dir.removeRecursively(); +} + +std::shared_ptr OneSixInstance::loaderModList() +{ + I_D(OneSixInstance); + if (!d->loader_mod_list) + { + d->loader_mod_list.reset(new ModList(loaderModsDir())); + } + d->loader_mod_list->update(); + return d->loader_mod_list; +} + +std::shared_ptr OneSixInstance::resourcePackList() +{ + I_D(OneSixInstance); + if (!d->resource_pack_list) + { + d->resource_pack_list.reset(new ModList(resourcePacksDir())); + } + d->resource_pack_list->update(); + return d->resource_pack_list; +} + +QDialog *OneSixInstance::createModEditDialog(QWidget *parent) +{ + return new OneSixModEditDialog(this, parent); +} + +bool OneSixInstance::setIntendedVersionId(QString version) +{ + settings().set("IntendedVersion", version); + setShouldUpdate(true); + QFile::remove(PathCombine(instanceRoot(), "version.json")); + clearFullVersion(); + return true; +} + +QString OneSixInstance::intendedVersionId() const +{ + return settings().get("IntendedVersion").toString(); +} + +void OneSixInstance::setShouldUpdate(bool val) +{ + settings().set("ShouldUpdate", val); +} + +bool OneSixInstance::shouldUpdate() const +{ + QVariant var = settings().get("ShouldUpdate"); + if (!var.isValid() || var.toBool() == false) + { + return intendedVersionId() != currentVersionId(); + } + return true; +} + +bool OneSixInstance::versionIsCustom() +{ + QDir patches(PathCombine(instanceRoot(), "patches/")); + return QFile::exists(PathCombine(instanceRoot(), "custom.json")) + || (patches.exists() && patches.count() >= 0); +} + +QString OneSixInstance::currentVersionId() const +{ + return intendedVersionId(); +} + +bool OneSixInstance::reloadFullVersion(QWidget *widgetParent) +{ + I_D(OneSixInstance); + + bool ret = d->version->reload(widgetParent); + emit versionReloaded(); + return ret; +} + +void OneSixInstance::clearFullVersion() +{ + I_D(OneSixInstance); + d->version->clear(); + emit versionReloaded(); +} + +std::shared_ptr OneSixInstance::getFullVersion() +{ + I_D(OneSixInstance); + return d->version; +} + +QString OneSixInstance::defaultBaseJar() const +{ + return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; +} + +QString OneSixInstance::defaultCustomBaseJar() const +{ + return PathCombine(instanceRoot(), "custom.jar"); +} + +bool OneSixInstance::menuActionEnabled(QString action_name) const +{ + if (action_name == "actionChangeInstLWJGLVersion") + return false; + return true; +} + +QString OneSixInstance::getStatusbarDescription() +{ + QString descr = "OneSix : " + intendedVersionId(); + if (versionIsCustom()) + { + descr + " (custom)"; + } + return descr; +} + +QString OneSixInstance::loaderModsDir() const +{ + return PathCombine(minecraftRoot(), "mods"); +} + +QString OneSixInstance::resourcePacksDir() const +{ + return PathCombine(minecraftRoot(), "resourcepacks"); +} + +QString OneSixInstance::instanceConfigFolder() const +{ + return PathCombine(minecraftRoot(), "config"); +} diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h new file mode 100644 index 00000000..94577828 --- /dev/null +++ b/logic/OneSixInstance.h @@ -0,0 +1,75 @@ +/* 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 "BaseInstance.h" + +#include "OneSixVersion.h" +#include "ModList.h" + +class OneSixInstance : public BaseInstance +{ + Q_OBJECT +public: + explicit OneSixInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + + ////// Mod Lists ////// + std::shared_ptr loaderModList(); + std::shared_ptr resourcePackList(); + + ////// Directories ////// + QString resourcePacksDir() const; + QString loaderModsDir() const; + virtual QString instanceConfigFolder() const override; + + virtual std::shared_ptr doUpdate(bool only_prepare) override; + virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; + + virtual void cleanupAfterRun() override; + + virtual QString intendedVersionId() const override; + virtual bool setIntendedVersionId(QString version) override; + + virtual QString currentVersionId() const override; + + virtual bool shouldUpdate() const override; + virtual void setShouldUpdate(bool val) override; + + virtual QDialog *createModEditDialog(QWidget *parent) override; + + /// reload the full version json files. return true on success! + bool reloadFullVersion(QWidget *widgetParent = 0); + /// clears all version information in preparation for an update + void clearFullVersion(); + /// get the current full version info + std::shared_ptr getFullVersion(); + /// is the current version original, or custom? + virtual bool versionIsCustom() override; + + virtual QString defaultBaseJar() const override; + virtual QString defaultCustomBaseJar() const override; + + virtual bool menuActionEnabled(QString action_name) const override; + virtual QString getStatusbarDescription() override; + +signals: + void versionReloaded(); + +private: + QStringList processMinecraftArgs(MojangAccountPtr account); + QDir reconstructAssets(std::shared_ptr version); +}; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h new file mode 100644 index 00000000..a2b70ac9 --- /dev/null +++ b/logic/OneSixInstance_p.h @@ -0,0 +1,27 @@ +/* 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 "BaseInstance_p.h" +#include "OneSixVersion.h" +#include "ModList.h" + +struct OneSixInstancePrivate : public BaseInstancePrivate +{ + std::shared_ptr version; + std::shared_ptr loader_mod_list; + std::shared_ptr resource_pack_list; +}; diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp new file mode 100644 index 00000000..4d892d53 --- /dev/null +++ b/logic/OneSixLibrary.cpp @@ -0,0 +1,268 @@ +/* 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 + +#include "OneSixLibrary.h" +#include "OneSixRule.h" +#include "OpSys.h" +#include "logic/net/URLConstants.h" +#include +#include +#include "logger/QsLog.h" + +void OneSixLibrary::finalize() +{ + QStringList parts = m_name.split(':'); + QString relative = parts[0]; + relative.replace('.', '/'); + relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; + + if (!m_is_native) + relative += ".jar"; + else + { + if (m_native_suffixes.contains(currentSystem)) + { + relative += "-" + m_native_suffixes[currentSystem] + ".jar"; + } + else + { + // really, bad. + relative += ".jar"; + } + } + + m_decentname = parts[1]; + m_decentversion = parts[2]; + m_storage_path = relative; + m_download_url = m_base_url + relative; + + if (m_rules.empty()) + { + m_is_active = true; + } + else + { + RuleAction result = Disallow; + for (auto rule : m_rules) + { + RuleAction temp = rule->apply(this); + if (temp != Defer) + result = temp; + } + m_is_active = (result == Allow); + } + if (m_is_native) + { + m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); + m_decenttype = "Native"; + } + else + { + m_decenttype = "Java"; + } +} + +void OneSixLibrary::setName(const QString &name) +{ + m_name = name; +} +void OneSixLibrary::setBaseUrl(const QString &base_url) +{ + m_base_url = base_url; +} +void OneSixLibrary::setIsNative() +{ + m_is_native = true; +} +void OneSixLibrary::addNative(OpSys os, const QString &suffix) +{ + m_is_native = true; + m_native_suffixes[os] = suffix; +} +void OneSixLibrary::setRules(QList> rules) +{ + m_rules = rules; +} +bool OneSixLibrary::isActive() const +{ + return m_is_active; +} +bool OneSixLibrary::isNative() const +{ + return m_is_native; +} +QString OneSixLibrary::downloadUrl() const +{ + if (m_absolute_url.size()) + return m_absolute_url; + return m_download_url; +} +QString OneSixLibrary::storagePath() const +{ + return m_storage_path; +} + +void OneSixLibrary::setAbsoluteUrl(const QString &absolute_url) +{ + m_absolute_url = absolute_url; +} + +QString OneSixLibrary::absoluteUrl() const +{ + return m_absolute_url; +} + +void OneSixLibrary::setHint(const QString &hint) +{ + m_hint = hint; +} + +QString OneSixLibrary::hint() const +{ + return m_hint; +} + +bool OneSixLibrary::filesExist() +{ + QString storage = storagePath(); + if (storage.contains("${arch}")) + { + QString cooked_storage = storage; + cooked_storage.replace("${arch}", "32"); + QFileInfo info32(PathCombine("libraries", cooked_storage)); + if (!info32.exists()) + { + return false; + } + cooked_storage = storage; + cooked_storage.replace("${arch}", "64"); + QFileInfo info64(PathCombine("libraries", cooked_storage)); + if (!info64.exists()) + { + return false; + } + } + else + { + QFileInfo info(PathCombine("libraries", storage)); + if (!info.exists()) + { + return false; + } + } + return true; +} + +bool OneSixLibrary::extractTo(QString target_dir) +{ + QString storage = storagePath(); + if (storage.contains("${arch}")) + { + QString cooked_storage = storage; + cooked_storage.replace("${arch}", "32"); + QString origin = PathCombine("libraries", cooked_storage); + QString target_dir_cooked = PathCombine(target_dir, "32"); + if(!ensureFolderPathExists(target_dir_cooked)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; + return false; + } + if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) + .isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + origin; + return false; + } + cooked_storage = storage; + cooked_storage.replace("${arch}", "64"); + origin = PathCombine("libraries", cooked_storage); + target_dir_cooked = PathCombine(target_dir, "64"); + if(!ensureFolderPathExists(target_dir_cooked)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; + return false; + } + if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes) + .isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + origin; + return false; + } + } + else + { + if(!ensureFolderPathExists(target_dir)) + { + QLOG_ERROR() << "Couldn't create folder " + target_dir; + return false; + } + QString path = PathCombine("libraries", storage); + if (JlCompress::extractWithExceptions(path, target_dir, extract_excludes).isEmpty()) + { + QLOG_ERROR() << "Couldn't extract " + path; + return false; + } + } + return true; +} + +QJsonObject OneSixLibrary::toJson() +{ + QJsonObject libRoot; + libRoot.insert("name", m_name); + if (m_absolute_url.size()) + libRoot.insert("MMC-absoluteUrl", m_absolute_url); + if (m_hint.size()) + libRoot.insert("MMC-hint", m_hint); + if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && + m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && + m_base_url != "https://" + URLConstants::LIBRARY_BASE) + libRoot.insert("url", m_base_url); + if (isNative() && m_native_suffixes.size()) + { + QJsonObject nativeList; + auto iter = m_native_suffixes.begin(); + while (iter != m_native_suffixes.end()) + { + nativeList.insert(OpSys_toString(iter.key()), iter.value()); + iter++; + } + libRoot.insert("natives", nativeList); + } + if (isNative() && extract_excludes.size()) + { + QJsonArray excludes; + QJsonObject extract; + for (auto exclude : extract_excludes) + { + excludes.append(exclude); + } + extract.insert("exclude", excludes); + libRoot.insert("extract", extract); + } + if (m_rules.size()) + { + QJsonArray allRules; + for (auto &rule : m_rules) + { + QJsonObject ruleObj = rule->toJson(); + allRules.append(ruleObj); + } + libRoot.insert("rules", allRules); + } + return libRoot; +} diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h new file mode 100644 index 00000000..52f89969 --- /dev/null +++ b/logic/OneSixLibrary.h @@ -0,0 +1,132 @@ +/* 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 "logic/net/URLConstants.h" +#include "OpSys.h" + +class Rule; + +class OneSixLibrary +{ +private: + // basic values used internally (so far) + QString m_name; + QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; + QList> m_rules; + + // custom values + /// absolute URL. takes precedence over m_download_path, if defined + QString m_absolute_url; + /// download hint - how to actually get the library + QString m_hint; + + // derived values used for real things + /// a decent name fit for display + QString m_decentname; + /// a decent version fit for display + QString m_decentversion; + /// a decent type fit for display + QString m_decenttype; + /// where to store the lib locally + QString m_storage_path; + /// where to download the lib from + QString m_download_url; + /// is this lib actually active on the current OS? + bool m_is_active = false; + /// is the library a native? + bool m_is_native = false; + /// native suffixes per OS + QMap m_native_suffixes; + +public: + QStringList extract_excludes; + +public: + /// Constructor + OneSixLibrary(const QString &name) + { + m_name = name; + } + + /// Returns the raw name field + QString rawName() const + { + return m_name; + } + + QJsonObject toJson(); + + /** + * finalize the library, processing the input values into derived values and state + * + * This SHALL be called after all the values are parsed or after any further change. + */ + void finalize(); + + /// Set the library composite name + void setName(const QString &name); + /// get a decent-looking name + QString name() const + { + return m_decentname; + } + /// get a decent-looking version + QString version() const + { + return m_decentversion; + } + /// what kind of library is it? (for display) + QString type() const + { + return m_decenttype; + } + /// Set the url base for downloads + void setBaseUrl(const QString &base_url); + + /// Call this to mark the library as 'native' (it's a zip archive with DLLs) + void setIsNative(); + /// Attach a name suffix to the specified OS native + void addNative(OpSys os, const QString &suffix); + /// Set the load rules + void setRules(QList> rules); + + /// Returns true if the library should be loaded (or extracted, in case of natives) + bool isActive() const; + /// Returns true if the library is native + bool isNative() const; + /// Get the URL to download the library from + QString downloadUrl() const; + /// Get the relative path where the library should be saved + QString storagePath() const; + + /// set an absolute URL for the library. This is an MMC extension. + void setAbsoluteUrl(const QString &absolute_url); + QString absoluteUrl() const; + + /// set a hint about how to treat the library. This is an MMC extension. + void setHint(const QString &hint); + QString hint() const; + + bool extractTo(QString target_dir); + bool filesExist(); +}; diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp new file mode 100644 index 00000000..d8d13b50 --- /dev/null +++ b/logic/OneSixRule.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 +#include + +#include "OneSixRule.h" + +QList> rulesFromJsonV4(const QJsonObject &objectWithRules) +{ + QList> rules; + auto rulesVal = objectWithRules.value("rules"); + if (!rulesVal.isArray()) + return rules; + + QJsonArray ruleList = rulesVal.toArray(); + for (auto ruleVal : ruleList) + { + std::shared_ptr rule; + if (!ruleVal.isObject()) + continue; + auto ruleObj = ruleVal.toObject(); + auto actionVal = ruleObj.value("action"); + if (!actionVal.isString()) + continue; + auto action = RuleAction_fromString(actionVal.toString()); + if (action == Defer) + continue; + + auto osVal = ruleObj.value("os"); + if (!osVal.isObject()) + { + // add a new implicit action rule + rules.append(ImplicitRule::create(action)); + continue; + } + + auto osObj = osVal.toObject(); + auto osNameVal = osObj.value("name"); + if (!osNameVal.isString()) + continue; + OpSys requiredOs = OpSys_fromString(osNameVal.toString()); + QString versionRegex = osObj.value("version").toString(); + // add a new OS rule + rules.append(OsRule::create(action, requiredOs, versionRegex)); + } + return rules; +} + +QJsonObject ImplicitRule::toJson() +{ + QJsonObject ruleObj; + ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); + return ruleObj; +} + +QJsonObject OsRule::toJson() +{ + QJsonObject ruleObj; + ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); + QJsonObject osObj; + { + osObj.insert("name", OpSys_toString(m_system)); + osObj.insert("version", m_version_regexp); + } + ruleObj.insert("os", osObj); + return ruleObj; +} + +RuleAction RuleAction_fromString(QString name) +{ + if (name == "allow") + return Allow; + if (name == "disallow") + return Disallow; + return Defer; +} diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h new file mode 100644 index 00000000..426e2886 --- /dev/null +++ b/logic/OneSixRule.h @@ -0,0 +1,98 @@ +/* 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 "logic/OneSixLibrary.h" + +enum RuleAction +{ + Allow, + Disallow, + Defer +}; + +RuleAction RuleAction_fromString(QString); +QList> rulesFromJsonV4(const QJsonObject &objectWithRules); + +class Rule +{ +protected: + RuleAction m_result; + virtual bool applies(OneSixLibrary *parent) = 0; + +public: + Rule(RuleAction result) : m_result(result) + { + } + virtual ~Rule() {}; + virtual QJsonObject toJson() = 0; + RuleAction apply(OneSixLibrary *parent) + { + if (applies(parent)) + return m_result; + else + return Defer; + } + ; +}; + +class OsRule : public Rule +{ +private: + // the OS + OpSys m_system; + // the OS version regexp + QString m_version_regexp; + +protected: + virtual bool applies(OneSixLibrary *) + { + return (m_system == currentSystem); + } + OsRule(RuleAction result, OpSys system, QString version_regexp) + : Rule(result), m_system(system), m_version_regexp(version_regexp) + { + } + +public: + virtual QJsonObject toJson(); + static std::shared_ptr create(RuleAction result, OpSys system, + QString version_regexp) + { + return std::shared_ptr(new OsRule(result, system, version_regexp)); + } +}; + +class ImplicitRule : public Rule +{ +protected: + virtual bool applies(OneSixLibrary *) + { + return true; + } + ImplicitRule(RuleAction result) : Rule(result) + { + } + +public: + virtual QJsonObject toJson(); + static std::shared_ptr create(RuleAction result) + { + return std::shared_ptr(new ImplicitRule(result)); + } +}; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp new file mode 100644 index 00000000..f51b6c23 --- /dev/null +++ b/logic/OneSixUpdate.cpp @@ -0,0 +1,372 @@ +/* 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 "MultiMC.h" +#include "OneSixUpdate.h" + +#include + +#include +#include +#include +#include + +#include "BaseInstance.h" +#include "lists/MinecraftVersionList.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "OneSixInstance.h" +#include "net/ForgeMirrors.h" +#include "net/URLConstants.h" +#include "assets/AssetsUtils.h" + +#include "pathutils.h" +#include + +OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) + : Task(parent), m_inst(inst), m_only_prepare(only_prepare) +{ +} + +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; + } + + if (m_only_prepare) + { + prepareForLaunch(); + return; + } + + if (m_inst->shouldUpdate()) + { + // Get a pointer to the version object that corresponds to the instance's version. + targetVersion = std::dynamic_pointer_cast( + MMC->minecraftlist()->findVersion(intendedVersion)); + if (targetVersion == nullptr) + { + // don't do anything if it was invalid + emitFailed("The specified Minecraft version is invalid. Choose a different one."); + return; + } + versionFileStart(); + } + else + { + jarlibStart(); + } +} + +void OneSixUpdate::versionFileStart() +{ + QLOG_INFO() << m_inst->name() << ": getting version file."; + 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"); + job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); + specificVersionDownloadJob.reset(job); + connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished())); + connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed())); + connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + specificVersionDownloadJob->start(); +} + +void OneSixUpdate::versionFileFinished() +{ + NetActionPtr DlJob = specificVersionDownloadJob->first(); + OneSixInstance *inst = (OneSixInstance *)m_inst; + + QString version_id = targetVersion->descriptor(); + QString inst_dir = m_inst->instanceRoot(); + // save the version file in $instanceId/version.json + { + QString version1 = PathCombine(inst_dir, "/version.json"); + ensureFilePathExists(version1); + // FIXME: detect errors here, download to a temp file, swap + QSaveFile vfile1(version1); + if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) + { + emitFailed("Can't open " + version1 + " for writing."); + return; + } + auto data = std::dynamic_pointer_cast(DlJob)->m_data; + qint64 actual = 0; + if ((actual = vfile1.write(data)) != data.size()) + { + emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " + + data.size() + '.'); + return; + } + if (!vfile1.commit()) + { + emitFailed("Can't commit changes to " + version1); + return; + } + } + + // the version is downloaded safely. update is 'done' at this point + m_inst->setShouldUpdate(false); + + // delete any custom version inside the instance (it's no longer relevant, we did an update) + QString custom = PathCombine(inst_dir, "/custom.json"); + QFile finfo(custom); + if (finfo.exists()) + { + finfo.remove(); + } + inst->reloadFullVersion(); + + jarlibStart(); +} + +void OneSixUpdate::versionFileFailed() +{ + emitFailed("Failed to download the version description. Try again."); +} + +void OneSixUpdate::assetIndexStart() +{ + setStatus(tr("Updating assets index...")); + OneSixInstance *inst = (OneSixInstance *)m_inst; + std::shared_ptr version = inst->getFullVersion(); + QString assetName = version->assets; + QUrl indexUrl = "http://" + URLConstants::AWS_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 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 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://" + URLConstants::RESOURCE_BASE + objectName), + objectFile.filePath()); + objectDL->m_total_progress = object.size; + dls.append(objectDL); + } + } + if (dls.size()) + { + setStatus(tr("Getting the assets files from Mojang...")); + 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(tr("Getting the library files from Mojang...")); + QLOG_INFO() << m_inst->name() << ": downloading libraries"; + OneSixInstance *inst = (OneSixInstance *)m_inst; + bool successful = inst->reloadFullVersion(); + if (!successful) + { + emitFailed("Failed to load the version description file. It might be " + "corrupted, missing or simply too new."); + return; + } + + // Build a list of URLs that will need to be downloaded. + std::shared_ptr version = inst->getFullVersion(); + // minecraft.jar for this version + { + QString version_id = version->id; + QString localPath = version_id + "/" + version_id + ".jar"; + QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; + + auto job = new NetJob("Libraries for instance " + inst->name()); + + auto metacache = MMC->metacache(); + auto entry = metacache->resolveEntry("versions", localPath); + job->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); + + jarlibDownloadJob.reset(job); + } + + auto libs = version->getActiveNativeLibs(); + libs.append(version->getActiveNormalLibs()); + + auto metacache = MMC->metacache(); + QList ForgeLibs; + for (auto lib : libs) + { + if (lib->hint() == "local") + continue; + + QString raw_storage = lib->storagePath(); + QString raw_dl = lib->downloadUrl(); + + auto f = [&](QString storage, QString dl) + { + auto entry = metacache->resolveEntry("libraries", storage); + if (entry->stale) + { + if (lib->hint() == "forge-pack-xz") + { + ForgeLibs.append(ForgeXzDownload::make(storage, entry)); + } + else + { + jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); + } + } + }; + if (raw_storage.contains("${arch}")) + { + QString cooked_storage = raw_storage; + QString cooked_dl = raw_dl; + f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); + cooked_storage = raw_storage; + cooked_dl = raw_dl; + f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); + } + else + { + f(raw_storage, raw_dl); + } + } + // TODO: think about how to propagate this from the original json file... or IF AT ALL + QString forgeMirrorList = "http://files.minecraftforge.net/mirror-brand.list"; + if (!ForgeLibs.empty()) + { + jarlibDownloadJob->addNetAction( + ForgeMirrors::make(ForgeLibs, jarlibDownloadJob, forgeMirrorList)); + } + + connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); + connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed())); + connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + + jarlibDownloadJob->start(); +} + +void OneSixUpdate::jarlibFinished() +{ + assetIndexStart(); +} + +void OneSixUpdate::jarlibFailed() +{ + QStringList failed = jarlibDownloadJob->getFailedFiles(); + QString failed_all = failed.join("\n"); + emitFailed("Failed to download the following files:\n" + failed_all + + "\n\nPlease try again."); +} + +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(); +} diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h new file mode 100644 index 00000000..bc717a94 --- /dev/null +++ b/logic/OneSixUpdate.h @@ -0,0 +1,63 @@ +/* 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 "logic/net/NetJob.h" +#include "logic/tasks/Task.h" + +class MinecraftVersion; +class BaseInstance; + +class OneSixUpdate : public Task +{ + Q_OBJECT +public: + explicit OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); + virtual void executeTask(); + +private +slots: + void versionFileStart(); + void versionFileFinished(); + void versionFileFailed(); + + void jarlibStart(); + void jarlibFinished(); + void jarlibFailed(); + + void assetIndexStart(); + void assetIndexFinished(); + void assetIndexFailed(); + + void assetsFinished(); + void assetsFailed(); + + // extract the appropriate libraries + void prepareForLaunch(); + +private: + NetJobPtr specificVersionDownloadJob; + NetJobPtr jarlibDownloadJob; + + // target version, determined during this task + std::shared_ptr targetVersion; + BaseInstance *m_inst = nullptr; + bool m_only_prepare = false; +}; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 3571be9b..3db3bd77 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -13,228 +13,60 @@ * limitations under the License. */ -#include "logic/DerpVersion.h" -#include "logic/DerpLibrary.h" -#include "logic/DerpRule.h" +#include "OneSixVersion.h" -#include "logger/QsLog.h" +#include -std::shared_ptr fromJsonV4(QJsonObject root, - std::shared_ptr fullVersion) -{ - fullVersion->id = root.value("id").toString(); - - fullVersion->mainClass = root.value("mainClass").toString(); - auto procArgsValue = root.value("processArguments"); - if (procArgsValue.isString()) - { - fullVersion->processArguments = procArgsValue.toString(); - QString toCompare = fullVersion->processArguments.toLower(); - if (toCompare == "legacy") - { - fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { - fullVersion->minecraftArguments = - "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { - fullVersion->minecraftArguments = "--username ${auth_player_name} " - "--session ${auth_session} " - "--version ${profile_name}"; - } - } - - auto minecraftArgsValue = root.value("minecraftArguments"); - if (minecraftArgsValue.isString()) - { - fullVersion->minecraftArguments = minecraftArgsValue.toString(); - } - - auto minecraftTypeValue = root.value("type"); - if (minecraftTypeValue.isString()) - { - fullVersion->type = minecraftTypeValue.toString(); - } - - fullVersion->releaseTime = root.value("releaseTime").toString(); - fullVersion->time = root.value("time").toString(); - - auto assetsID = root.value("assets"); - if (assetsID.isString()) - { - fullVersion->assets = assetsID.toString(); - } - else - { - fullVersion->assets = "legacy"; - } - - QLOG_DEBUG() << "Assets version:" << fullVersion->assets; - - // Iterate through the list, if it's a list. - auto librariesValue = root.value("libraries"); - if (!librariesValue.isArray()) - return fullVersion; - - QJsonArray libList = root.value("libraries").toArray(); - for (auto libVal : libList) - { - if (!libVal.isObject()) - { - continue; - } - - QJsonObject libObj = libVal.toObject(); - - // Library name - auto nameVal = libObj.value("name"); - if (!nameVal.isString()) - continue; - std::shared_ptr library(new DerpLibrary(nameVal.toString())); - - auto urlVal = libObj.value("url"); - if (urlVal.isString()) - { - library->setBaseUrl(urlVal.toString()); - } - auto hintVal = libObj.value("MMC-hint"); - if (hintVal.isString()) - { - library->setHint(hintVal.toString()); - } - auto urlAbsVal = libObj.value("MMC-absoluteUrl"); - auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility - if (urlAbsVal.isString()) - { - library->setAbsoluteUrl(urlAbsVal.toString()); - } - else if (urlAbsuVal.isString()) - { - library->setAbsoluteUrl(urlAbsuVal.toString()); - } - // Extract excludes (if any) - auto extractVal = libObj.value("extract"); - if (extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) - { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) - { - if (excludeVal.isString()) - excludes.append(excludeVal.toString()); - } - library->extract_excludes = excludes; - } - } +#include "OneSixVersionBuilder.h" - auto nativesVal = libObj.value("natives"); - if (nativesVal.isObject()) - { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - auto iter = nativesObj.begin(); - while (iter != nativesObj.end()) - { - auto osType = OpSys_fromString(iter.key()); - if (osType == Os_Other) - continue; - if (!iter.value().isString()) - continue; - library->addNative(osType, iter.value().toString()); - iter++; - } - } - library->setRules(rulesFromJsonV4(libObj)); - library->finalize(); - fullVersion->libraries.append(library); - } - return fullVersion; +OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) + : QAbstractListModel(parent), m_instance(instance) +{ + clear(); } -std::shared_ptr DerpVersion::fromJson(QJsonObject root) +bool OneSixVersion::reload(QWidget *widgetParent) { - std::shared_ptr readVersion(new DerpVersion()); - int launcher_ver = readVersion->minimumLauncherVersion = - root.value("minimumLauncherVersion").toDouble(); - - // ADD MORE HERE :D - if (launcher_ver > 0 && launcher_ver <= 13) - return fromJsonV4(root, readVersion); - else - { - return std::shared_ptr(); - } + return OneSixVersionBuilder::build(this, m_instance, widgetParent); } -std::shared_ptr DerpVersion::fromFile(QString filepath) +void OneSixVersion::clear() { - QFile file(filepath); - if (!file.open(QIODevice::ReadOnly)) - { - return std::shared_ptr(); - } - - auto data = file.readAll(); - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - return std::shared_ptr(); - } - - if (!jsonDoc.isObject()) - { - return std::shared_ptr(); - } - QJsonObject root = jsonDoc.object(); - auto version = fromJson(root); - if (version) - version->original_file = filepath; - return version; + id.clear(); + time.clear(); + releaseTime.clear(); + type.clear(); + assets.clear(); + processArguments.clear(); + minecraftArguments.clear(); + minimumLauncherVersion = 0xDEADBEAF; + mainClass.clear(); + libraries.clear(); } -bool DerpVersion::toOriginalFile() +void OneSixVersion::dump() const { - if (original_file.isEmpty()) - return false; - QSaveFile file(original_file); - if (!file.open(QIODevice::WriteOnly)) - { - return false; - } - // serialize base attributes (those we care about anyway) - QJsonObject root; - root.insert("minecraftArguments", minecraftArguments); - root.insert("mainClass", mainClass); - root.insert("minimumLauncherVersion", minimumLauncherVersion); - root.insert("time", time); - root.insert("id", id); - root.insert("type", type); - // screw processArguments - root.insert("releaseTime", releaseTime); - QJsonArray libarray; - for (const auto &lib : libraries) + qDebug().nospace() << "OneSixVersion(" + << "\n\tid=" << id + << "\n\ttime=" << time + << "\n\treleaseTime=" << releaseTime + << "\n\ttype=" << type + << "\n\tassets=" << assets + << "\n\tprocessArguments=" << processArguments + << "\n\tminecraftArguments=" << minecraftArguments + << "\n\tminimumLauncherVersion=" << minimumLauncherVersion + << "\n\tmainClass=" << mainClass + << "\n\tlibraries="; + for (auto lib : libraries) { - libarray.append(lib->toJson()); + qDebug().nospace() << "\n\t\t" << lib.get(); } - if (libarray.count()) - root.insert("libraries", libarray); - QJsonDocument doc(root); - file.write(doc.toJson()); - return file.commit(); + qDebug().nospace() << "\n)"; } -QList> DerpVersion::getActiveNormalLibs() +QList > OneSixVersion::getActiveNormalLibs() { - QList> output; + QList > output; for (auto lib : libraries) { if (lib->isActive() && !lib->isNative()) @@ -245,9 +77,9 @@ QList> DerpVersion::getActiveNormalLibs() return output; } -QList> DerpVersion::getActiveNativeLibs() +QList > OneSixVersion::getActiveNativeLibs() { - QList> output; + QList > output; for (auto lib : libraries) { if (lib->isActive() && lib->isNative()) @@ -258,17 +90,17 @@ QList> DerpVersion::getActiveNativeLibs() return output; } -void DerpVersion::externalUpdateStart() -{ - beginResetModel(); -} - -void DerpVersion::externalUpdateFinish() +std::shared_ptr OneSixVersion::fromJson(const QJsonObject &obj) { - endResetModel(); + std::shared_ptr version(new OneSixVersion(0)); + if (OneSixVersionBuilder::read(version.get(), obj)) + { + return version; + } + return 0; } -QVariant DerpVersion::data(const QModelIndex &index, int role) const +QVariant OneSixVersion::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); @@ -296,7 +128,7 @@ QVariant DerpVersion::data(const QModelIndex &index, int role) const return QVariant(); } -Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const +Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; @@ -312,7 +144,7 @@ Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const // return QAbstractListModel::flags(index); } -QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const +QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); @@ -329,12 +161,34 @@ QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int r } } -int DerpVersion::rowCount(const QModelIndex &parent) const +int OneSixVersion::rowCount(const QModelIndex &parent) const { return libraries.size(); } -int DerpVersion::columnCount(const QModelIndex &parent) const +int OneSixVersion::columnCount(const QModelIndex &parent) const { return 3; } + +QDebug operator<<(QDebug &dbg, const OneSixVersion *version) +{ + version->dump(); + return dbg.maybeSpace(); +} +QDebug operator<<(QDebug &dbg, const OneSixLibrary *library) +{ + dbg.nospace() << "OneSixLibrary(" + << "\n\t\t\trawName=" << library->rawName() + << "\n\t\t\tname=" << library->name() + << "\n\t\t\tversion=" << library->version() + << "\n\t\t\ttype=" << library->type() + << "\n\t\t\tisActive=" << library->isActive() + << "\n\t\t\tisNative=" << library->isNative() + << "\n\t\t\tdownloadUrl=" << library->downloadUrl() + << "\n\t\t\tstoragePath=" << library->storagePath() + << "\n\t\t\tabsolutePath=" << library->absoluteUrl() + << "\n\t\t\thint=" << library->hint(); + dbg.nospace() << "\n\t\t)"; + return dbg.maybeSpace(); +} diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index e8f744aa..00afd010 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -14,15 +14,23 @@ */ #pragma once -#include + +#include + +#include +#include #include -class DerpLibrary; +#include "OneSixLibrary.h" + +class OneSixInstance; -class DerpVersion : public QAbstractListModel +class OneSixVersion : public QAbstractListModel { - // Things required to implement the Qt list model + Q_OBJECT public: + explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0); + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant headerData(int section, Qt::Orientation orientation, @@ -30,24 +38,19 @@ public: virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; - // serialization/deserialization -public: - bool toOriginalFile(); - static std::shared_ptr fromJson(QJsonObject root); - static std::shared_ptr fromFile(QString filepath); + bool reload(QWidget *widgetParent); + void clear(); + + void dump() const; public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); - // called when something starts/stops messing with the object - // FIXME: these are ugly in every possible way. - void externalUpdateStart(); - void externalUpdateFinish(); + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); + + static std::shared_ptr fromJson(const QJsonObject &obj); // data members public: - /// file this was read from. blank, if none - QString original_file; /// the ID - determines which jar to use! ACTUALLY IMPORTANT! QString id; /// Last updated time - as a string @@ -81,7 +84,7 @@ public: QString mainClass; /// the list of libs - both active and inactive, native and java - QList> libraries; + QList> libraries; /* FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. @@ -103,4 +106,10 @@ public: } */ // QList rules; + +private: + OneSixInstance *m_instance; }; + +QDebug operator<<(QDebug &dbg, const OneSixVersion *version); +QDebug operator<<(QDebug &dbg, const OneSixLibrary *library); diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp new file mode 100644 index 00000000..85f55a52 --- /dev/null +++ b/logic/OneSixVersionBuilder.cpp @@ -0,0 +1,446 @@ +/* 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 "OneSixVersionBuilder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "OneSixVersion.h" +#include "OneSixInstance.h" +#include "OneSixRule.h" +#include "logger/QsLog.h" + +OneSixVersionBuilder::OneSixVersionBuilder() +{ + +} + +bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent) +{ + OneSixVersionBuilder builder; + builder.m_version = version; + builder.m_instance = instance; + builder.m_widgetParent = widgetParent; + return builder.build(); +} + +bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) +{ + OneSixVersionBuilder builder; + builder.m_version = version; + builder.m_instance = 0; + builder.m_widgetParent = 0; + return builder.read(obj); +} + +bool OneSixVersionBuilder::build() +{ + m_version->clear(); + + QDir root(m_instance->instanceRoot()); + QDir patches(root.absoluteFilePath("patches/")); + + // version.json -> patches/*.json -> custom.json + + // version.json + { + QLOG_INFO() << "Reading version.json"; + QJsonObject obj; + if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) + { + return false; + } + if (!apply(obj)) + { + return false; + } + } + + // patches/ + { + // load all, put into map for ordering, apply in the right order + + QMap objects; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + { + QLOG_INFO() << "Reading" << info.fileName(); + QJsonObject obj; + if (!read(info, &obj)) + { + return false; + } + if (!obj.contains("order") || !obj.value("order").isDouble()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath())); + return false; + } + objects.insert(obj.value("order").toDouble(), obj); + } + for (auto object : objects.values()) + { + qDebug() << "Applying object with order" << objects.key(object); + if (!apply(object)) + { + return false; + } + } + } + + // custom.json + { + if (QFile::exists(root.absoluteFilePath("custom.json"))) + { + QLOG_INFO() << "Reading custom.json"; + QJsonObject obj; + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) + { + return false; + } + if (!apply(obj)) + { + return false; + } + } + } + + return true; +} + +bool OneSixVersionBuilder::read(const QJsonObject &obj) +{ + m_version->clear(); + + return apply(obj); +} + +void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) +{ + if (obj.contains(key) && obj.value(key).isString()) + { + out = obj.value(key).toString(); + } + else if (!onlyOverride) + { + if (obj.contains("+" + key) && obj.value("+" + key).isString()) + { + out += obj.value("+" + key).toString(); + } + else if (obj.contains("-" + key) && obj.value("-" + key).isString()) + { + out.remove(obj.value("-" + key).toString()); + } + } +} +void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(OneSixLibrary::*func)(const QString &val)) +{ + if (obj.contains(key) && obj.value(key).isString()) + { + (lib.get()->*func)(obj.value(key).toString()); + } +} +bool OneSixVersionBuilder::apply(const QJsonObject &object) +{ + applyString(object, "id", m_version->id); + applyString(object, "mainClass", m_version->mainClass); + applyString(object, "minecraftArguments", m_version->minecraftArguments, false); + applyString(object, "processArguments", m_version->processArguments, false); + if (m_version->minecraftArguments.isEmpty()) + { + const QString toCompare = m_version->processArguments.toLower(); + if (toCompare == "legacy") + { + m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; + } + else if (toCompare == "username_session") + { + m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; + } + else if (toCompare == "username_session_version") + { + m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; + } + } + applyString(object, "type", m_version->type); + applyString(object, "releaseTime", m_version->releaseTime); + applyString(object, "time", m_version->time); + applyString(object, "assets", m_version->assets); + { + if (m_version->assets.isEmpty()) + { + m_version->assets = "legacy"; + } + } + if (object.contains("minimumLauncherVersion")) + { + auto minLauncherVersionVal = object.value("minimumLauncherVersion"); + if (minLauncherVersionVal.isDouble()) + { + m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble(); + } + } + + // libraries + if (object.contains("libraries")) + { + m_version->libraries.clear(); + auto librariesValue = object.value("libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } + auto array = librariesValue.toArray(); + for (auto libVal : array) + { + if (libVal.isObject()) + { + if (!applyLibrary(libVal.toObject(), Override)) + { + return false; + } + } + + } + } + + // +libraries + if (object.contains("+libraries")) + { + auto librariesValue = object.value("+libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } + for (auto libVal : librariesValue.toArray()) + { + if (libVal.isObject()) + { + applyLibrary(libVal.toObject(), Add); + } + + } + } + + // -libraries + if (object.contains("-libraries")) + { + auto librariesValue = object.value("-libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } + for (auto libVal : librariesValue.toArray()) + { + if (libVal.isObject()) + { + applyLibrary(libVal.toObject(), Remove); + } + + } + } + + return true; +} + +int findLibrary(QList > haystack, const QString &needle) +{ + for (int i = 0; i < haystack.size(); ++i) + { + if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(haystack.at(i)->rawName()) != -1) + { + return i; + } + } + return -1; +} + +bool OneSixVersionBuilder::applyLibrary(const QJsonObject &lib, const OneSixVersionBuilder::Type type) +{ + // Library name + auto nameVal = lib.value("name"); + if (!nameVal.isString()) + { + return false; + } + auto name = nameVal.toString(); + + if (type == Remove) + { + int index = findLibrary(m_version->libraries, name); + if (index >= 0) + { + m_version->libraries.removeAt(index); + } + return true; + } + + if (type == Add && !lib.contains("insert")) + { + return false; + } + + std::shared_ptr library; + + if (lib.value("insert").toString() != "apply" && type == Add) + { + QMutableListIterator > it(m_version->libraries); + while (it.hasNext()) + { + if (it.next()->rawName() == name) + { + it.remove(); + } + } + } + + if (lib.value("insert").toString() == "apply" && type == Add) + { + library = m_version->libraries[findLibrary(m_version->libraries, name)]; + } + else + { + library.reset(new OneSixLibrary(nameVal.toString())); + } + + applyString(lib, "url", library, &OneSixLibrary::setBaseUrl); + applyString(lib, "MMC-hint", library, &OneSixLibrary::setHint); + applyString(lib, "MMC-absulute_url", library, &OneSixLibrary::setAbsoluteUrl); + applyString(lib, "MMC-absoluteUrl", library, &OneSixLibrary::setAbsoluteUrl); + + auto extractVal = lib.value("extract"); + if (extractVal.isObject()) + { + QStringList excludes; + auto extractObj = extractVal.toObject(); + auto excludesVal = extractObj.value("exclude"); + if (excludesVal.isArray()) + { + auto excludesList = excludesVal.toArray(); + for (auto excludeVal : excludesList) + { + if (excludeVal.isString()) + { + excludes.append(excludeVal.toString()); + } + } + library->extract_excludes = excludes; + } + } + + auto nativesVal = lib.value("natives"); + if (nativesVal.isObject()) + { + library->setIsNative(); + auto nativesObj = nativesVal.toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + auto osType = OpSys_fromString(it.key()); + if (osType == Os_Other) + { + continue; + } + if (!it.value().isString()) + { + continue; + } + library->addNative(osType, it.value().toString()); + } + } + + if (lib.contains("rules")) + { + library->setRules(rulesFromJsonV4(lib)); + } + library->finalize(); + if (type == Override) + { + m_version->libraries.append(library); + } + else if (lib.value("insert").toString() != "apply") + { + if (lib.value("insert").toString() == "append") + { + m_version->libraries.append(library); + } + else if (lib.value("insert").toString() == "prepend") + { + m_version->libraries.prepend(library); + } + else if (lib.value("insert").isObject()) + { + QJsonObject insertObj = lib.value("insert").toObject(); + if (insertObj.isEmpty()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty")); + return false; + } + const QString key = insertObj.keys().first(); + const QString value = insertObj.value(key).toString(); + const int index = findLibrary(m_version->libraries, value); + if (index >= 0) + { + if (key == "before") + { + m_version->libraries.insert(index, library); + } + else if (key == "after") + { + m_version->libraries.insert(index + 1, library); + } + else + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); + return false; + } + } + } + else + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); + return false; + } + } + return true; +} + +bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) +{ + QFile file(fileInfo.absoluteFilePath()); + if (!file.open(QFile::ReadOnly)) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); + return false; + } + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); + if (error.error != QJsonParseError::NoError) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to parse %1: %2 at %3").arg(file.fileName(), error.errorString()).arg(error.offset)); + return false; + } + *out = doc.object(); + return true; +} diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h new file mode 100644 index 00000000..a3ba331c --- /dev/null +++ b/logic/OneSixVersionBuilder.h @@ -0,0 +1,53 @@ +/* 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 + +class OneSixVersion; +class OneSixInstance; +class QWidget; +class QJsonObject; +class QFileInfo; + +class OneSixVersionBuilder +{ + OneSixVersionBuilder(); +public: + static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent); + static bool read(OneSixVersion *version, const QJsonObject &obj); + +private: + OneSixVersion *m_version; + OneSixInstance *m_instance; + QWidget *m_widgetParent; + + enum Type + { + Override, + Add, + Remove + }; + + bool build(); + bool read(const QJsonObject &obj); + + void clear(); + bool apply(const QJsonObject &object); + bool applyLibrary(const QJsonObject &lib, const Type type); + + bool read(const QFileInfo &fileInfo, QJsonObject *out); +}; diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 29894b5a..91f86df0 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -234,18 +234,18 @@ void MCVListLoadTask::list_downloaded() } // Parse the type. MinecraftVersion::VersionType versionType; - // Derp or Legacy. use filter to determine type + // OneSix or Legacy. use filter to determine type if (versionTypeStr == "release") { versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy - : MinecraftVersion::Derp; + : 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::Derp; + : MinecraftVersion::OneSix; is_latest = (versionID == latestSnapshotID); is_snapshot = true; } -- cgit v1.2.3 From 0f7b38c76bacc210beae21c4e23cc312f53fa5e4 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Fri, 24 Jan 2014 18:17:26 +0100 Subject: Fix some stuff --- logic/InstanceFactory.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'logic') diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 730bcd69..807bccd0 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -51,7 +51,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst QString inst_type = m_settings->get("InstanceType").toString(); // FIXME: replace with a map lookup, where instance classes register their types - if (inst_type == "OneSix" || inst_type == "OneSix") + if (inst_type == "OneSix") { inst = new OneSixInstance(instDir, m_settings, this); } @@ -67,7 +67,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new LegacyFTBInstance(instDir, m_settings, this); } - else if (inst_type == "OneSixFTB" || inst_type == "OneSixFTB") + else if (inst_type == "OneSixFTB") { inst = new OneSixFTBInstance(instDir, m_settings, this); } @@ -102,7 +102,7 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& switch (mcVer->type) { case MinecraftVersion::Legacy: - // TODO OneSix + // TODO new instance type m_settings->set("InstanceType", "Legacy"); inst = new LegacyInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); @@ -176,7 +176,7 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); - if(inst_type == "OneSixFTB" || inst_type == "OneSixFTB") + if(inst_type == "OneSixFTB") m_settings->set("InstanceType", "OneSix"); if(inst_type == "LegacyFTB") m_settings->set("InstanceType", "Legacy"); -- cgit v1.2.3 From f9ea3dbfdea0db5626a6afadcfd599a5f53d8abd Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 27 Jan 2014 19:20:07 +0100 Subject: Split parsing/applying. Better error logging. Fix crash. --- logic/ForgeInstaller.cpp | 49 +- logic/OneSixFTBInstance.cpp | 2 +- logic/OneSixInstance.cpp | 30 +- logic/OneSixInstance.h | 8 +- logic/OneSixInstance_p.h | 1 + logic/OneSixLibrary.cpp | 14 +- logic/OneSixLibrary.h | 2 + logic/OneSixUpdate.cpp | 4 +- logic/OneSixVersion.cpp | 5 +- logic/OneSixVersion.h | 6 +- logic/OneSixVersionBuilder.cpp | 1002 ++++++++++++++++++++++++++++------------ logic/OneSixVersionBuilder.h | 18 +- 12 files changed, 814 insertions(+), 327 deletions(-) (limited to 'logic') diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 863c3dfd..c0340e95 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "MultiMC.h" #include "OneSixInstance.h" @@ -146,36 +148,71 @@ bool ForgeInstaller::add(OneSixInstance *to) QJsonObject libObj = lib->toJson(); bool found = false; + bool equals = false; // find an entry that matches this one - for (auto tolib : to->getFullVersion()->libraries) + for (auto tolib : to->getNonCustomVersion()->libraries) { if (tolib->name() != libName) continue; found = true; + if (tolib->toJson() == libObj) + { + equals = true; + } // replace lib libObj.insert("insert", QString("apply")); break; } + if (equals) + { + continue; + } if (!found) { // add lib QJsonObject insertObj; - insertObj.insert("before", to->getFullVersion()->libraries.at(sliding_insert_window)->rawName()); + insertObj.insert( + "before", + to->getFullVersion()->libraries.at(sliding_insert_window + 1)->rawName()); libObj.insert("insert", insertObj); sliding_insert_window++; } - librariesPlus.append(libObj); + librariesPlus.prepend(libObj); } obj.insert("+libraries", librariesPlus); obj.insert("mainClass", m_forge_version->mainClass); - obj.insert("minecraftArguments", m_forge_version->minecraftArguments); - obj.insert("processArguments", m_forge_version->processArguments); + QString args = m_forge_version->minecraftArguments; + QStringList tweakers; + { + QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)"); + QRegularExpressionMatch match = expression.match(args); + while (match.hasMatch()) + { + tweakers.append(match.captured(1)); + args.remove(match.capturedStart(), match.capturedLength()); + match = expression.match(args); + } + } + if (!args.isEmpty() && args != to->getNonCustomVersion()->minecraftArguments) + { + obj.insert("minecraftArguments", args); + } + if (!tweakers.isEmpty()) + { + obj.insert("+tweakers", QJsonArray::fromStringList(tweakers)); + } + if (!m_forge_version->processArguments.isEmpty() && + m_forge_version->processArguments != to->getNonCustomVersion()->processArguments) + { + obj.insert("processArguments", m_forge_version->processArguments); + } } QFile file(filename(to->instanceRoot())); if (!file.open(QFile::WriteOnly)) { - QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); + QLOG_ERROR() << "Error opening" << file.fileName() + << "for reading:" << file.errorString(); return false; } file.write(QJsonDocument(obj).toJson()); diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 71bc1470..54bfbe2b 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -55,7 +55,7 @@ slots: setStatus(tr("Installing Forge...")); QString forgePath = entry->getFullPath(); ForgeInstaller forge(forgePath, forgeVersion->universal_url); - if (!instance->reloadFullVersion()) + if (!instance->reloadVersion()) { emitFailed(tr("Couldn't load the version config")); return; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index c43a5187..19d5e112 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -35,13 +35,14 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); d->version.reset(new OneSixVersion(this, this)); + d->nonCustomVersion.reset(new OneSixVersion(this, this)); if (QDir(instanceRoot()).exists("version.json")) { - reloadFullVersion(); + reloadVersion(); } else { - clearFullVersion(); + clearVersion(); } } @@ -139,6 +140,10 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) I_D(OneSixInstance); auto version = d->version; QString args_pattern = version->minecraftArguments; + for (auto tweaker : version->tweakers) + { + args_pattern += " --tweakClass " + tweaker; + } QMap token_mapping; // yggdrasil! @@ -287,7 +292,7 @@ bool OneSixInstance::setIntendedVersionId(QString version) settings().set("IntendedVersion", version); setShouldUpdate(true); QFile::remove(PathCombine(instanceRoot(), "version.json")); - clearFullVersion(); + clearVersion(); return true; } @@ -323,28 +328,39 @@ QString OneSixInstance::currentVersionId() const return intendedVersionId(); } -bool OneSixInstance::reloadFullVersion(QWidget *widgetParent) +bool OneSixInstance::reloadVersion(QWidget *widgetParent) { I_D(OneSixInstance); bool ret = d->version->reload(widgetParent); + if (ret) + { + ret = d->nonCustomVersion->reload(widgetParent, true); + } emit versionReloaded(); return ret; } -void OneSixInstance::clearFullVersion() +void OneSixInstance::clearVersion() { I_D(OneSixInstance); d->version->clear(); + d->nonCustomVersion->clear(); emit versionReloaded(); } -std::shared_ptr OneSixInstance::getFullVersion() +std::shared_ptr OneSixInstance::getFullVersion() const { - I_D(OneSixInstance); + I_D(const OneSixInstance); return d->version; } +std::shared_ptr OneSixInstance::getNonCustomVersion() const +{ + I_D(const OneSixInstance); + return d->nonCustomVersion; +} + QString OneSixInstance::defaultBaseJar() const { return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 94577828..ee64e886 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -52,11 +52,13 @@ public: virtual QDialog *createModEditDialog(QWidget *parent) override; /// reload the full version json files. return true on success! - bool reloadFullVersion(QWidget *widgetParent = 0); + bool reloadVersion(QWidget *widgetParent = 0); /// clears all version information in preparation for an update - void clearFullVersion(); + void clearVersion(); /// get the current full version info - std::shared_ptr getFullVersion(); + std::shared_ptr getFullVersion() const; + /// gets the current version info, excluding custom.json + std::shared_ptr getNonCustomVersion() const; /// is the current version original, or custom? virtual bool versionIsCustom() override; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index a2b70ac9..6dc74e50 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -22,6 +22,7 @@ struct OneSixInstancePrivate : public BaseInstancePrivate { std::shared_ptr version; + std::shared_ptr nonCustomVersion; std::shared_ptr loader_mod_list; std::shared_ptr resource_pack_list; }; diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 4d892d53..2b1f0600 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -93,6 +93,10 @@ void OneSixLibrary::addNative(OpSys os, const QString &suffix) m_is_native = true; m_native_suffixes[os] = suffix; } +void OneSixLibrary::clearSuffixes() +{ + m_native_suffixes.clear(); +} void OneSixLibrary::setRules(QList> rules) { m_rules = rules; @@ -176,7 +180,7 @@ bool OneSixLibrary::extractTo(QString target_dir) cooked_storage.replace("${arch}", "32"); QString origin = PathCombine("libraries", cooked_storage); QString target_dir_cooked = PathCombine(target_dir, "32"); - if(!ensureFolderPathExists(target_dir_cooked)) + if (!ensureFolderPathExists(target_dir_cooked)) { QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; return false; @@ -191,7 +195,7 @@ bool OneSixLibrary::extractTo(QString target_dir) cooked_storage.replace("${arch}", "64"); origin = PathCombine("libraries", cooked_storage); target_dir_cooked = PathCombine(target_dir, "64"); - if(!ensureFolderPathExists(target_dir_cooked)) + if (!ensureFolderPathExists(target_dir_cooked)) { QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; return false; @@ -205,7 +209,7 @@ bool OneSixLibrary::extractTo(QString target_dir) } else { - if(!ensureFolderPathExists(target_dir)) + if (!ensureFolderPathExists(target_dir)) { QLOG_ERROR() << "Couldn't create folder " + target_dir; return false; @@ -230,8 +234,10 @@ QJsonObject OneSixLibrary::toJson() libRoot.insert("MMC-hint", m_hint); if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - m_base_url != "https://" + URLConstants::LIBRARY_BASE) + m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty()) + { libRoot.insert("url", m_base_url); + } if (isNative() && m_native_suffixes.size()) { QJsonObject nativeList; diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 52f89969..5384015a 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -107,6 +107,8 @@ public: void setIsNative(); /// Attach a name suffix to the specified OS native void addNative(OpSys os, const QString &suffix); + /// Clears all suffixes + void clearSuffixes(); /// Set the load rules void setRules(QList> rules); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index f51b6c23..c056bc4c 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -137,7 +137,7 @@ void OneSixUpdate::versionFileFinished() { finfo.remove(); } - inst->reloadFullVersion(); + inst->reloadVersion(); jarlibStart(); } @@ -235,7 +235,7 @@ void OneSixUpdate::jarlibStart() setStatus(tr("Getting the library files from Mojang...")); QLOG_INFO() << m_inst->name() << ": downloading libraries"; OneSixInstance *inst = (OneSixInstance *)m_inst; - bool successful = inst->reloadFullVersion(); + bool successful = inst->reloadVersion(); if (!successful) { emitFailed("Failed to load the version description file. It might be " diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 3db3bd77..1b6bc9cb 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -25,9 +25,9 @@ OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) clear(); } -bool OneSixVersion::reload(QWidget *widgetParent) +bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom) { - return OneSixVersionBuilder::build(this, m_instance, widgetParent); + return OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); } void OneSixVersion::clear() @@ -42,6 +42,7 @@ void OneSixVersion::clear() minimumLauncherVersion = 0xDEADBEAF; mainClass.clear(); libraries.clear(); + tweakers.clear(); } void OneSixVersion::dump() const diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 00afd010..767e05db 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -38,7 +38,7 @@ public: virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; - bool reload(QWidget *widgetParent); + bool reload(QWidget *widgetParent, const bool excludeCustom = false); void clear(); void dump() const; @@ -78,6 +78,10 @@ public: * writing) */ int minimumLauncherVersion = 0xDEADBEEF; + /** + * A list of all tweaker classes + */ + QStringList tweakers; /** * The main class to load first */ diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 85f55a52..c031a94a 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -31,416 +31,844 @@ #include "OneSixRule.h" #include "logger/QsLog.h" -OneSixVersionBuilder::OneSixVersionBuilder() -{ - -} - -bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent) +struct VersionFile { - OneSixVersionBuilder builder; - builder.m_version = version; - builder.m_instance = instance; - builder.m_widgetParent = widgetParent; - return builder.build(); -} - -bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) -{ - OneSixVersionBuilder builder; - builder.m_version = version; - builder.m_instance = 0; - builder.m_widgetParent = 0; - return builder.read(obj); -} - -bool OneSixVersionBuilder::build() -{ - m_version->clear(); - - QDir root(m_instance->instanceRoot()); - QDir patches(root.absoluteFilePath("patches/")); - - // version.json -> patches/*.json -> custom.json + int order; + QString id; + QString mainClass; + QString overwriteMinecraftArguments; + QString addMinecraftArguments; + QString removeMinecraftArguments; + QString processArguments; + QString type; + QString releaseTime; + QString time; + QString assets; + int minimumLauncherVersion = -1; + + bool shouldOverwriteTweakers = false; + QStringList overwriteTweakers; + QStringList addTweakers; + QStringList removeTweakers; + + struct Library + { + QString name; + QString url; + QString hint; + QString absoluteUrl; + bool applyExcludes = false; + QStringList excludes; + bool applyNatives = false; + QList> natives; + bool applyRules = false; + QList> rules; - // version.json + // user for '+' libraries + enum InsertType + { + Apply, + Append, + Prepend, + InsertBefore, + InsertAfter, + Replace + }; + InsertType insertType; + QString insertData; + }; + bool shouldOverwriteLibs = false; + QList overwriteLibs; + QList addLibs; + QList removeLibs; + + static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename, + bool &isError) { - QLOG_INFO() << "Reading version.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) + isError = true; + Library out; + if (!libObj.contains("name")) { - return false; + QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field"; + return out; } - if (!apply(obj)) - { - return false; - } - } + out.name = libObj.value("name").toString(); - // patches/ - { - // load all, put into map for ordering, apply in the right order + auto readString = [libObj, filename](const QString &key, QString &variable) + { + if (libObj.contains(key)) + { + QJsonValue val = libObj.value(key); + if (!val.isString()) + { + QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; + } + else + { + variable = val.toString(); + } + } + }; - QMap objects; - for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + readString("url", out.url); + readString("MMC-hint", out.hint); + readString("MMC-absulute_url", out.absoluteUrl); + readString("MMC-absoluteUrl", out.absoluteUrl); + if (libObj.contains("extract")) { - QLOG_INFO() << "Reading" << info.fileName(); - QJsonObject obj; - if (!read(info, &obj)) + if (!libObj.value("extract").isObject()) { - return false; + QLOG_ERROR() + << filename + << "contains a library with an 'extract' field that's not an object"; + return out; } - if (!obj.contains("order") || !obj.value("order").isDouble()) + QJsonObject extractObj = libObj.value("extract").toObject(); + if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray()) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath())); - return false; + QLOG_ERROR() << filename + << "contains a library with an invalid 'extract' field"; + return out; } - objects.insert(obj.value("order").toDouble(), obj); - } - for (auto object : objects.values()) - { - qDebug() << "Applying object with order" << objects.key(object); - if (!apply(object)) + out.applyExcludes = true; + QJsonArray excludeArray = extractObj.value("exclude").toArray(); + for (auto excludeVal : excludeArray) { - return false; + if (!excludeVal.isString()) + { + QLOG_WARN() << filename << "contains a library that contains an 'extract' " + "field that contains an invalid 'exclude' entry " + "(skipping)"; + } + else + { + out.excludes.append(excludeVal.toString()); + } } } - } - - // custom.json - { - if (QFile::exists(root.absoluteFilePath("custom.json"))) + if (libObj.contains("natives")) { - QLOG_INFO() << "Reading custom.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) + if (!libObj.value("natives").isObject()) { - return false; + QLOG_ERROR() + << filename + << "contains a library with a 'natives' field that's not an object"; + return out; } - if (!apply(obj)) + out.applyNatives = true; + QJsonObject nativesObj = libObj.value("natives").toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) { - return false; + if (!it.value().isString()) + { + QLOG_WARN() << filename << "contains an invalid native (skipping)"; + } + OpSys opSys = OpSys_fromString(it.key()); + if (opSys != Os_Other) + { + out.natives.append(qMakePair(opSys, it.value().toString())); + } } } + if (libObj.contains("rules")) + { + out.applyRules = true; + out.rules = rulesFromJsonV4(libObj); + } + isError = false; + return out; } - - return true; -} - -bool OneSixVersionBuilder::read(const QJsonObject &obj) -{ - m_version->clear(); - - return apply(obj); -} - -void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) -{ - if (obj.contains(key) && obj.value(key).isString()) - { - out = obj.value(key).toString(); - } - else if (!onlyOverride) + static VersionFile fromJson(const QJsonDocument &doc, const QString &filename, + const bool requireOrder, bool &isError) { - if (obj.contains("+" + key) && obj.value("+" + key).isString()) + VersionFile out; + isError = true; + if (doc.isEmpty() || doc.isNull()) { - out += obj.value("+" + key).toString(); + QLOG_ERROR() << filename << "is empty or null"; + return out; } - else if (obj.contains("-" + key) && obj.value("-" + key).isString()) + if (!doc.isObject()) { - out.remove(obj.value("-" + key).toString()); + QLOG_ERROR() << "The root of" << filename << "is not an object"; + return out; } - } -} -void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(OneSixLibrary::*func)(const QString &val)) -{ - if (obj.contains(key) && obj.value(key).isString()) - { - (lib.get()->*func)(obj.value(key).toString()); - } -} -bool OneSixVersionBuilder::apply(const QJsonObject &object) -{ - applyString(object, "id", m_version->id); - applyString(object, "mainClass", m_version->mainClass); - applyString(object, "minecraftArguments", m_version->minecraftArguments, false); - applyString(object, "processArguments", m_version->processArguments, false); - if (m_version->minecraftArguments.isEmpty()) - { - const QString toCompare = m_version->processArguments.toLower(); - if (toCompare == "legacy") + + QJsonObject root = doc.object(); + + if (requireOrder) { - m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; + if (root.contains("order")) + { + if (root.value("order").isDouble()) + { + out.order = root.value("order").toDouble(); + } + else + { + QLOG_ERROR() << "'order' field contains an invalid value in" << filename; + return out; + } + } + else + { + QLOG_ERROR() << filename << "doesn't contain an order field"; + } } - else if (toCompare == "username_session") + + auto readString = [root, filename](const QString &key, QString &variable) + { + if (root.contains(key)) + { + QJsonValue val = root.value(key); + if (!val.isString()) + { + QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; + } + else + { + variable = val.toString(); + } + } + }; + + readString("id", out.id); + readString("mainClass", out.mainClass); + readString("processArguments", out.processArguments); + readString("minecraftArguments", out.overwriteMinecraftArguments); + readString("+minecraftArguments", out.addMinecraftArguments); + readString("-minecraftArguments", out.removeMinecraftArguments); + readString("type", out.type); + readString("releaseTime", out.releaseTime); + readString("time", out.time); + readString("assets", out.assets); + if (root.contains("minimumLauncherVersion")) { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; + QJsonValue val = root.value("minimumLauncherVersion"); + if (!val.isDouble()) + { + QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename + << "(skipping)"; + } + else + { + out.minimumLauncherVersion = val.toDouble(); + } } - else if (toCompare == "username_session_version") + + if (root.contains("tweakers")) { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; + QJsonValue tweakersVal = root.value("tweakers"); + if (!tweakersVal.isArray()) + { + QLOG_ERROR() << filename << "contains a 'tweakers' field, but it's not an array"; + return out; + } + out.shouldOverwriteTweakers = true; + QJsonArray tweakers = root.value("tweakers").toArray(); + for (auto tweakerVal : tweakers) + { + if (!tweakerVal.isString()) + { + QLOG_ERROR() << filename << "contains a 'tweakers' field entry that's not a string"; + return out; + } + out.overwriteTweakers.append(tweakerVal.toString()); + } } - } - applyString(object, "type", m_version->type); - applyString(object, "releaseTime", m_version->releaseTime); - applyString(object, "time", m_version->time); - applyString(object, "assets", m_version->assets); - { - if (m_version->assets.isEmpty()) + if (root.contains("+tweakers")) { - m_version->assets = "legacy"; + QJsonValue tweakersVal = root.value("+tweakers"); + if (!tweakersVal.isArray()) + { + QLOG_ERROR() << filename << "contains a '+tweakers' field, but it's not an array"; + return out; + } + QJsonArray tweakers = root.value("+tweakers").toArray(); + for (auto tweakerVal : tweakers) + { + if (!tweakerVal.isString()) + { + QLOG_ERROR() << filename << "contains a '+tweakers' field entry that's not a string"; + return out; + } + out.addTweakers.append(tweakerVal.toString()); + } } - } - if (object.contains("minimumLauncherVersion")) - { - auto minLauncherVersionVal = object.value("minimumLauncherVersion"); - if (minLauncherVersionVal.isDouble()) + if (root.contains("-tweakers")) { - m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble(); + QJsonValue tweakersVal = root.value("-tweakers"); + if (!tweakersVal.isArray()) + { + QLOG_ERROR() << filename << "contains a '-tweakers' field, but it's not an array"; + return out; + } + out.shouldOverwriteTweakers = true; + QJsonArray tweakers = root.value("-tweakers").toArray(); + for (auto tweakerVal : tweakers) + { + if (!tweakerVal.isString()) + { + QLOG_ERROR() << filename << "contains a '-tweakers' field entry that's not a string"; + return out; + } + out.removeTweakers.append(tweakerVal.toString()); + } } - } - // libraries - if (object.contains("libraries")) - { - m_version->libraries.clear(); - auto librariesValue = object.value("libraries"); - if (!librariesValue.isArray()) + if (root.contains("libraries")) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; + out.shouldOverwriteLibs = true; + QJsonValue librariesVal = root.value("libraries"); + if (!librariesVal.isArray()) + { + QLOG_ERROR() << filename + << "contains a 'libraries' field, but its not an array"; + return out; + } + QJsonArray librariesArray = librariesVal.toArray(); + for (auto libVal : librariesArray) + { + if (!libVal.isObject()) + { + QLOG_ERROR() << filename << "contains a library that's not an object"; + return out; + } + QJsonObject libObj = libVal.toObject(); + bool error; + Library lib = fromLibraryJson(libObj, filename, error); + if (error) + { + QLOG_ERROR() << "Error while reading a library entry in" << filename; + return out; + } + out.overwriteLibs.append(lib); + } } - auto array = librariesValue.toArray(); - for (auto libVal : array) + if (root.contains("+libraries")) { - if (libVal.isObject()) + QJsonValue librariesVal = root.value("+libraries"); + if (!librariesVal.isArray()) { - if (!applyLibrary(libVal.toObject(), Override)) + QLOG_ERROR() << filename + << "contains a '+libraries' field, but its not an array"; + return out; + } + QJsonArray librariesArray = librariesVal.toArray(); + for (auto libVal : librariesArray) + { + if (!libVal.isObject()) + { + QLOG_ERROR() << filename << "contains a library that's not an object"; + return out; + } + QJsonObject libObj = libVal.toObject(); + bool error; + Library lib = fromLibraryJson(libObj, filename, error); + if (error) + { + QLOG_ERROR() << "Error while reading a library entry in" << filename; + return out; + } + if (!libObj.contains("insert")) { - return false; + QLOG_ERROR() << "Missing 'insert' field in '+libraries' field in" + << filename; + return out; } + QJsonValue insertVal = libObj.value("insert"); + QString insertString; + { + if (insertVal.isString()) + { + insertString = insertVal.toString(); + } + else if (insertVal.isObject()) + { + QJsonObject insertObj = insertVal.toObject(); + if (insertObj.isEmpty()) + { + QLOG_ERROR() << "One library has an empty insert object in" + << filename; + return out; + } + insertString = insertObj.keys().first(); + lib.insertData = insertObj.value(insertString).toString(); + } + } + if (insertString == "apply") + { + lib.insertType = Library::Apply; + } + else if (insertString == "append") + { + lib.insertType = Library::Append; + } + else if (insertString == "prepend") + { + lib.insertType = Library::Prepend; + } + else if (insertString == "before") + { + lib.insertType = Library::InsertBefore; + } + else if (insertString == "after") + { + lib.insertType = Library::InsertAfter; + } + else if (insertString == "replace") + { + lib.insertType = Library::Replace; + } + else + { + QLOG_ERROR() << "A '+' library in" << filename + << "contains an invalid insert type"; + return out; + } + out.addLibs.append(lib); } - } + if (root.contains("-libraries")) + { + QJsonValue librariesVal = root.value("-libraries"); + if (!librariesVal.isArray()) + { + QLOG_ERROR() << filename + << "contains a '-libraries' field, but its not an array"; + return out; + } + QJsonArray librariesArray = librariesVal.toArray(); + for (auto libVal : librariesArray) + { + if (!libVal.isObject()) + { + QLOG_ERROR() << filename << "contains a library that's not an object"; + return out; + } + QJsonObject libObj = libVal.toObject(); + if (!libObj.contains("name")) + { + QLOG_ERROR() << filename << "contains a library without a name"; + return out; + } + if (!libObj.value("name").isString()) + { + QLOG_ERROR() << filename + << "contains a library without a valid 'name' field"; + return out; + } + out.removeLibs.append(libObj.value("name").toString()); + } + } + + isError = false; + return out; } - // +libraries - if (object.contains("+libraries")) + static std::shared_ptr createLibrary(const Library &lib) { - auto librariesValue = object.value("+libraries"); - if (!librariesValue.isArray()) + std::shared_ptr out(new OneSixLibrary(lib.name)); + out->setBaseUrl(lib.url); + out->setHint(lib.hint); + out->setAbsoluteUrl(lib.absoluteUrl); + out->extract_excludes = lib.excludes; + for (auto native : lib.natives) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; + out->addNative(native.first, native.second); } - for (auto libVal : librariesValue.toArray()) + out->setRules(lib.rules); + out->finalize(); + return out; + } + int findLibrary(QList> haystack, const QString &needle) + { + for (int i = 0; i < haystack.size(); ++i) { - if (libVal.isObject()) + if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix) + .indexIn(haystack.at(i)->rawName()) != -1) { - applyLibrary(libVal.toObject(), Add); + return i; } - } + return -1; } - - // -libraries - if (object.contains("-libraries")) + void applyTo(OneSixVersion *version, bool &isError) { - auto librariesValue = object.value("-libraries"); - if (!librariesValue.isArray()) + isError = true; + if (!id.isNull()) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; + version->id = id; + } + if (!mainClass.isNull()) + { + version->mainClass = mainClass; + } + if (!processArguments.isNull()) + { + version->processArguments = processArguments; + } + if (!type.isNull()) + { + version->type = type; + } + if (!releaseTime.isNull()) + { + version->releaseTime = releaseTime; + } + if (!time.isNull()) + { + version->time = time; + } + if (!assets.isNull()) + { + version->assets = assets; + } + if (minimumLauncherVersion >= 0) + { + version->minimumLauncherVersion = minimumLauncherVersion; + } + if (!overwriteMinecraftArguments.isNull()) + { + version->minecraftArguments = overwriteMinecraftArguments; + } + if (!addMinecraftArguments.isNull()) + { + version->minecraftArguments += addMinecraftArguments; } - for (auto libVal : librariesValue.toArray()) + if (!removeMinecraftArguments.isNull()) { - if (libVal.isObject()) + version->minecraftArguments.remove(removeMinecraftArguments); + } + if (shouldOverwriteTweakers) + { + version->tweakers = overwriteTweakers; + } + for (auto tweaker : addTweakers) + { + version->tweakers += tweaker; + } + for (auto tweaker : removeTweakers) + { + version->tweakers.removeAll(tweaker); + } + if (shouldOverwriteLibs) + { + version->libraries.clear(); + for (auto lib : overwriteLibs) + { + version->libraries.append(createLibrary(lib)); + } + } + for (auto lib : addLibs) + { + switch (lib.insertType) + { + case Library::Apply: + { + + int index = findLibrary(version->libraries, lib.name); + if (index >= 0) + { + auto library = version->libraries[index]; + if (!lib.url.isNull()) + { + library->setBaseUrl(lib.url); + } + if (!lib.hint.isNull()) + { + library->setHint(lib.hint); + } + if (!lib.absoluteUrl.isNull()) + { + library->setAbsoluteUrl(lib.absoluteUrl); + } + if (lib.applyExcludes) + { + library->extract_excludes = lib.excludes; + } + if (lib.applyNatives) + { + library->clearSuffixes(); + for (auto native : lib.natives) + { + library->addNative(native.first, native.second); + } + } + if (lib.applyRules) + { + library->setRules(lib.rules); + } + library->finalize(); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + case Library::Append: + version->libraries.append(createLibrary(lib)); + break; + case Library::Prepend: + version->libraries.prepend(createLibrary(lib)); + break; + case Library::InsertBefore: { - applyLibrary(libVal.toObject(), Remove); + + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.insert(index, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; } + case Library::InsertAfter: + { + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.insert(index + 1, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + case Library::Replace: + { + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.replace(index, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + } + } + for (auto lib : removeLibs) + { + int index = findLibrary(version->libraries, lib); + if (index >= 0) + { + version->libraries.removeAt(index); + } + else + { + QLOG_WARN() << "Couldn't find" << lib << "(skipping)"; + } } + + isError = false; } +}; - return true; +OneSixVersionBuilder::OneSixVersionBuilder() +{ } -int findLibrary(QList > haystack, const QString &needle) +bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, + QWidget *widgetParent, const bool excludeCustom) { - for (int i = 0; i < haystack.size(); ++i) - { - if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(haystack.at(i)->rawName()) != -1) - { - return i; - } - } - return -1; + OneSixVersionBuilder builder; + builder.m_version = version; + builder.m_instance = instance; + builder.m_widgetParent = widgetParent; + return builder.build(excludeCustom); } -bool OneSixVersionBuilder::applyLibrary(const QJsonObject &lib, const OneSixVersionBuilder::Type type) +bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) { - // Library name - auto nameVal = lib.value("name"); - if (!nameVal.isString()) - { - return false; - } - auto name = nameVal.toString(); + OneSixVersionBuilder builder; + builder.m_version = version; + builder.m_instance = 0; + builder.m_widgetParent = 0; + return builder.read(obj); +} - if (type == Remove) +bool OneSixVersionBuilder::build(const bool excludeCustom) +{ + m_version->clear(); + + QDir root(m_instance->instanceRoot()); + QDir patches(root.absoluteFilePath("patches/")); + + // version.json -> patches/*.json -> custom.json + + // version.json { - int index = findLibrary(m_version->libraries, name); - if (index >= 0) + QLOG_INFO() << "Reading version.json"; + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) + { + return false; + } + bool isError = false; + file.applyTo(m_version, isError); + if (isError) { - m_version->libraries.removeAt(index); + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("version.json"))); + return false; } - return true; } - if (type == Add && !lib.contains("insert")) + // patches/ { - return false; - } - - std::shared_ptr library; + // load all, put into map for ordering, apply in the right order - if (lib.value("insert").toString() != "apply" && type == Add) - { - QMutableListIterator > it(m_version->libraries); - while (it.hasNext()) + QMap> files; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { - if (it.next()->rawName() == name) + QLOG_INFO() << "Reading" << info.fileName(); + VersionFile file; + if (!read(info, true, &file)) { - it.remove(); + return false; } + files.insert(file.order, qMakePair(info.fileName(), file)); } - } - - if (lib.value("insert").toString() == "apply" && type == Add) - { - library = m_version->libraries[findLibrary(m_version->libraries, name)]; - } - else - { - library.reset(new OneSixLibrary(nameVal.toString())); - } - - applyString(lib, "url", library, &OneSixLibrary::setBaseUrl); - applyString(lib, "MMC-hint", library, &OneSixLibrary::setHint); - applyString(lib, "MMC-absulute_url", library, &OneSixLibrary::setAbsoluteUrl); - applyString(lib, "MMC-absoluteUrl", library, &OneSixLibrary::setAbsoluteUrl); - - auto extractVal = lib.value("extract"); - if (extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) + for (auto order : files.keys()) { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) + QLOG_DEBUG() << "Applying file with order" << order; + auto filePair = files[order]; + bool isError = false; + filePair.second.applyTo(m_version, isError); + if (isError) { - if (excludeVal.isString()) - { - excludes.append(excludeVal.toString()); - } + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(filePair.first)); + return false; } - library->extract_excludes = excludes; } } - auto nativesVal = lib.value("natives"); - if (nativesVal.isObject()) + // custom.json + if (!excludeCustom) { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + if (QFile::exists(root.absoluteFilePath("custom.json"))) { - auto osType = OpSys_fromString(it.key()); - if (osType == Os_Other) + QLOG_INFO() << "Reading custom.json"; + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) { - continue; + return false; } - if (!it.value().isString()) + bool isError = false; + file.applyTo(m_version, isError); + if (isError) { - continue; + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("custom.json"))); + return false; } - library->addNative(osType, it.value().toString()); } } - if (lib.contains("rules")) + // some final touches { - library->setRules(rulesFromJsonV4(lib)); - } - library->finalize(); - if (type == Override) - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() != "apply") - { - if (lib.value("insert").toString() == "append") - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() == "prepend") + if (m_version->assets.isEmpty()) { - m_version->libraries.prepend(library); + m_version->assets = "legacy"; } - else if (lib.value("insert").isObject()) + if (m_version->minecraftArguments.isEmpty()) { - QJsonObject insertObj = lib.value("insert").toObject(); - if (insertObj.isEmpty()) + QString toCompare = m_version->processArguments.toLower(); + if (toCompare == "legacy") { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty")); - return false; + m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; } - const QString key = insertObj.keys().first(); - const QString value = insertObj.value(key).toString(); - const int index = findLibrary(m_version->libraries, value); - if (index >= 0) + else if (toCompare == "username_session") { - if (key == "before") - { - m_version->libraries.insert(index, library); - } - else if (key == "after") - { - m_version->libraries.insert(index + 1, library); - } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } + m_version->minecraftArguments = + "--username ${auth_player_name} --session ${auth_session}"; + } + else if (toCompare == "username_session_version") + { + m_version->minecraftArguments = "--username ${auth_player_name} " + "--session ${auth_session} " + "--version ${profile_name}"; } } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } } + + return true; +} + +bool OneSixVersionBuilder::read(const QJsonObject &obj) +{ + m_version->clear(); + + bool isError = false; + VersionFile file = VersionFile::fromJson(QJsonDocument(obj), QString(), false, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while reading. Please check MultiMC-0.log for more info.")); + return false; + } + file.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while applying. Please check MultiMC-0.log for more info.")); + return false; + } + return true; } -bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) +bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder, + VersionFile *out) { QFile file(fileInfo.absoluteFilePath()); if (!file.open(QFile::ReadOnly)) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); return false; } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); if (error.error != QJsonParseError::NoError) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to parse %1: %2 at %3").arg(file.fileName(), error.errorString()).arg(error.offset)); + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), + QObject::tr("Unable to parse %1: %2 at %3") + .arg(file.fileName(), error.errorString()) + .arg(error.offset)); return false; } - *out = doc.object(); + bool isError = false; + *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while reading %1. Please check MultiMC-0.log for more info.") + .arg(file.fileName())); + ; + } return true; } diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h index a3ba331c..ac8c13bf 100644 --- a/logic/OneSixVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -22,12 +22,13 @@ class OneSixInstance; class QWidget; class QJsonObject; class QFileInfo; +class VersionFile; class OneSixVersionBuilder { OneSixVersionBuilder(); public: - static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent); + static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool excludeCustom); static bool read(OneSixVersion *version, const QJsonObject &obj); private: @@ -35,19 +36,8 @@ private: OneSixInstance *m_instance; QWidget *m_widgetParent; - enum Type - { - Override, - Add, - Remove - }; - - bool build(); + bool build(const bool excludeCustom); bool read(const QJsonObject &obj); - void clear(); - bool apply(const QJsonObject &object); - bool applyLibrary(const QJsonObject &lib, const Type type); - - bool read(const QFileInfo &fileInfo, QJsonObject *out); + bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out); }; -- cgit v1.2.3 From 176783c8caacafd5d320c2fef7374335ee54796b Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 27 Jan 2014 20:17:29 +0100 Subject: Have the libraries tab show tweaker mods instead of libraries --- logic/LiteLoaderInstaller.cpp | 2 +- logic/OneSixVersion.cpp | 52 ++++++++++--------------------------------- logic/OneSixVersion.h | 2 -- 3 files changed, 13 insertions(+), 43 deletions(-) (limited to 'logic') diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 1ce9d3f4..1c38fd5e 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -53,7 +53,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) QJsonObject obj; obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch")); - obj.insert("+minecraftArguments", QString(" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker")); + obj.insert("+tweakers", QJsonArray::fromStringList(QStringList() << "com.mumfrey.liteloader.launch.LiteLoaderTweaker")); obj.insert("order", 10); QJsonArray libraries; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 1b6bc9cb..1abd5114 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -27,11 +27,15 @@ OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom) { - return OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); + beginResetModel(); + bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); + endResetModel(); + return ret; } void OneSixVersion::clear() { + beginResetModel(); id.clear(); time.clear(); releaseTime.clear(); @@ -43,6 +47,7 @@ void OneSixVersion::clear() mainClass.clear(); libraries.clear(); tweakers.clear(); + endResetModel(); } void OneSixVersion::dump() const @@ -109,21 +114,14 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const int row = index.row(); int column = index.column(); - if (row < 0 || row >= libraries.size()) + if (row < 0 || row >= tweakers.size()) return QVariant(); if (role == Qt::DisplayRole) { - switch (column) + if (column == 0) { - case 0: - return libraries[row]->name(); - case 1: - return libraries[row]->type(); - case 2: - return libraries[row]->version(); - default: - return QVariant(); + return tweakers.at(row); } } return QVariant(); @@ -133,43 +131,17 @@ Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; - int row = index.row(); - if (libraries[row]->isActive()) - { - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; - } - else - { - return Qt::ItemNeverHasChildren; - } - // return QAbstractListModel::flags(index); -} - -QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - switch (section) - { - case 0: - return QString("Name"); - case 1: - return QString("Type"); - case 2: - return QString("Version"); - default: - return QString(); - } + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } int OneSixVersion::rowCount(const QModelIndex &parent) const { - return libraries.size(); + return tweakers.size(); } int OneSixVersion::columnCount(const QModelIndex &parent) const { - return 3; + return 1; } QDebug operator<<(QDebug &dbg, const OneSixVersion *version) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 767e05db..98a4b418 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -33,8 +33,6 @@ public: virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; -- cgit v1.2.3 From 986141b5035c99747c7e06cd447b8b4e27eb2b27 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 27 Jan 2014 22:23:07 +0100 Subject: Fix library ordering --- logic/ForgeInstaller.cpp | 8 ++------ logic/LiteLoaderInstaller.cpp | 2 +- logic/OneSixVersionBuilder.cpp | 36 ++++++++++++++---------------------- 3 files changed, 17 insertions(+), 29 deletions(-) (limited to 'logic') diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index c0340e95..8d2445ed 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -160,7 +160,7 @@ bool ForgeInstaller::add(OneSixInstance *to) equals = true; } // replace lib - libObj.insert("insert", QString("apply")); + libObj.insert("insert", QString("replace")); break; } if (equals) @@ -170,11 +170,7 @@ bool ForgeInstaller::add(OneSixInstance *to) if (!found) { // add lib - QJsonObject insertObj; - insertObj.insert( - "before", - to->getFullVersion()->libraries.at(sliding_insert_window + 1)->rawName()); - libObj.insert("insert", insertObj); + libObj.insert("insert", QString("prepend-if-not-exists")); sliding_insert_window++; } librariesPlus.prepend(libObj); diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 1c38fd5e..f658f834 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -63,7 +63,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); launchwrapperLib.finalize(); QJsonObject lwLibObj = launchwrapperLib.toJson(); - lwLibObj.insert("insert", QString("prepend")); + lwLibObj.insert("insert", QString("prepend-if-not-exists")); libraries.append(lwLibObj); } diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index c031a94a..3b119b45 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -70,8 +70,8 @@ struct VersionFile Apply, Append, Prepend, - InsertBefore, - InsertAfter, + AppendIfNotExists, + PrependIfNotExists, Replace }; InsertType insertType; @@ -408,13 +408,13 @@ struct VersionFile { lib.insertType = Library::Prepend; } - else if (insertString == "before") + else if (insertString == "prepend-if-not-exists") { - lib.insertType = Library::InsertBefore; + lib.insertType = Library::PrependIfNotExists; } - else if (insertString == "after") + else if (insertString == "append-if-not-exists") { - lib.insertType = Library::InsertAfter; + lib.insertType = Library::PrependIfNotExists; } else if (insertString == "replace") { @@ -613,31 +613,23 @@ struct VersionFile case Library::Prepend: version->libraries.prepend(createLibrary(lib)); break; - case Library::InsertBefore: + case Library::AppendIfNotExists: { - int index = findLibrary(version->libraries, lib.insertData); - if (index >= 0) - { - version->libraries.insert(index, createLibrary(lib)); - } - else + int index = findLibrary(version->libraries, lib.name); + if (index < 0) { - QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + version->libraries.append(createLibrary(lib)); } break; } - case Library::InsertAfter: + case Library::PrependIfNotExists: { - int index = findLibrary(version->libraries, lib.insertData); - if (index >= 0) - { - version->libraries.insert(index + 1, createLibrary(lib)); - } - else + int index = findLibrary(version->libraries, lib.name); + if (index < 0) { - QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + version->libraries.prepend(createLibrary(lib)); } break; } -- cgit v1.2.3 From 556d8f0ec16e5030cfbfac9dc0fc2c796e829dcb Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Tue, 28 Jan 2014 07:39:43 +0100 Subject: custom.json overrides all. For user patching there now is instance.json --- logic/OneSixVersionBuilder.cpp | 107 +++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 42 deletions(-) (limited to 'logic') diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 3b119b45..a8e7253b 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -695,13 +695,11 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) QDir root(m_instance->instanceRoot()); QDir patches(root.absoluteFilePath("patches/")); - // version.json -> patches/*.json -> custom.json - - // version.json + if (QFile::exists(root.absoluteFilePath("custom.json"))) { - QLOG_INFO() << "Reading version.json"; + QLOG_INFO() << "Reading custom.json"; VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) { return false; } @@ -713,65 +711,90 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) m_widgetParent, QObject::tr("Error"), QObject::tr( "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("version.json"))); + .arg(root.absoluteFilePath("custom.json"))); return false; } } - - // patches/ + else { - // load all, put into map for ordering, apply in the right order + // version.json -> patches/*.json -> instance.json - QMap> files; - for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + // version.json { - QLOG_INFO() << "Reading" << info.fileName(); + QLOG_INFO() << "Reading version.json"; VersionFile file; - if (!read(info, true, &file)) + if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) { return false; } - files.insert(file.order, qMakePair(info.fileName(), file)); - } - for (auto order : files.keys()) - { - QLOG_DEBUG() << "Applying file with order" << order; - auto filePair = files[order]; bool isError = false; - filePair.second.applyTo(m_version, isError); + file.applyTo(m_version, isError); if (isError) { QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(filePair.first)); + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("version.json"))); return false; } } - } - // custom.json - if (!excludeCustom) - { - if (QFile::exists(root.absoluteFilePath("custom.json"))) + // patches/ { - QLOG_INFO() << "Reading custom.json"; - VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) + // load all, put into map for ordering, apply in the right order + + QMap> files; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { - return false; + QLOG_INFO() << "Reading" << info.fileName(); + VersionFile file; + if (!read(info, true, &file)) + { + return false; + } + files.insert(file.order, qMakePair(info.fileName(), file)); } - bool isError = false; - file.applyTo(m_version, isError); - if (isError) + for (auto order : files.keys()) { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("custom.json"))); - return false; + QLOG_DEBUG() << "Applying file with order" << order; + auto filePair = files[order]; + bool isError = false; + filePair.second.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(filePair.first)); + return false; + } + } + } + + // instance.json + if (!excludeCustom) + { + if (QFile::exists(root.absoluteFilePath("instance.json"))) + { + QLOG_INFO() << "Reading instance.json"; + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("instance.json")), false, &file)) + { + return false; + } + bool isError = false; + file.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("instance.json"))); + return false; + } } } } -- cgit v1.2.3 From 4a9e213238234b03fdd994143726a3f75290fc26 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 1 Feb 2014 14:52:21 +0100 Subject: Change the OneSix library view. It now shows a list of patches. --- logic/ForgeInstaller.cpp | 6 +++++ logic/ForgeInstaller.h | 1 + logic/LiteLoaderInstaller.cpp | 4 +++ logic/OneSixInstance.cpp | 5 ++-- logic/OneSixVersion.cpp | 55 ++++++++++++++++++++++++++++++++++++++---- logic/OneSixVersion.h | 17 +++++++++++++ logic/OneSixVersionBuilder.cpp | 40 ++++++++++++++++++++++++++---- 7 files changed, 116 insertions(+), 12 deletions(-) (limited to 'logic') diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 8d2445ed..c7160d6e 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -74,6 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) QJsonObject installObj = installVal.toObject(); QString libraryName = installObj.value("path").toString(); internalPath = installObj.value("filePath").toString(); + m_forgeVersionString = installObj.value("version").toString().remove("Forge").trimmed(); // where do we put the library? decode the mojang path OneSixLibrary lib(libraryName); @@ -204,6 +205,11 @@ bool ForgeInstaller::add(OneSixInstance *to) } } + obj.insert("name", QString("Forge")); + obj.insert("id", id()); + obj.insert("version", m_forgeVersionString); + obj.insert("mcVersion", to->intendedVersionId()); + QFile file(filename(to->instanceRoot())); if (!file.open(QFile::WriteOnly)) { diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index eb8ecd79..c5052092 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -37,5 +37,6 @@ private: QString internalPath; QString finalPath; QString realVersionId; + QString m_forgeVersionString; QString m_universal_url; }; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index f658f834..48bbaeff 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -78,6 +78,10 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) } obj.insert("+libraries", libraries); + obj.insert("name", QString("LiteLoader")); + obj.insert("id", id()); + obj.insert("version", to->intendedVersionId()); + obj.insert("mcVersion", to->intendedVersionId()); QFile file(filename(to->instanceRoot())); if (!file.open(QFile::WriteOnly)) diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 19d5e112..88c40316 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -319,8 +319,9 @@ bool OneSixInstance::shouldUpdate() const bool OneSixInstance::versionIsCustom() { QDir patches(PathCombine(instanceRoot(), "patches/")); - return QFile::exists(PathCombine(instanceRoot(), "custom.json")) - || (patches.exists() && patches.count() >= 0); + return (patches.exists() && patches.count() >= 0) + || QFile::exists(PathCombine(instanceRoot(), "custom.json")) + || QFile::exists(PathCombine(instanceRoot(), "user.json")); } QString OneSixInstance::currentVersionId() const diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 1abd5114..0b6d1986 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -16,6 +16,7 @@ #include "OneSixVersion.h" #include +#include #include "OneSixVersionBuilder.h" @@ -47,6 +48,7 @@ void OneSixVersion::clear() mainClass.clear(); libraries.clear(); tweakers.clear(); + versionFiles.clear(); endResetModel(); } @@ -70,6 +72,24 @@ void OneSixVersion::dump() const qDebug().nospace() << "\n)"; } +bool OneSixVersion::canRemove(const int index) const +{ + if (index < versionFiles.size()) + { + return versionFiles.at(index).id != "org.multimc.version.json"; + } + return false; +} + +bool OneSixVersion::remove(const int index) +{ + if (canRemove(index)) + { + return QFile::remove(versionFiles.at(index).filename); + } + return false; +} + QList > OneSixVersion::getActiveNormalLibs() { QList > output; @@ -114,14 +134,39 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const int row = index.row(); int column = index.column(); - if (row < 0 || row >= tweakers.size()) + if (row < 0 || row >= versionFiles.size()) return QVariant(); if (role == Qt::DisplayRole) { - if (column == 0) + switch (column) + { + case 0: + return versionFiles.at(row).name; + case 1: + return versionFiles.at(row).version; + default: + return QVariant(); + } + } + return QVariant(); +} + +QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) + { + if (role == Qt::DisplayRole) { - return tweakers.at(row); + switch (section) + { + case 0: + return tr("Name"); + case 1: + return tr("Version"); + default: + return QVariant(); + } } } return QVariant(); @@ -136,12 +181,12 @@ Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const int OneSixVersion::rowCount(const QModelIndex &parent) const { - return tweakers.size(); + return versionFiles.size(); } int OneSixVersion::columnCount(const QModelIndex &parent) const { - return 1; + return 2; } QDebug operator<<(QDebug &dbg, const OneSixVersion *version) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 98a4b418..d7a6684d 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -32,6 +32,7 @@ public: explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; @@ -41,6 +42,12 @@ public: void dump() const; + bool canRemove(const int index) const; + +public +slots: + bool remove(const int index); + public: QList> getActiveNormalLibs(); QList> getActiveNativeLibs(); @@ -109,6 +116,16 @@ public: */ // QList rules; + struct VersionFile + { + QString name; + QString id; + QString version; + QString mcVersion; + QString filename; + }; + QList versionFiles; + private: OneSixInstance *m_instance; }; diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index a8e7253b..08506d4b 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -34,6 +34,14 @@ struct VersionFile { int order; + QString name; + QString fileId; + QString version; + // TODO use the mcVersion to determine if a version file should be removed on update + QString mcVersion; + QString filename; + // TODO requirements + // QMap requirements; QString id; QString mainClass; QString overwriteMinecraftArguments; @@ -216,6 +224,12 @@ struct VersionFile } } + out.name = root.value("name").toString(); + out.fileId = root.value("id").toString(); + out.version = root.value("version").toString(); + out.mcVersion = root.value("mcVersion").toString(); + out.filename = filename; + auto readString = [root, filename](const QString &key, QString &variable) { if (root.contains(key)) @@ -661,6 +675,14 @@ struct VersionFile } } + OneSixVersion::VersionFile versionFile; + versionFile.name = name; + versionFile.id = fileId; + versionFile.version = this->version; + versionFile.mcVersion = mcVersion; + versionFile.filename = filename; + version->versionFiles.append(versionFile); + isError = false; } }; @@ -727,6 +749,10 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) { return false; } + file.name = "version.json"; + file.fileId = "org.multimc.version.json"; + file.version = m_instance->intendedVersionId(); + file.mcVersion = m_instance->intendedVersionId(); bool isError = false; file.applyTo(m_version, isError); if (isError) @@ -773,17 +799,21 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) } } - // instance.json + // user.json if (!excludeCustom) { - if (QFile::exists(root.absoluteFilePath("instance.json"))) + if (QFile::exists(root.absoluteFilePath("user.json"))) { - QLOG_INFO() << "Reading instance.json"; + QLOG_INFO() << "Reading user.json"; VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("instance.json")), false, &file)) + if (!read(QFileInfo(root.absoluteFilePath("user.json")), false, &file)) { return false; } + file.name = "user.json"; + file.fileId = "org.multimc.user.json"; + file.version = QString(); + file.mcVersion = QString(); bool isError = false; file.applyTo(m_version, isError); if (isError) @@ -792,7 +822,7 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) m_widgetParent, QObject::tr("Error"), QObject::tr( "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("instance.json"))); + .arg(root.absoluteFilePath("user.json"))); return false; } } -- cgit v1.2.3 From 8637cce4333aaf56a231d5fab866b0e770436783 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 1 Feb 2014 16:26:38 +0100 Subject: Fix a bug --- logic/ForgeInstaller.cpp | 2 +- logic/LiteLoaderInstaller.cpp | 2 +- logic/OneSixVersionBuilder.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'logic') diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index c7160d6e..47c42694 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -206,7 +206,7 @@ bool ForgeInstaller::add(OneSixInstance *to) } obj.insert("name", QString("Forge")); - obj.insert("id", id()); + obj.insert("fileId", id()); obj.insert("version", m_forgeVersionString); obj.insert("mcVersion", to->intendedVersionId()); diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 48bbaeff..60a43d49 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -79,7 +79,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) obj.insert("+libraries", libraries); obj.insert("name", QString("LiteLoader")); - obj.insert("id", id()); + obj.insert("fileId", id()); obj.insert("version", to->intendedVersionId()); obj.insert("mcVersion", to->intendedVersionId()); diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 08506d4b..a541766b 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -225,7 +225,7 @@ struct VersionFile } out.name = root.value("name").toString(); - out.fileId = root.value("id").toString(); + out.fileId = root.value("fileId").toString(); out.version = root.value("version").toString(); out.mcVersion = root.value("mcVersion").toString(); out.filename = filename; -- cgit v1.2.3