diff options
27 files changed, 498 insertions, 122 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 46446541..ac497f46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,8 +103,8 @@ set(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch ######## Set version numbers ######## set(MultiMC_VERSION_MAJOR 0) -set(MultiMC_VERSION_MINOR 4) -set(MultiMC_VERSION_HOTFIX 1) +set(MultiMC_VERSION_MINOR 5) +set(MultiMC_VERSION_HOTFIX 0) # Build number set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") @@ -427,6 +427,9 @@ SET(MULTIMC_SOURCES # RW lock protected map logic/RWStorage.h + # A variable that has an implicit default value and keeps track of changes + logic/DefaultVariable.h + # network stuffs logic/net/NetAction.h logic/net/MD5EtagDownload.h @@ -493,6 +496,7 @@ SET(MULTIMC_SOURCES logic/OneSixInstance_p.h # OneSix version json infrastructure + logic/minecraft/GradleSpecifier.h logic/minecraft/InstanceVersion.cpp logic/minecraft/InstanceVersion.h logic/minecraft/JarMod.cpp diff --git a/MultiMC.cpp b/MultiMC.cpp index 7e7c6c88..acf05c89 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -357,6 +357,9 @@ void MultiMC::initGlobalSettings() m_settings->registerSetting("AutoUpdate", true); m_settings->registerSetting("IconTheme", QString("multimc")); + // Minecraft Sneaky Updates + m_settings->registerSetting("AutoUpdateMinecraftVersions", true); + // Notifications m_settings->registerSetting("ShownNotifications", QString()); diff --git a/changelog.md b/changelog.md index e4da86b6..8bbade3e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,8 @@ #MultiMC Changelog +##0.5.0 +- In development... + ##0.4.1 - Fix LWJGL version list (SourceForge has changed the download API) diff --git a/gui/dialogs/IconPickerDialog.h b/gui/dialogs/IconPickerDialog.h index 70951da6..f00c2388 100644 --- a/gui/dialogs/IconPickerDialog.h +++ b/gui/dialogs/IconPickerDialog.h @@ -29,10 +29,7 @@ class IconPickerDialog : public QDialog public: explicit IconPickerDialog(QWidget *parent = 0); ~IconPickerDialog(); - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Woverloaded-virtual" int exec(QString selection); - #pragma clang diagnostic pop QString selectedIconKey; protected: diff --git a/gui/dialogs/ProgressDialog.h b/gui/dialogs/ProgressDialog.h index 45a6238a..3276fd7a 100644 --- a/gui/dialogs/ProgressDialog.h +++ b/gui/dialogs/ProgressDialog.h @@ -34,10 +34,7 @@ public: void updateSize(); - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Woverloaded-virtual" int exec(ProgressProvider *task); - #pragma clang diagnostic pop void setSkipButton(bool present, QString label = QString()); diff --git a/gui/dialogs/SettingsDialog.cpp b/gui/dialogs/SettingsDialog.cpp index 46368bc1..347cd67e 100644 --- a/gui/dialogs/SettingsDialog.cpp +++ b/gui/dialogs/SettingsDialog.cpp @@ -365,6 +365,9 @@ void SettingsDialog::applySettings(SettingsObject *s) } s->set("JsonEditor", jsonEditor); + // Minecraft version updates + s->set("AutoUpdateMinecraftVersions", ui->autoupdateMinecraft->isChecked()); + // Console s->set("ShowConsole", ui->showConsoleCheck->isChecked()); s->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked()); @@ -470,6 +473,9 @@ void SettingsDialog::loadSettings(SettingsObject *s) // Editors ui->jsonEditorTextBox->setText(s->get("JsonEditor").toString()); + // Minecraft version updates + ui->autoupdateMinecraft->setChecked(s->get("AutoUpdateMinecraftVersions").toBool()); + // Console ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool()); ui->autoCloseConsoleCheck->setChecked(s->get("AutoCloseConsole").toBool()); diff --git a/gui/dialogs/SettingsDialog.ui b/gui/dialogs/SettingsDialog.ui index d4e90302..94b81dd7 100644 --- a/gui/dialogs/SettingsDialog.ui +++ b/gui/dialogs/SettingsDialog.ui @@ -410,6 +410,22 @@ </attribute> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>Minecraft Version Updates</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_14"> + <item> + <widget class="QCheckBox" name="autoupdateMinecraft"> + <property name="text"> + <string>Automatically update to latest version revision</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QGroupBox" name="windowSizeGroupBox"> <property name="title"> <string>Window Size</string> @@ -1056,11 +1072,30 @@ </layout> </widget> <tabstops> - <tabstop>buttonBox</tabstop> + <tabstop>settingsTabs</tabstop> + <tabstop>autoUpdateCheckBox</tabstop> + <tabstop>updateChannelComboBox</tabstop> + <tabstop>trackFtbBox</tabstop> + <tabstop>ftbLauncherBox</tabstop> + <tabstop>ftbLauncherBrowseBtn</tabstop> + <tabstop>ftbBox</tabstop> + <tabstop>ftbBrowseBtn</tabstop> + <tabstop>instDirTextBox</tabstop> + <tabstop>instDirBrowseBtn</tabstop> + <tabstop>modsDirTextBox</tabstop> + <tabstop>modsDirBrowseBtn</tabstop> + <tabstop>lwjglDirTextBox</tabstop> + <tabstop>lwjglDirBrowseBtn</tabstop> + <tabstop>iconsDirTextBox</tabstop> + <tabstop>iconsDirBrowseBtn</tabstop> + <tabstop>languageBox</tabstop> + <tabstop>resetNotificationsBtn</tabstop> <tabstop>sortLastLaunchedBtn</tabstop> <tabstop>sortByNameBtn</tabstop> + <tabstop>themeComboBox</tabstop> <tabstop>jsonEditorTextBox</tabstop> <tabstop>jsonEditorBrowseBtn</tabstop> + <tabstop>autoupdateMinecraft</tabstop> <tabstop>maximizedCheckBox</tabstop> <tabstop>windowWidthSpinBox</tabstop> <tabstop>windowHeightSpinBox</tabstop> @@ -1076,7 +1111,23 @@ <tabstop>jvmArgsTextBox</tabstop> <tabstop>preLaunchCmdTextBox</tabstop> <tabstop>postExitCmdTextBox</tabstop> - <tabstop>settingsTabs</tabstop> + <tabstop>proxyDefaultBtn</tabstop> + <tabstop>proxyNoneBtn</tabstop> + <tabstop>proxySOCKS5Btn</tabstop> + <tabstop>proxyHTTPBtn</tabstop> + <tabstop>proxyAddrEdit</tabstop> + <tabstop>proxyPortEdit</tabstop> + <tabstop>proxyUserEdit</tabstop> + <tabstop>proxyPassEdit</tabstop> + <tabstop>jprofilerPathEdit</tabstop> + <tabstop>jprofilerPathBtn</tabstop> + <tabstop>jprofilerCheckBtn</tabstop> + <tabstop>jvisualvmPathEdit</tabstop> + <tabstop>jvisualvmPathBtn</tabstop> + <tabstop>jvisualvmCheckBtn</tabstop> + <tabstop>mceditPathEdit</tabstop> + <tabstop>mceditPathBtn</tabstop> + <tabstop>mceditCheckBtn</tabstop> </tabstops> <resources> <include location="../../graphics.qrc"/> diff --git a/gui/pages/InstanceSettingsPage.ui b/gui/pages/InstanceSettingsPage.ui index b8af6c60..b141dbca 100644 --- a/gui/pages/InstanceSettingsPage.ui +++ b/gui/pages/InstanceSettingsPage.ui @@ -427,6 +427,30 @@ </item> </layout> </widget> + <tabstops> + <tabstop>settingsTabs</tabstop> + <tabstop>javaSettingsGroupBox</tabstop> + <tabstop>javaPathTextBox</tabstop> + <tabstop>javaDetectBtn</tabstop> + <tabstop>javaBrowseBtn</tabstop> + <tabstop>javaTestBtn</tabstop> + <tabstop>memoryGroupBox</tabstop> + <tabstop>minMemSpinBox</tabstop> + <tabstop>maxMemSpinBox</tabstop> + <tabstop>permGenSpinBox</tabstop> + <tabstop>javaArgumentsGroupBox</tabstop> + <tabstop>jvmArgsTextBox</tabstop> + <tabstop>windowSizeGroupBox</tabstop> + <tabstop>maximizedCheckBox</tabstop> + <tabstop>windowWidthSpinBox</tabstop> + <tabstop>windowHeightSpinBox</tabstop> + <tabstop>consoleSettingsBox</tabstop> + <tabstop>showConsoleCheck</tabstop> + <tabstop>autoCloseConsoleCheck</tabstop> + <tabstop>customCommandsGroupBox</tabstop> + <tabstop>preLaunchCmdTextBox</tabstop> + <tabstop>postExitCmdTextBox</tabstop> + </tabstops> <resources/> <connections/> </ui> diff --git a/logic/BaseVersionList.h b/logic/BaseVersionList.h index f903b52c..21b44e8d 100644 --- a/logic/BaseVersionList.h +++ b/logic/BaseVersionList.h @@ -100,10 +100,7 @@ public: /*! * Sorts the version list. */ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Woverloaded-virtual" virtual void sort() = 0; - #pragma clang diagnostic pop protected slots: diff --git a/logic/DefaultVariable.h b/logic/DefaultVariable.h new file mode 100644 index 00000000..38d7ecc2 --- /dev/null +++ b/logic/DefaultVariable.h @@ -0,0 +1,35 @@ +#pragma once + +template <typename T> +class DefaultVariable +{ +public: + DefaultVariable(const T & value) + { + defaultValue = value; + } + DefaultVariable<T> & operator =(const T & value) + { + currentValue = value; + is_default = currentValue == defaultValue; + is_explicit = true; + return *this; + } + operator const T &() const + { + return is_default ? defaultValue : currentValue; + } + bool isDefault() const + { + return is_default; + } + bool isExplicit() const + { + return is_explicit; + } +private: + T currentValue; + T defaultValue; + bool is_default = true; + bool is_explicit = false; +}; diff --git a/logic/InstanceList.cpp b/logic/InstanceList.cpp index 8a68c334..650f5c80 100644 --- a/logic/InstanceList.cpp +++ b/logic/InstanceList.cpp @@ -331,7 +331,29 @@ QSet<FTBRecord> InstanceList::discoverFTBInstances() continue; record.name = attrs.value("name").toString(); record.logo = attrs.value("logo").toString(); - record.mcVersion = attrs.value("mcVersion").toString(); + auto customVersions = attrs.value("customMCVersions"); + if(!customVersions.isNull()) + { + QMap<QString, QString> versionMatcher; + QString customVersionsStr = customVersions.toString(); + QStringList list = customVersionsStr.split(';'); + for(auto item: list) + { + auto segment = item.split('^'); + if(segment.size() != 2) + { + QLOG_ERROR() << "FTB: Segment of size < 2 in " << customVersionsStr; + continue; + } + versionMatcher[segment[0]] = segment[1]; + } + auto actualVersion = attrs.value("version").toString(); + record.mcVersion = versionMatcher[actualVersion]; + } + else + { + record.mcVersion = attrs.value("mcVersion").toString(); + } record.description = attrs.value("description").toString(); records.insert(record); } diff --git a/logic/VersionFilterData.cpp b/logic/VersionFilterData.cpp index e8523018..866d4947 100644 --- a/logic/VersionFilterData.cpp +++ b/logic/VersionFilterData.cpp @@ -65,4 +65,8 @@ VersionFilterData::VersionFilterData() * (these versions need to be tested by us first) */ legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00"); + lwjglWhitelist = + QSet<QString>{"net.java.jinput:jinput", "net.java.jinput:jinput-platform", + "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl", + "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"}; } diff --git a/logic/VersionFilterData.h b/logic/VersionFilterData.h index e010adc7..98ecb36c 100644 --- a/logic/VersionFilterData.h +++ b/logic/VersionFilterData.h @@ -22,5 +22,7 @@ struct VersionFilterData QSet<QString> legacyBlacklist; // no new versions below this date will be accepted from Mojang servers QDateTime legacyCutoffDate; + // Libraries that belong to LWJGL + QSet<QString> lwjglWhitelist; }; extern VersionFilterData g_VersionFilterData; diff --git a/logic/forge/ForgeInstaller.cpp b/logic/forge/ForgeInstaller.cpp index 7ab0a09b..74db2bfc 100644 --- a/logic/forge/ForgeInstaller.cpp +++ b/logic/forge/ForgeInstaller.cpp @@ -133,8 +133,8 @@ bool ForgeInstaller::add(OneSixInstance *to) int sliding_insert_window = 0; { QJsonArray librariesPlus; - // A blacklist - we ignore these entirely - QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"}; + // A blacklist + QSet<QString> blacklist{"authlib", "realms"}; // QList<QString> xzlist{"org.scala-lang", "com.typesafe"}; // for each library in the version we are adding (except for the blacklisted) @@ -143,15 +143,38 @@ bool ForgeInstaller::add(OneSixInstance *to) QString libName = lib->name(); QString rawName = lib->rawName(); - // ignore blacklisted stuff + // ignore lwjgl libraries. + if (g_VersionFilterData.lwjglWhitelist.contains(lib->fullname())) + continue; + // ignore other blacklisted (realms, authlib) if (blacklist.contains(libName)) continue; // WARNING: This could actually break. // if this is the actual forge lib, set an absolute url for the download - if (libName.contains("minecraftforge")) + if(m_forge_version->type == ForgeVersion::Gradle) + { + if (libName == "forge") + { + lib->m_name.setClassifier("universal"); + lib->finalize(); + } + else if (libName == "minecraftforge") + { + QString forgeCoord ("net.minecraftforge:forge:%1:universal"); + // using insane form of the MC version... + QString longVersion = m_forge_version->mcver + "-" + m_forge_version->jobbuildver; + GradleSpecifier spec(forgeCoord.arg(longVersion)); + lib->m_name = spec; + lib->finalize(); + } + } + else { - lib->setAbsoluteUrl(m_universal_url); + if (libName.contains("minecraftforge")) + { + lib->setAbsoluteUrl(m_universal_url); + } } // WARNING: This could actually break. @@ -192,7 +215,7 @@ bool ForgeInstaller::add(OneSixInstance *to) { // add lib libObj.insert("insert", QString("prepend")); - if (lib->name() == "minecraftforge") + if (lib->name() == "minecraftforge" || lib->name() == "forge") { libObj.insert("MMC-depend", QString("hard")); } diff --git a/logic/forge/ForgeVersion.h b/logic/forge/ForgeVersion.h index 466f46bd..c7adc572 100644 --- a/logic/forge/ForgeVersion.h +++ b/logic/forge/ForgeVersion.h @@ -17,6 +17,13 @@ struct ForgeVersion : public BaseVersion QString filename(); QString url(); + enum + { + Invalid, + Legacy, + Gradle + } type = Invalid; + bool usesInstaller(); int m_buildnr = 0; diff --git a/logic/forge/ForgeVersionList.cpp b/logic/forge/ForgeVersionList.cpp index efb30ba6..ee7c63e3 100644 --- a/logic/forge/ForgeVersionList.cpp +++ b/logic/forge/ForgeVersionList.cpp @@ -286,6 +286,7 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out) fVersion->installer_filename = installer_filename; fVersion->universal_filename = universal_filename; fVersion->m_buildnr = build_nr; + fVersion->type = ForgeVersion::Legacy; out.append(fVersion); } } @@ -335,6 +336,10 @@ bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out) QJsonObject number = it.value().toObject(); std::shared_ptr<ForgeVersion> fVersion(new ForgeVersion()); fVersion->m_buildnr = number.value("build").toDouble(); + if(fVersion->m_buildnr >= 953 && fVersion->m_buildnr <= 965) + { + QLOG_DEBUG() << fVersion->m_buildnr; + } fVersion->jobbuildver = number.value("version").toString(); fVersion->branch = number.value("branch").toString(""); fVersion->mcver = number.value("mcversion").toString(); @@ -395,6 +400,7 @@ bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out) } fVersion->universal_filename = universal_filename; fVersion->installer_filename = installer_filename; + fVersion->type = ForgeVersion::Gradle; out.append(fVersion); } return true; diff --git a/logic/liteloader/LiteLoaderVersionList.cpp b/logic/liteloader/LiteLoaderVersionList.cpp index eced78e9..af8509d6 100644 --- a/logic/liteloader/LiteLoaderVersionList.cpp +++ b/logic/liteloader/LiteLoaderVersionList.cpp @@ -212,7 +212,7 @@ void LLListLoadTask::listDownloaded() { auto lib = RawLibrary::fromJson(libobject, "versions.json"); // hack to make liteloader 1.7.10_00 work - if(lib->m_name == "org.ow2.asm:asm-all:5.0.3") + if(lib->m_name == GradleSpecifier("org.ow2.asm:asm-all:5.0.3")) { lib->m_base_url = "http://repo.maven.apache.org/maven2/"; } diff --git a/logic/minecraft/GradleSpecifier.h b/logic/minecraft/GradleSpecifier.h new file mode 100644 index 00000000..8a02e4d9 --- /dev/null +++ b/logic/minecraft/GradleSpecifier.h @@ -0,0 +1,129 @@ +#pragma once + +#include <QString> +#include <QStringList> +#include "logic/DefaultVariable.h" + +struct GradleSpecifier +{ + GradleSpecifier() + { + m_valid = false; + } + GradleSpecifier(QString value) + { + operator=(value); + } + GradleSpecifier & operator =(const QString & value) + { + /* + org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar + DEBUG 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar" + DEBUG 1 "org.gradle.test.classifiers" + DEBUG 2 "service" + DEBUG 3 "1.0" + DEBUG 4 ":jdk15" + DEBUG 5 "jdk15" + DEBUG 6 "@jar" + DEBUG 7 "jar" + */ + QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(:([^:@]+))?" "(@([^:@]+))?"); + m_valid = matcher.exactMatch(value); + auto elements = matcher.capturedTexts(); + m_groupId = elements[1]; + m_artifactId = elements[2]; + m_version = elements[3]; + m_classifier = elements[5]; + if(!elements[7].isEmpty()) + { + m_extension = elements[7]; + } + return *this; + } + operator QString() const + { + if(!m_valid) + return "INVALID"; + QString retval = m_groupId + ":" + m_artifactId + ":" + m_version; + if(!m_classifier.isEmpty()) + { + retval += ":" + m_classifier; + } + if(m_extension.isExplicit()) + { + retval += "@" + m_extension; + } + return retval; + } + QString toPath() const + { + if(!m_valid) + return "INVALID"; + QString path = m_groupId; + path.replace('.', '/'); + path += '/' + m_artifactId + '/' + m_version + '/' + m_artifactId + '-' + m_version; + if(!m_classifier.isEmpty()) + { + path += "-" + m_classifier; + } + path += "." + m_extension; + return path; + } + inline bool valid() const + { + return m_valid; + } + inline QString version() const + { + return m_version; + } + inline QString groupId() const + { + return m_groupId; + } + inline QString artifactId() const + { + return m_artifactId; + } + inline void setClassifier(const QString & classifier) + { + m_classifier = classifier; + } + inline QString classifier() const + { + return m_classifier; + } + inline QString extension() const + { + return m_extension; + } + inline QString artifactPrefix() const + { + return m_groupId + ":" + m_artifactId; + } + bool matchName(const GradleSpecifier & other) + { + return other.artifactId() == artifactId() && other.groupId() == groupId(); + } + bool operator==(const GradleSpecifier & other) + { + if(m_groupId != other.m_groupId) + return false; + if(m_artifactId != other.m_artifactId) + return false; + if(m_version != other.m_version) + return false; + if(m_classifier != other.m_classifier) + return false; + if(m_extension != other.m_extension) + return false; + return true; + } +private: + QString m_groupId; + QString m_artifactId; + QString m_version; + QString m_classifier; + DefaultVariable<QString> m_extension = DefaultVariable<QString>("jar"); + bool m_valid = false; +}; diff --git a/logic/minecraft/MinecraftVersion.cpp b/logic/minecraft/MinecraftVersion.cpp index 488a180a..8368c430 100644 --- a/logic/minecraft/MinecraftVersion.cpp +++ b/logic/minecraft/MinecraftVersion.cpp @@ -2,6 +2,8 @@ #include "InstanceVersion.h" #include "VersionBuildError.h" #include "VersionBuilder.h" +#include "MultiMC.h" +#include "logic/settings/SettingsObject.h" bool MinecraftVersion::usesLegacyLauncher() { @@ -141,3 +143,15 @@ QString MinecraftVersion::getPatchFilename() { return QString(); } + +bool MinecraftVersion::needsUpdate() +{ + auto settings = MMC->settings(); + bool result = hasUpdate() && settings->get("AutoUpdateMinecraftVersions").toBool(); + return result; +} + +bool MinecraftVersion::hasUpdate() +{ + return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate); +} diff --git a/logic/minecraft/MinecraftVersion.h b/logic/minecraft/MinecraftVersion.h index 82073a97..e06dadb1 100644 --- a/logic/minecraft/MinecraftVersion.h +++ b/logic/minecraft/MinecraftVersion.h @@ -45,14 +45,8 @@ public: /* methods */ virtual QString getPatchVersion() override; virtual QString getPatchName() override; virtual QString getPatchFilename() override; - bool needsUpdate() - { - return m_versionSource == Remote; - } - bool hasUpdate() - { - return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate); - } + bool needsUpdate(); + bool hasUpdate(); private: /* methods */ void applyFileTo(InstanceVersion *version); diff --git a/logic/minecraft/MinecraftVersionList.cpp b/logic/minecraft/MinecraftVersionList.cpp index 3aa1ac01..ee228528 100644 --- a/logic/minecraft/MinecraftVersionList.cpp +++ b/logic/minecraft/MinecraftVersionList.cpp @@ -266,7 +266,8 @@ void MinecraftVersionList::loadMojangList(QJsonDocument jsonDoc, VersionSource s QString versionTypeStr = versionObj.value("type").toString(""); if (versionTypeStr.isEmpty()) { - // FIXME: log this somewhere + QLOG_ERROR() << "Ignoring" << versionID + << "because it doesn't have the version type set."; continue; } // OneSix or Legacy. use filter to determine type @@ -284,10 +285,13 @@ void MinecraftVersionList::loadMojangList(QJsonDocument jsonDoc, VersionSource s } else { - // FIXME: log this somewhere + QLOG_ERROR() << "Ignoring" << versionID + << "because it has an invalid version type."; continue; } mcVersion->m_type = versionTypeStr; + QLOG_INFO() << "Loaded version" << versionID << "from" + << ((source == Remote) ? "remote" : "local") << "version list."; tempList.append(mcVersion); } updateListData(tempList); @@ -333,12 +337,6 @@ void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions) { continue; } - // is it actually an update? - if (orig->m_updateTime >= added->m_updateTime) - { - // nope. - continue; - } // alright, it's an update. put it inside the original, for further processing. orig->upstreamUpdate = added; } @@ -451,13 +449,10 @@ void MCVListVersionUpdateTask::json_downloaded() } QList<RawLibraryPtr> filteredLibs; QList<RawLibraryPtr> lwjglLibs; - QSet<QString> lwjglFilter = { - "net.java.jinput:jinput", "net.java.jinput:jinput-platform", - "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl", - "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"}; + for (auto lib : file->overwriteLibs) { - if (lwjglFilter.contains(lib->fullname())) + if (g_VersionFilterData.lwjglWhitelist.contains(lib->fullname())) { lwjglLibs.append(lib); } @@ -581,9 +576,11 @@ void MinecraftVersionList::finalizeUpdate(QString version) auto updatedVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[idx]); + // reject any updates to builtin versions. if (updatedVersion->m_versionSource == Builtin) return; + // if we have an update for the version, replace it, make the update local if (updatedVersion->upstreamUpdate) { auto updatedWith = updatedVersion->upstreamUpdate; @@ -593,6 +590,7 @@ void MinecraftVersionList::finalizeUpdate(QString version) } else { + // otherwise, just set the version as local; updatedVersion->m_versionSource = Local; } diff --git a/logic/minecraft/OneSixLibrary.cpp b/logic/minecraft/OneSixLibrary.cpp index 7f69d9f8..c562f353 100644 --- a/logic/minecraft/OneSixLibrary.cpp +++ b/logic/minecraft/OneSixLibrary.cpp @@ -30,7 +30,7 @@ OneSixLibrary::OneSixLibrary(RawLibraryPtr base) m_hint = base->m_hint; m_absolute_url = base->m_absolute_url; extract_excludes = base->extract_excludes; - m_native_suffixes = base->m_native_suffixes; + m_native_classifiers = base->m_native_classifiers; m_rules = base->m_rules; finalize(); } @@ -42,31 +42,8 @@ OneSixLibraryPtr OneSixLibrary::fromRawLibrary(RawLibraryPtr lib) void OneSixLibrary::finalize() { - QStringList parts = m_name.split(':'); - QString relative = parts[0]; - relative.replace('.', '/'); - relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; - - if (!isNative()) - 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 = minVersion = parts[2]; - m_storage_path = relative; - m_download_url = m_base_url + relative; - + QString relative; + if (m_rules.empty()) { m_is_active = true; @@ -84,13 +61,32 @@ void OneSixLibrary::finalize() } if (isNative()) { - m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); + GradleSpecifier nativeSpec = m_name; + m_is_active = m_is_active && m_native_classifiers.contains(currentSystem); m_decenttype = "Native"; + if(m_native_classifiers.contains(currentSystem)) + { + nativeSpec.setClassifier(m_native_classifiers[currentSystem]); + } + else + { + nativeSpec.setClassifier("INVALID"); + } + relative = nativeSpec.toPath(); } else { + relative = m_name.toPath(); m_decenttype = "Java"; } + + m_decentname = m_name.artifactId(); + m_decentversion = minVersion = m_name.version(); + m_storage_path = relative; + if(m_base_url.isEmpty()) + m_download_url = QString("https://" + URLConstants::LIBRARY_BASE) + relative; + else + m_download_url = m_base_url + relative; } void OneSixLibrary::setName(const QString &name) @@ -103,11 +99,11 @@ void OneSixLibrary::setBaseUrl(const QString &base_url) } void OneSixLibrary::addNative(OpSys os, const QString &suffix) { - m_native_suffixes[os] = suffix; + m_native_classifiers[os] = suffix; } void OneSixLibrary::clearSuffixes() { - m_native_suffixes.clear(); + m_native_classifiers.clear(); } void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules) { diff --git a/logic/minecraft/RawLibrary.cpp b/logic/minecraft/RawLibrary.cpp index 7e0ebff0..76bd7260 100644 --- a/logic/minecraft/RawLibrary.cpp +++ b/logic/minecraft/RawLibrary.cpp @@ -13,20 +13,20 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil } out->m_name = libObj.value("name").toString(); - auto readString = [libObj, filename](const QString & key, QString & variable) + auto readString = [libObj, filename](const QString & key, QString & variable) -> bool { - if (libObj.contains(key)) + if (!libObj.contains(key)) + return false; + QJsonValue val = libObj.value(key); + + if (!val.isString()) { - QJsonValue val = libObj.value(key); - if (!val.isString()) - { - QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; - } - else - { - variable = val.toString(); - } + QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; + return false; } + + variable = val.toString(); + return true; }; readString("url", out->m_base_url); @@ -54,7 +54,7 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil OpSys opSys = OpSys_fromString(it.key()); if (opSys != Os_Other) { - out->m_native_suffixes[opSys] = it.value().toString(); + out->m_native_classifiers[opSys] = it.value().toString(); } } } @@ -134,13 +134,13 @@ RawLibraryPtr RawLibrary::fromJsonPlus(const QJsonObject &libObj, const QString bool RawLibrary::isNative() const { - return m_native_suffixes.size() != 0; + return m_native_classifiers.size() != 0; } QJsonObject RawLibrary::toJson() { QJsonObject libRoot; - libRoot.insert("name", m_name); + libRoot.insert("name", (QString)m_name); if (m_absolute_url.size()) libRoot.insert("MMC-absoluteUrl", m_absolute_url); if (m_hint.size()) @@ -154,8 +154,8 @@ QJsonObject RawLibrary::toJson() if (isNative()) { QJsonObject nativeList; - auto iter = m_native_suffixes.begin(); - while (iter != m_native_suffixes.end()) + auto iter = m_native_classifiers.begin(); + while (iter != m_native_classifiers.end()) { nativeList.insert(OpSys_toString(iter.key()), iter.value()); iter++; @@ -188,18 +188,5 @@ QJsonObject RawLibrary::toJson() QString RawLibrary::fullname() { - QStringList parts = m_name.split(':'); - return parts[0] + ":" + parts[1]; -} - -QString RawLibrary::group() -{ - QStringList parts = m_name.split(':'); - return parts[0]; -} - -QString RawLibrary::version() -{ - QStringList parts = m_name.split(':'); - return parts[2]; + return m_name.artifactPrefix(); } diff --git a/logic/minecraft/RawLibrary.h b/logic/minecraft/RawLibrary.h index f5a28c61..6e8f7f10 100644 --- a/logic/minecraft/RawLibrary.h +++ b/logic/minecraft/RawLibrary.h @@ -8,6 +8,7 @@ #include "logic/minecraft/OneSixRule.h" #include "logic/minecraft/OpSys.h" +#include "GradleSpecifier.h" #include "logic/net/URLConstants.h" class RawLibrary; @@ -23,15 +24,14 @@ public: /* methods */ QJsonObject toJson(); QString fullname(); - QString version(); - QString group(); public: /* data */ - QString m_name; - QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; + GradleSpecifier m_name; + QString m_base_url; + /// type hint - modifies how the library is treated QString m_hint; - /// absolute URL. takes precedence over m_download_path, if defined + /// DEPRECATED: absolute URL. takes precedence over m_download_path, if defined QString m_absolute_url; bool applyExcludes = false; @@ -40,7 +40,7 @@ public: /* data */ /// Returns true if the library is native bool isNative() const; /// native suffixes per OS - QMap<OpSys, QString> m_native_suffixes; + QMap<OpSys, QString> m_native_classifiers; bool applyRules = false; QList<std::shared_ptr<Rule>> m_rules; diff --git a/logic/minecraft/VersionFile.cpp b/logic/minecraft/VersionFile.cpp index 93f57116..600c71ae 100644 --- a/logic/minecraft/VersionFile.cpp +++ b/logic/minecraft/VersionFile.cpp @@ -17,13 +17,13 @@ using namespace MMCJson; #define CURRENT_MINIMUM_LAUNCHER_VERSION 14 -int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle) +int findLibraryByName(QList<OneSixLibraryPtr> haystack, const GradleSpecifier &needle) { int retval = -1; for (int i = 0; i < haystack.size(); ++i) { - QString chunk = haystack.at(i)->rawName(); - if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(chunk) != -1) + + if(haystack.at(i)->m_name.matchName(needle)) { // only one is allowed. if (retval != -1) @@ -382,7 +382,7 @@ void VersionFile::applyTo(InstanceVersion *version) case RawLibrary::Apply: { // QLOG_INFO() << "Applying lib " << lib->name; - int index = findLibrary(version->libraries, lib->m_name); + int index = findLibraryByName(version->libraries, lib->m_name); if (index >= 0) { auto library = version->libraries[index]; @@ -405,7 +405,7 @@ void VersionFile::applyTo(InstanceVersion *version) if (lib->isNative()) { // library->clearSuffixes(); - library->m_native_suffixes = lib->m_native_suffixes; + library->m_native_classifiers = lib->m_native_classifiers; /* for (auto native : lib->natives) { @@ -429,9 +429,7 @@ void VersionFile::applyTo(InstanceVersion *version) case RawLibrary::Prepend: { // QLOG_INFO() << "Adding lib " << lib->name; - const int startOfVersion = lib->m_name.lastIndexOf(':') + 1; - const int index = findLibrary( - version->libraries, QString(lib->m_name).replace(startOfVersion, INT_MAX, '*')); + const int index = findLibraryByName(version->libraries, lib->m_name); if (index < 0) { if (lib->insertType == RawLibrary::Append) @@ -446,8 +444,8 @@ void VersionFile::applyTo(InstanceVersion *version) else { auto otherLib = version->libraries.at(index); - const Util::Version ourVersion = lib->m_name.mid(startOfVersion, INT_MAX); - const Util::Version otherVersion = otherLib->version(); + const Util::Version ourVersion = lib->m_name.version(); + const Util::Version otherVersion = otherLib->m_name.version(); // if the existing version is a hard dependency we can either use it or // fail, but we can't change it if (otherLib->dependType == OneSixLibrary::Hard) @@ -497,16 +495,17 @@ void VersionFile::applyTo(InstanceVersion *version) } case RawLibrary::Replace: { - QString toReplace; + GradleSpecifier toReplace; if (lib->insertData.isEmpty()) { - const int startOfVersion = lib->m_name.lastIndexOf(':') + 1; - toReplace = QString(lib->m_name).replace(startOfVersion, INT_MAX, '*'); + toReplace = lib->m_name; } else + { toReplace = lib->insertData; + } // QLOG_INFO() << "Replacing lib " << toReplace << " with " << lib->name; - int index = findLibrary(version->libraries, toReplace); + int index = findLibraryByName(version->libraries, toReplace); if (index >= 0) { version->libraries.replace(index, OneSixLibrary::fromRawLibrary(lib)); @@ -521,7 +520,7 @@ void VersionFile::applyTo(InstanceVersion *version) } for (auto lib : removeLibs) { - int index = findLibrary(version->libraries, lib); + int index = findLibraryByName(version->libraries, lib); if (index >= 0) { // QLOG_INFO() << "Removing lib " << lib; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3b2b8b74..d56988e0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,6 +22,7 @@ endmacro() # Tests START # add_unit_test(pathutils tst_pathutils.cpp) +add_unit_test(gradlespecifier tst_gradlespecifier.cpp) add_unit_test(userutils tst_userutils.cpp) add_unit_test(inifile tst_inifile.cpp) add_unit_test(UpdateChecker tst_UpdateChecker.cpp) diff --git a/tests/tst_gradlespecifier.cpp b/tests/tst_gradlespecifier.cpp new file mode 100644 index 00000000..69dd54f7 --- /dev/null +++ b/tests/tst_gradlespecifier.cpp @@ -0,0 +1,77 @@ +#include <QTest> +#include "TestUtil.h" + +#include "logic/minecraft/GradleSpecifier.h" + +class GradleSpecifierTest : public QObject +{ + Q_OBJECT +private +slots: + void initTestCase() + { + + } + void cleanupTestCase() + { + + } + + void test_Positive_data() + { + QTest::addColumn<QString>("through"); + + QTest::newRow("3 parter") << "org.gradle.test.classifiers:service:1.0"; + QTest::newRow("classifier") << "org.gradle.test.classifiers:service:1.0:jdk15"; + QTest::newRow("jarextension") << "org.gradle.test.classifiers:service:1.0@jar"; + QTest::newRow("jarboth") << "org.gradle.test.classifiers:service:1.0:jdk15@jar"; + QTest::newRow("packxz") << "org.gradle.test.classifiers:service:1.0:jdk15@jar.pack.xz"; + } + void test_Positive() + { + QFETCH(QString, through); + + QString converted = GradleSpecifier(through); + + QCOMPARE(converted, through); + } + + void test_Path_data() + { + QTest::addColumn<QString>("spec"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("3 parter") << "group.id:artifact:1.0" << "group/id/artifact/1.0/artifact-1.0.jar"; + QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" << "id/software/doom/1.666/doom-1.666-demons.wad"; + } + void test_Path() + { + QFETCH(QString, spec); + QFETCH(QString, expected); + + QString converted = GradleSpecifier(spec).toPath(); + + QCOMPARE(converted, expected); + } + void test_Negative_data() + { + QTest::addColumn<QString>("input"); + + QTest::newRow("too many :") << "org:gradle.test:class:::ifiers:service:1.0::"; + QTest::newRow("nonsense") << "I like turtles"; + QTest::newRow("empty string") << ""; + QTest::newRow("missing version") << "herp.derp:artifact"; + } + void test_Negative() + { + QFETCH(QString, input); + + GradleSpecifier spec(input); + QVERIFY(!spec.valid()); + QCOMPARE(spec.operator QString(), QString("INVALID")); + } +}; + +QTEST_GUILESS_MAIN_MULTIMC(GradleSpecifierTest) + +#include "tst_gradlespecifier.moc" |