| @@ -0,0 +1,58 @@ | ||
| #pragma once | ||
|
|
||
| #include <cstddef> | ||
| #include <memory> | ||
|
|
||
| class Usable; | ||
|
|
||
| /** | ||
| * Base class for things that can be used by multiple other things and we want to track the use count. | ||
| * | ||
| * @see UseLock | ||
| */ | ||
| class Usable | ||
| { | ||
| friend class UseLock; | ||
| public: | ||
| std::size_t useCount() | ||
| { | ||
| return m_useCount; | ||
| } | ||
| bool isInUse() | ||
| { | ||
| return m_useCount > 0; | ||
| } | ||
| protected: | ||
| virtual void decrementUses() | ||
| { | ||
| m_useCount--; | ||
| } | ||
| virtual void incrementUses() | ||
| { | ||
| m_useCount++; | ||
| } | ||
| private: | ||
| std::size_t m_useCount = 0; | ||
| }; | ||
|
|
||
| /** | ||
| * Lock class to use for keeping track of uses of other things derived from Usable | ||
| * | ||
| * @see Usable | ||
| */ | ||
| class UseLock | ||
| { | ||
| public: | ||
| UseLock(std::shared_ptr<Usable> usable) | ||
| : m_usable(usable) | ||
| { | ||
| // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate. | ||
| m_usable->incrementUses(); | ||
| } | ||
| ~UseLock() | ||
| { | ||
| m_usable->decrementUses(); | ||
| } | ||
| private: | ||
| std::shared_ptr<Usable> m_usable; | ||
| }; |
| @@ -0,0 +1,7 @@ | ||
| #include "IIconList.h" | ||
|
|
||
| // blargh | ||
| IIconList::~IIconList() | ||
| { | ||
| } | ||
|
|
| @@ -0,0 +1,25 @@ | ||
| #pragma once | ||
|
|
||
| #include <QString> | ||
| #include <QStringList> | ||
| #include "multimc_logic_export.h" | ||
|
|
||
| enum IconType : unsigned | ||
| { | ||
| Builtin, | ||
| Transient, | ||
| FileBased, | ||
| ICONS_TOTAL, | ||
| ToBeDeleted | ||
| }; | ||
|
|
||
| class MULTIMC_LOGIC_EXPORT IIconList | ||
| { | ||
| public: | ||
| virtual ~IIconList(); | ||
| virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0; | ||
| virtual bool deleteIcon(const QString &key) = 0; | ||
| virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0; | ||
| virtual bool iconFileExists(const QString &key) const = 0; | ||
| virtual void installIcons(const QStringList &iconFiles) = 0; | ||
| }; |
| @@ -1,4 +1,4 @@ | ||
| /* Copyright 2013-2017 MultiMC Contributors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| @@ -0,0 +1,136 @@ | ||
| /* Copyright 2013-2017 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 "CheckJava.h" | ||
| #include <launch/LaunchTask.h> | ||
| #include <FileSystem.h> | ||
| #include <QStandardPaths> | ||
| #include <QFileInfo> | ||
| #include <sys.h> | ||
|
|
||
| void CheckJava::executeTask() | ||
| { | ||
| auto instance = m_parent->instance(); | ||
| auto settings = instance->settings(); | ||
| m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString()); | ||
| bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool(); | ||
|
|
||
| auto realJavaPath = QStandardPaths::findExecutable(m_javaPath); | ||
| if (realJavaPath.isEmpty()) | ||
| { | ||
| if (perInstance) | ||
| { | ||
| emit logLine( | ||
| tr("The java binary \"%1\" couldn't be found. Please fix the java path " | ||
| "override in the instance's settings or disable it.").arg(m_javaPath), | ||
| MessageLevel::Warning); | ||
| } | ||
| else | ||
| { | ||
| emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in " | ||
| "the settings.").arg(m_javaPath), | ||
| MessageLevel::Warning); | ||
| } | ||
| emitFailed(tr("Java path is not valid.")); | ||
| return; | ||
| } | ||
| else | ||
| { | ||
| emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC); | ||
| } | ||
|
|
||
| QFileInfo javaInfo(realJavaPath); | ||
| qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); | ||
| auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); | ||
| auto storedArchitecture = settings->get("JavaArchitecture").toString(); | ||
| auto storedVersion = settings->get("JavaVersion").toString(); | ||
| m_javaUnixTime = javaUnixTime; | ||
| // if timestamps are not the same, or something is missing, check! | ||
| if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0) | ||
| { | ||
| m_JavaChecker = std::make_shared<JavaChecker>(); | ||
| emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC); | ||
| connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); | ||
| m_JavaChecker->m_path = realJavaPath; | ||
| m_JavaChecker->performCheck(); | ||
| return; | ||
| } | ||
| else | ||
| { | ||
| auto verString = instance->settings()->get("JavaVersion").toString(); | ||
| auto archString = instance->settings()->get("JavaArchitecture").toString(); | ||
| printJavaInfo(verString, archString); | ||
| } | ||
| emitSucceeded(); | ||
| } | ||
|
|
||
| void CheckJava::checkJavaFinished(JavaCheckResult result) | ||
| { | ||
| switch (result.validity) | ||
| { | ||
| case JavaCheckResult::Validity::Errored: | ||
| { | ||
| // Error message displayed if java can't start | ||
| emit logLine(tr("Could not start java:"), MessageLevel::Error); | ||
| emit logLines(result.errorLog.split('\n'), MessageLevel::Error); | ||
| emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC); | ||
| printSystemInfo(false, false); | ||
| emitFailed(tr("Could not start java!")); | ||
| return; | ||
| } | ||
| case JavaCheckResult::Validity::ReturnedInvalidData: | ||
| { | ||
| emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error); | ||
| emit logLines(result.outLog.split('\n'), MessageLevel::Warning); | ||
| emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC); | ||
| printSystemInfo(false, false); | ||
| emitSucceeded(); | ||
| return; | ||
| } | ||
| case JavaCheckResult::Validity::Valid: | ||
| { | ||
| auto instance = m_parent->instance(); | ||
| printJavaInfo(result.javaVersion.toString(), result.mojangPlatform); | ||
| instance->settings()->set("JavaVersion", result.javaVersion.toString()); | ||
| instance->settings()->set("JavaArchitecture", result.mojangPlatform); | ||
| instance->settings()->set("JavaTimestamp", m_javaUnixTime); | ||
| emitSucceeded(); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void CheckJava::printJavaInfo(const QString& version, const QString& architecture) | ||
| { | ||
| emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC); | ||
| printSystemInfo(true, architecture == "64"); | ||
| } | ||
|
|
||
| void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit) | ||
| { | ||
| auto cpu64 = Sys::isCPU64bit(); | ||
| auto system64 = Sys::isSystem64bit(); | ||
| if(cpu64 != system64) | ||
| { | ||
| emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error); | ||
| } | ||
| if(javaIsKnown) | ||
| { | ||
| if(javaIs64bit != system64) | ||
| { | ||
| emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error); | ||
| } | ||
| } | ||
| } |
| @@ -1,4 +1,4 @@ | ||
| /* Copyright 2013-2017 MultiMC Contributors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| @@ -0,0 +1,144 @@ | ||
| #include "LogModel.h" | ||
|
|
||
| LogModel::LogModel(QObject *parent):QAbstractListModel(parent) | ||
| { | ||
| m_content.resize(m_maxLines); | ||
| } | ||
|
|
||
| int LogModel::rowCount(const QModelIndex &parent) const | ||
| { | ||
| if (parent.isValid()) | ||
| return 0; | ||
|
|
||
| return m_numLines; | ||
| } | ||
|
|
||
| QVariant LogModel::data(const QModelIndex &index, int role) const | ||
| { | ||
| if (index.row() < 0 || index.row() >= m_numLines) | ||
| return QVariant(); | ||
|
|
||
| auto row = index.row(); | ||
| auto realRow = (row + m_firstLine) % m_maxLines; | ||
| if (role == Qt::DisplayRole || role == Qt::EditRole) | ||
| { | ||
| return m_content[realRow].line; | ||
| } | ||
| if(role == LevelRole) | ||
| { | ||
| return m_content[realRow].level; | ||
| } | ||
|
|
||
| return QVariant(); | ||
| } | ||
|
|
||
| void LogModel::append(MessageLevel::Enum level, QString line) | ||
| { | ||
| if(m_suspended) | ||
| { | ||
| return; | ||
| } | ||
| int lineNum = (m_firstLine + m_numLines) % m_maxLines; | ||
| // overflow | ||
| if(m_numLines == m_maxLines) | ||
| { | ||
| if(m_stopOnOverflow) | ||
| { | ||
| // nothing more to do, the buffer is full | ||
| return; | ||
| } | ||
| beginRemoveRows(QModelIndex(), 0, 0); | ||
| m_firstLine = (m_firstLine + 1) % m_maxLines; | ||
| m_numLines --; | ||
| endRemoveRows(); | ||
| } | ||
| else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow) | ||
| { | ||
| level = MessageLevel::Fatal; | ||
| line = m_overflowMessage; | ||
| } | ||
| beginInsertRows(QModelIndex(), m_numLines, m_numLines); | ||
| m_numLines ++; | ||
| m_content[lineNum].level = level; | ||
| m_content[lineNum].line = line; | ||
| endInsertRows(); | ||
| } | ||
|
|
||
| void LogModel::suspend(bool suspend) | ||
| { | ||
| m_suspended = suspend; | ||
| } | ||
|
|
||
| void LogModel::clear() | ||
| { | ||
| beginResetModel(); | ||
| m_firstLine = 0; | ||
| m_numLines = 0; | ||
| endResetModel(); | ||
| } | ||
|
|
||
| QString LogModel::toPlainText() | ||
| { | ||
| QString out; | ||
| out.reserve(m_numLines * 80); | ||
| for(int i = 0; i < m_numLines; i++) | ||
| { | ||
| QString & line = m_content[(m_firstLine + i) % m_maxLines].line; | ||
| out.append(line + '\n'); | ||
| } | ||
| out.squeeze(); | ||
| return out; | ||
| } | ||
|
|
||
| void LogModel::setMaxLines(int maxLines) | ||
| { | ||
| // no-op | ||
| if(maxLines == m_maxLines) | ||
| { | ||
| return; | ||
| } | ||
| // if it all still fits in the buffer, just resize it | ||
| if(m_firstLine + m_numLines < maxLines) | ||
| { | ||
| m_maxLines = maxLines; | ||
| m_content.resize(maxLines); | ||
| return; | ||
| } | ||
| // otherwise, we need to reorganize the data because it crosses the wrap boundary | ||
| QVector<entry> newContent; | ||
| newContent.resize(maxLines); | ||
| if(m_numLines <= maxLines) | ||
| { | ||
| // if it all fits in the new buffer, just copy it over | ||
| for(int i = 0; i < m_numLines; i++) | ||
| { | ||
| newContent[i] = m_content[(m_firstLine + i) % m_maxLines]; | ||
| } | ||
| m_content.swap(newContent); | ||
| } | ||
| else | ||
| { | ||
| // if it doesn't fit, part of the data needs to be thrown away (the oldest log messages) | ||
| int lead = m_numLines - maxLines; | ||
| beginRemoveRows(QModelIndex(), 0, lead - 1); | ||
| for(int i = 0; i < maxLines; i++) | ||
| { | ||
| newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines]; | ||
| } | ||
| m_numLines = m_maxLines; | ||
| m_content.swap(newContent); | ||
| endRemoveRows(); | ||
| } | ||
| m_firstLine = 0; | ||
| m_maxLines = maxLines; | ||
| } | ||
|
|
||
| void LogModel::setStopOnOverflow(bool stop) | ||
| { | ||
| m_stopOnOverflow = stop; | ||
| } | ||
|
|
||
| void LogModel::setOverflowMessage(const QString& overflowMessage) | ||
| { | ||
| m_overflowMessage = overflowMessage; | ||
| } |
| @@ -0,0 +1,53 @@ | ||
| #pragma once | ||
|
|
||
| #include <QAbstractListModel> | ||
| #include <QString> | ||
| #include "MessageLevel.h" | ||
|
|
||
| #include <multimc_logic_export.h> | ||
|
|
||
| class MULTIMC_LOGIC_EXPORT LogModel : public QAbstractListModel | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| explicit LogModel(QObject *parent = 0); | ||
|
|
||
| int rowCount(const QModelIndex &parent = QModelIndex()) const; | ||
| QVariant data(const QModelIndex &index, int role) const; | ||
|
|
||
| void append(MessageLevel::Enum, QString line); | ||
| void clear(); | ||
| void suspend(bool suspend); | ||
|
|
||
| QString toPlainText(); | ||
|
|
||
| void setMaxLines(int maxLines); | ||
| void setStopOnOverflow(bool stop); | ||
| void setOverflowMessage(const QString & overflowMessage); | ||
|
|
||
| enum Roles | ||
| { | ||
| LevelRole = Qt::UserRole | ||
| }; | ||
|
|
||
| private /* types */: | ||
| struct entry | ||
| { | ||
| MessageLevel::Enum level; | ||
| QString line; | ||
| }; | ||
|
|
||
| private: /* data */ | ||
| QVector <entry> m_content; | ||
| int m_maxLines = 1000; | ||
| // first line in the circular buffer | ||
| int m_firstLine = 0; | ||
| // number of lines occupied in the circular buffer | ||
| int m_numLines = 0; | ||
| bool m_stopOnOverflow = false; | ||
| QString m_overflowMessage = "OVERFLOW"; | ||
| bool m_suspended = false; | ||
|
|
||
| private: | ||
| Q_DISABLE_COPY(LogModel) | ||
| }; |
| @@ -1,4 +1,4 @@ | ||
| /* Copyright 2013-2017 MultiMC Contributors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| @@ -1,4 +1,4 @@ | ||
| /* Copyright 2013-2017 MultiMC Contributors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| @@ -74,4 +74,4 @@ private | ||
|
|
||
| QTEST_GUILESS_MAIN(GradleSpecifierTest) | ||
|
|
||
| #include "GradleSpecifier_test.moc" | ||
| @@ -0,0 +1,295 @@ | ||
| #include "Library.h" | ||
| #include "MinecraftInstance.h" | ||
|
|
||
| #include <net/Download.h> | ||
| #include <net/ChecksumValidator.h> | ||
| #include <minecraft/forge/ForgeXzDownload.h> | ||
| #include <Env.h> | ||
| #include <FileSystem.h> | ||
|
|
||
|
|
||
| void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32, | ||
| QStringList& native64, const QString &overridePath) const | ||
| { | ||
| bool isLocal = (hint() == "local"); | ||
| auto actualPath = [&](QString relPath) | ||
| { | ||
| QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); | ||
| if(isLocal && !overridePath.isEmpty()) | ||
| { | ||
| QString fileName = out.fileName(); | ||
| auto fullPath = FS::PathCombine(overridePath, fileName); | ||
| qDebug() << fullPath; | ||
| QFileInfo fileinfo(fullPath); | ||
| if(fileinfo.exists()) | ||
| { | ||
| return fileinfo.absoluteFilePath(); | ||
| } | ||
| } | ||
| return out.absoluteFilePath(); | ||
| }; | ||
| if(m_mojangDownloads) | ||
| { | ||
| if(m_mojangDownloads->artifact) | ||
| { | ||
| auto artifact = m_mojangDownloads->artifact; | ||
| jar += actualPath(artifact->path); | ||
| } | ||
| if(!isNative()) | ||
| return; | ||
| if(m_nativeClassifiers.contains(system)) | ||
| { | ||
| auto nativeClassifier = m_nativeClassifiers[system]; | ||
| if(nativeClassifier.contains("${arch}")) | ||
| { | ||
| auto nat32Classifier = nativeClassifier; | ||
| nat32Classifier.replace("${arch}", "32"); | ||
| auto nat64Classifier = nativeClassifier; | ||
| nat64Classifier.replace("${arch}", "64"); | ||
| auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); | ||
| if(nat32info) | ||
| native32 += actualPath(nat32info->path); | ||
| auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); | ||
| if(nat64info) | ||
| native64 += actualPath(nat64info->path); | ||
| } | ||
| else | ||
| { | ||
| native += actualPath(m_mojangDownloads->getDownloadInfo(nativeClassifier)->path); | ||
| } | ||
| } | ||
| } | ||
| else | ||
| { | ||
| QString raw_storage = storageSuffix(system); | ||
| if(isNative()) | ||
| { | ||
| if (raw_storage.contains("${arch}")) | ||
| { | ||
| auto nat32Storage = raw_storage; | ||
| nat32Storage.replace("${arch}", "32"); | ||
| auto nat64Storage = raw_storage; | ||
| nat64Storage.replace("${arch}", "64"); | ||
| native32 += actualPath(nat32Storage); | ||
| native64 += actualPath(nat64Storage); | ||
| } | ||
| else | ||
| { | ||
| native += actualPath(raw_storage); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| jar += actualPath(raw_storage); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| QList< std::shared_ptr< NetAction > > Library::getDownloads(OpSys system, class HttpMetaCache* cache, | ||
| QStringList& failedFiles, const QString & overridePath) const | ||
| { | ||
| QList<NetActionPtr> out; | ||
| bool isAlwaysStale = (hint() == "always-stale"); | ||
| bool isLocal = (hint() == "local"); | ||
| bool isForge = (hint() == "forge-pack-xz"); | ||
|
|
||
| auto add_download = [&](QString storage, QString url, QString sha1 = QString()) | ||
| { | ||
| auto entry = cache->resolveEntry("libraries", storage); | ||
| if(isAlwaysStale) | ||
| { | ||
| entry->setStale(true); | ||
| } | ||
| if (!entry->isStale()) | ||
| return true; | ||
| if(isLocal) | ||
| { | ||
| if(!overridePath.isEmpty()) | ||
| { | ||
| QString fileName; | ||
| int position = storage.lastIndexOf('/'); | ||
| if(position == -1) | ||
| { | ||
| fileName = storage; | ||
| } | ||
| else | ||
| { | ||
| fileName = storage.mid(position); | ||
| } | ||
| auto fullPath = FS::PathCombine(overridePath, fileName); | ||
| QFileInfo fileinfo(fullPath); | ||
| if(fileinfo.exists()) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
| QFileInfo fileinfo(entry->getFullPath()); | ||
| if(!fileinfo.exists()) | ||
| { | ||
| failedFiles.append(entry->getFullPath()); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| Net::Download::Options options; | ||
| if(isAlwaysStale) | ||
| { | ||
| options |= Net::Download::Option::AcceptLocalFiles; | ||
| } | ||
| if (isForge) | ||
| { | ||
| out.append(ForgeXzDownload::make(storage, entry)); | ||
| } | ||
| else | ||
| { | ||
| if(sha1.size()) | ||
| { | ||
| auto rawSha1 = QByteArray::fromHex(sha1.toLatin1()); | ||
| auto dl = Net::Download::makeCached(url, entry, options); | ||
| dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); | ||
| out.append(dl); | ||
| } | ||
|
|
||
| else | ||
| out.append(Net::Download::makeCached(url, entry, options)); | ||
| } | ||
| return true; | ||
| }; | ||
|
|
||
| if(m_mojangDownloads) | ||
| { | ||
| if(m_mojangDownloads->artifact) | ||
| { | ||
| auto artifact = m_mojangDownloads->artifact; | ||
| add_download(artifact->path, artifact->url, artifact->sha1); | ||
| } | ||
| if(m_nativeClassifiers.contains(system)) | ||
| { | ||
| auto nativeClassifier = m_nativeClassifiers[system]; | ||
| if(nativeClassifier.contains("${arch}")) | ||
| { | ||
| auto nat32Classifier = nativeClassifier; | ||
| nat32Classifier.replace("${arch}", "32"); | ||
| auto nat64Classifier = nativeClassifier; | ||
| nat64Classifier.replace("${arch}", "64"); | ||
| auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); | ||
| if(nat32info) | ||
| add_download(nat32info->path, nat32info->url, nat32info->sha1); | ||
| auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); | ||
| if(nat64info) | ||
| add_download(nat64info->path, nat64info->url, nat64info->sha1); | ||
| } | ||
| else | ||
| { | ||
| auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier); | ||
| if(info) | ||
| { | ||
| add_download(info->path, info->url, info->sha1); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| else | ||
| { | ||
| QString raw_storage = storageSuffix(system); | ||
| auto raw_dl = [&](){ | ||
| if (!m_absoluteURL.isEmpty()) | ||
| { | ||
| return m_absoluteURL; | ||
| } | ||
|
|
||
| if (m_repositoryURL.isEmpty()) | ||
| { | ||
| return QString("https://" + URLConstants::LIBRARY_BASE) + raw_storage; | ||
| } | ||
|
|
||
| if(m_repositoryURL.endsWith('/')) | ||
| { | ||
| return m_repositoryURL + raw_storage; | ||
| } | ||
| else | ||
| { | ||
| return m_repositoryURL + QChar('/') + raw_storage; | ||
| } | ||
| }(); | ||
| if (raw_storage.contains("${arch}")) | ||
| { | ||
| QString cooked_storage = raw_storage; | ||
| QString cooked_dl = raw_dl; | ||
| add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); | ||
| cooked_storage = raw_storage; | ||
| cooked_dl = raw_dl; | ||
| add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); | ||
| } | ||
| else | ||
| { | ||
| add_download(raw_storage, raw_dl); | ||
| } | ||
| } | ||
| return out; | ||
| } | ||
|
|
||
| bool Library::isActive() const | ||
| { | ||
| bool result = true; | ||
| if (m_rules.empty()) | ||
| { | ||
| result = true; | ||
| } | ||
| else | ||
| { | ||
| RuleAction ruleResult = Disallow; | ||
| for (auto rule : m_rules) | ||
| { | ||
| RuleAction temp = rule->apply(this); | ||
| if (temp != Defer) | ||
| ruleResult = temp; | ||
| } | ||
| result = result && (ruleResult == Allow); | ||
| } | ||
| if (isNative()) | ||
| { | ||
| result = result && m_nativeClassifiers.contains(currentSystem); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| void Library::setStoragePrefix(QString prefix) | ||
| { | ||
| m_storagePrefix = prefix; | ||
| } | ||
|
|
||
| QString Library::defaultStoragePrefix() | ||
| { | ||
| return "libraries/"; | ||
| } | ||
|
|
||
| QString Library::storagePrefix() const | ||
| { | ||
| if(m_storagePrefix.isEmpty()) | ||
| { | ||
| return defaultStoragePrefix(); | ||
| } | ||
| return m_storagePrefix; | ||
| } | ||
|
|
||
| QString Library::storageSuffix(OpSys system) const | ||
| { | ||
| // non-native? use only the gradle specifier | ||
| if (!isNative()) | ||
| { | ||
| return m_name.toPath(); | ||
| } | ||
|
|
||
| // otherwise native, override classifiers. Mojang HACK! | ||
| GradleSpecifier nativeSpec = m_name; | ||
| if (m_nativeClassifiers.contains(system)) | ||
| { | ||
| nativeSpec.setClassifier(m_nativeClassifiers[system]); | ||
| } | ||
| else | ||
| { | ||
| nativeSpec.setClassifier("INVALID"); | ||
| } | ||
| return nativeSpec.toPath(); | ||
| } |
| @@ -0,0 +1,271 @@ | ||
| #include <QTest> | ||
| #include "TestUtil.h" | ||
|
|
||
| #include "minecraft/MojangVersionFormat.h" | ||
| #include "minecraft/onesix/OneSixVersionFormat.h" | ||
| #include "minecraft/Library.h" | ||
| #include "net/HttpMetaCache.h" | ||
| #include "FileSystem.h" | ||
|
|
||
| class LibraryTest : public QObject | ||
| { | ||
| Q_OBJECT | ||
| private: | ||
| LibraryPtr readMojangJson(const char *file) | ||
| { | ||
| auto path = QFINDTESTDATA(file); | ||
| QFile jsonFile(path); | ||
| jsonFile.open(QIODevice::ReadOnly); | ||
| auto data = jsonFile.readAll(); | ||
| jsonFile.close(); | ||
| return MojangVersionFormat::libraryFromJson(QJsonDocument::fromJson(data).object(), file); | ||
| } | ||
| // get absolute path to expected storage, assuming default cache prefix | ||
| QStringList getStorage(QString relative) | ||
| { | ||
| return {FS::PathCombine(cache->getBasePath("libraries"), relative)}; | ||
| } | ||
| private | ||
| slots: | ||
| void initTestCase() | ||
| { | ||
| cache.reset(new HttpMetaCache()); | ||
| cache->addBase("libraries", QDir("libraries").absolutePath()); | ||
| dataDir = QDir("data").absolutePath(); | ||
| } | ||
| void test_legacy() | ||
| { | ||
| Library test("test.package:testname:testversion"); | ||
| QCOMPARE(test.artifactPrefix(), QString("test.package:testname")); | ||
| QCOMPARE(test.isNative(), false); | ||
|
|
||
| QStringList jar, native, native32, native64; | ||
| test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, getStorage("test/package/testname/testversion/testname-testversion.jar")); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, {}); | ||
| QCOMPARE(native64, {}); | ||
| } | ||
| void test_legacy_url() | ||
| { | ||
| QStringList failedFiles; | ||
| Library test("test.package:testname:testversion"); | ||
| test.setRepositoryURL("file://foo/bar"); | ||
| auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(downloads.size(), 1); | ||
| QCOMPARE(failedFiles, {}); | ||
| NetActionPtr dl = downloads[0]; | ||
| QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar")); | ||
| } | ||
| void test_legacy_url_local_broken() | ||
| { | ||
| Library test("test.package:testname:testversion"); | ||
| QCOMPARE(test.isNative(), false); | ||
| QStringList failedFiles; | ||
| test.setHint("local"); | ||
| auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(downloads.size(), 0); | ||
| QCOMPARE(failedFiles, getStorage("test/package/testname/testversion/testname-testversion.jar")); | ||
| } | ||
| void test_legacy_url_local_override() | ||
| { | ||
| Library test("com.paulscode:codecwav:20101023"); | ||
| QCOMPARE(test.isNative(), false); | ||
| QStringList failedFiles; | ||
| test.setHint("local"); | ||
| auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString("data")); | ||
| QCOMPARE(downloads.size(), 0); | ||
| QCOMPARE(failedFiles.size(), 0); | ||
|
|
||
| QStringList jar, native, native32, native64; | ||
| test.getApplicableFiles(currentSystem, jar, native, native32, native64, QString("data")); | ||
| QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, {}); | ||
| QCOMPARE(native64, {}); | ||
| } | ||
| void test_legacy_native() | ||
| { | ||
| Library test("test.package:testname:testversion"); | ||
| test.m_nativeClassifiers[OpSys::Os_Linux]="linux"; | ||
| QCOMPARE(test.isNative(), true); | ||
| test.setRepositoryURL("file://foo/bar"); | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, {}); | ||
| QCOMPARE(native, getStorage("test/package/testname/testversion/testname-testversion-linux.jar")); | ||
| QCOMPARE(native32, {}); | ||
| QCOMPARE(native64, {}); | ||
| QStringList failedFiles; | ||
| auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(dls.size(), 1); | ||
| QCOMPARE(failedFiles, {}); | ||
| auto dl = dls[0]; | ||
| QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar")); | ||
| } | ||
| } | ||
| void test_legacy_native_arch() | ||
| { | ||
| Library test("test.package:testname:testversion"); | ||
| test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}"; | ||
| test.m_nativeClassifiers[OpSys::Os_OSX]="osx-${arch}"; | ||
| test.m_nativeClassifiers[OpSys::Os_Windows]="windows-${arch}"; | ||
| QCOMPARE(test.isNative(), true); | ||
| test.setRepositoryURL("file://foo/bar"); | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, {}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-linux-32.jar")); | ||
| QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")); | ||
| QStringList failedFiles; | ||
| auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(dls.size(), 2); | ||
| QCOMPARE(failedFiles, {}); | ||
| QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar")); | ||
| QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar")); | ||
| } | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test.getApplicableFiles(Os_Windows, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, {}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-windows-32.jar")); | ||
| QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-windows-64.jar")); | ||
| QStringList failedFiles; | ||
| auto dls = test.getDownloads(Os_Windows, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(dls.size(), 2); | ||
| QCOMPARE(failedFiles, {}); | ||
| QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar")); | ||
| QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar")); | ||
| } | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test.getApplicableFiles(Os_OSX, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, {}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-osx-32.jar")); | ||
| QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-osx-64.jar")); | ||
| QStringList failedFiles; | ||
| auto dls = test.getDownloads(Os_OSX, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(dls.size(), 2); | ||
| QCOMPARE(failedFiles, {}); | ||
| QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar")); | ||
| QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar")); | ||
| } | ||
| } | ||
| void test_legacy_native_arch_local_override() | ||
| { | ||
| Library test("test.package:testname:testversion"); | ||
| test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}"; | ||
| test.setHint("local"); | ||
| QCOMPARE(test.isNative(), true); | ||
| test.setRepositoryURL("file://foo/bar"); | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test.getApplicableFiles(Os_Linux, jar, native, native32, native64, QString("data")); | ||
| QCOMPARE(jar, {}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, {QFileInfo("data/testname-testversion-linux-32.jar").absoluteFilePath()}); | ||
| QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")); | ||
| QStringList failedFiles; | ||
| auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); | ||
| QCOMPARE(dls.size(), 0); | ||
| QCOMPARE(failedFiles, {getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")}); | ||
| } | ||
| } | ||
| void test_onenine() | ||
| { | ||
| auto test = readMojangJson("data/lib-simple.json"); | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, getStorage("com/paulscode/codecwav/20101023/codecwav-20101023.jar")); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, {}); | ||
| QCOMPARE(native64, {}); | ||
| } | ||
| { | ||
| QStringList failedFiles; | ||
| auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(dls.size(), 1); | ||
| QCOMPARE(failedFiles, {}); | ||
| QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar")); | ||
| } | ||
| test->setHint("local"); | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data")); | ||
| QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, {}); | ||
| QCOMPARE(native64, {}); | ||
| } | ||
| { | ||
| QStringList failedFiles; | ||
| auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); | ||
| QCOMPARE(dls.size(), 0); | ||
| QCOMPARE(failedFiles, {}); | ||
| } | ||
| } | ||
| void test_onenine_local_override() | ||
| { | ||
| auto test = readMojangJson("data/lib-simple.json"); | ||
| test->setHint("local"); | ||
| { | ||
| QStringList jar, native, native32, native64; | ||
| test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString("data")); | ||
| QCOMPARE(jar, {QFileInfo("data/codecwav-20101023.jar").absoluteFilePath()}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, {}); | ||
| QCOMPARE(native64, {}); | ||
| } | ||
| { | ||
| QStringList failedFiles; | ||
| auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles, QString("data")); | ||
| QCOMPARE(dls.size(), 0); | ||
| QCOMPARE(failedFiles, {}); | ||
| } | ||
| } | ||
| void test_onenine_native() | ||
| { | ||
| auto test = readMojangJson("data/lib-native.json"); | ||
| QStringList jar, native, native32, native64; | ||
| test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar")); | ||
| QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); | ||
| QCOMPARE(native32, {}); | ||
| QCOMPARE(native64, {}); | ||
| QStringList failedFiles; | ||
| auto dls = test->getDownloads(Os_OSX, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(dls.size(), 2); | ||
| QCOMPARE(failedFiles, {}); | ||
| QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar")); | ||
| QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); | ||
| } | ||
| void test_onenine_native_arch() | ||
| { | ||
| auto test = readMojangJson("data/lib-native-arch.json"); | ||
| QStringList jar, native, native32, native64; | ||
| test->getApplicableFiles(Os_Windows, jar, native, native32, native64, QString()); | ||
| QCOMPARE(jar, {}); | ||
| QCOMPARE(native, {}); | ||
| QCOMPARE(native32, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); | ||
| QCOMPARE(native64, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); | ||
| QStringList failedFiles; | ||
| auto dls = test->getDownloads(Os_Windows, cache.get(), failedFiles, QString()); | ||
| QCOMPARE(dls.size(), 2); | ||
| QCOMPARE(failedFiles, {}); | ||
| QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); | ||
| QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); | ||
| } | ||
| private: | ||
| std::unique_ptr<HttpMetaCache> cache; | ||
| QString dataDir; | ||
| }; | ||
|
|
||
| QTEST_GUILESS_MAIN(LibraryTest) | ||
|
|
||
| #include "Library_test.moc" |