aboutsummaryrefslogtreecommitdiff
path: root/launcher/minecraft
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/minecraft')
-rw-r--r--launcher/minecraft/GradleSpecifier.h55
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp48
-rw-r--r--launcher/minecraft/MinecraftInstance.h2
-rw-r--r--launcher/minecraft/MinecraftUpdate.cpp10
-rw-r--r--launcher/minecraft/MinecraftUpdate.h2
-rw-r--r--launcher/minecraft/OneSixVersionFormat.cpp37
-rw-r--r--launcher/minecraft/PackProfile.cpp44
-rw-r--r--launcher/minecraft/ProfileUtils.cpp53
-rw-r--r--launcher/minecraft/ProfileUtils.h38
-rw-r--r--launcher/minecraft/VersionFile.cpp11
-rw-r--r--launcher/minecraft/World.cpp51
-rw-r--r--launcher/minecraft/World.h6
-rw-r--r--launcher/minecraft/WorldList.cpp50
-rw-r--r--launcher/minecraft/auth/AccountData.cpp3
-rw-r--r--launcher/minecraft/auth/AccountList.cpp11
-rw-r--r--launcher/minecraft/auth/AccountTask.cpp2
-rw-r--r--launcher/minecraft/auth/AuthRequest.cpp43
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.cpp12
-rw-r--r--launcher/minecraft/auth/steps/LauncherLoginStep.cpp17
-rw-r--r--launcher/minecraft/auth/steps/MinecraftProfileStep.cpp17
-rw-r--r--launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp17
-rw-r--r--launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp21
-rw-r--r--launcher/minecraft/auth/steps/XboxProfileStep.cpp17
-rw-r--r--launcher/minecraft/auth/steps/XboxUserStep.cpp13
-rw-r--r--launcher/minecraft/launch/LauncherPartLaunch.cpp1
-rw-r--r--launcher/minecraft/mod/MetadataHandler.h22
-rw-r--r--launcher/minecraft/mod/Mod.cpp39
-rw-r--r--launcher/minecraft/mod/Mod.h12
-rw-r--r--launcher/minecraft/mod/ModFolderModel.cpp158
-rw-r--r--launcher/minecraft/mod/ModFolderModel.h19
-rw-r--r--launcher/minecraft/mod/tasks/LocalModParseTask.cpp9
-rw-r--r--launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp17
-rw-r--r--launcher/minecraft/mod/tasks/LocalModUpdateTask.h3
-rw-r--r--launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp50
-rw-r--r--launcher/minecraft/mod/tasks/ModFolderLoadTask.h5
-rw-r--r--launcher/minecraft/services/CapeChange.cpp39
-rw-r--r--launcher/minecraft/services/SkinDelete.cpp39
-rw-r--r--launcher/minecraft/services/SkinUpload.cpp39
38 files changed, 781 insertions, 251 deletions
diff --git a/launcher/minecraft/GradleSpecifier.h b/launcher/minecraft/GradleSpecifier.h
index d9bb0207..27514ab9 100644
--- a/launcher/minecraft/GradleSpecifier.h
+++ b/launcher/minecraft/GradleSpecifier.h
@@ -1,7 +1,43 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 <QString>
#include <QStringList>
+#include <QRegularExpression>
#include "DefaultVariable.h"
struct GradleSpecifier
@@ -25,20 +61,21 @@ struct GradleSpecifier
4 "jdk15"
5 "jar"
*/
- QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?");
- m_valid = matcher.exactMatch(value);
+ QRegularExpression matcher(QRegularExpression::anchoredPattern("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?"));
+ QRegularExpressionMatch match = matcher.match(value);
+ m_valid = match.hasMatch();
if(!m_valid) {
m_invalidValue = value;
return *this;
}
- auto elements = matcher.capturedTexts();
- m_groupId = elements[1];
- m_artifactId = elements[2];
- m_version = elements[3];
- m_classifier = elements[4];
- if(!elements[5].isEmpty())
+ auto elements = match.captured();
+ m_groupId = match.captured(1);
+ m_artifactId = match.captured(2);
+ m_version = match.captured(3);
+ m_classifier = match.captured(4);
+ if(match.lastCapturedIndex() >= 5)
{
- m_extension = elements[5];
+ m_extension = match.captured(5);
}
return *this;
}
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index fa6535b0..c677b677 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -480,25 +480,25 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
static QString replaceTokensIn(QString text, QMap<QString, QString> with)
{
+ // TODO: does this still work??
QString result;
- QRegExp token_regexp("\\$\\{(.+)\\}");
- token_regexp.setMinimal(true);
+ QRegularExpression token_regexp("\\$\\{(.+)\\}", QRegularExpression::InvertedGreedinessOption);
QStringList list;
- int tail = 0;
- int head = 0;
- while ((head = token_regexp.indexIn(text, head)) != -1)
+ QRegularExpressionMatchIterator i = token_regexp.globalMatch(text);
+ int lastCapturedEnd = 0;
+ while (i.hasNext())
{
- result.append(text.mid(tail, head - tail));
- QString key = token_regexp.cap(1);
+ QRegularExpressionMatch match = i.next();
+ result.append(text.mid(lastCapturedEnd, match.capturedStart()));
+ QString key = match.captured(1);
auto iter = with.find(key);
if (iter != with.end())
{
result.append(*iter);
}
- head += token_regexp.matchedLength();
- tail = head;
+ lastCapturedEnd = match.capturedEnd();
}
- result.append(text.mid(tail));
+ result.append(text.mid(lastCapturedEnd));
return result;
}
@@ -547,7 +547,11 @@ QStringList MinecraftInstance::processMinecraftArgs(
token_mapping["assets_root"] = absAssetsDir;
token_mapping["assets_index_name"] = assets->id;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QStringList parts = args_pattern.split(' ', Qt::SkipEmptyParts);
+#else
QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
+#endif
for (int i = 0; i < parts.length(); i++)
{
parts[i] = replaceTokensIn(parts[i], token_mapping);
@@ -703,24 +707,24 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
{
out << QString("%1:").arg(label);
auto modList = model.allMods();
- std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) {
- auto aName = a.fileinfo().completeBaseName();
- auto bName = b.fileinfo().completeBaseName();
+ std::sort(modList.begin(), modList.end(), [](Mod::Ptr a, Mod::Ptr b) {
+ auto aName = a->fileinfo().completeBaseName();
+ auto bName = b->fileinfo().completeBaseName();
return aName.localeAwareCompare(bName) < 0;
});
- for(auto & mod: modList)
+ for(auto mod: modList)
{
- if(mod.type() == Mod::MOD_FOLDER)
+ if(mod->type() == Mod::MOD_FOLDER)
{
- out << u8" [📁] " + mod.fileinfo().completeBaseName() + " (folder)";
+ out << u8" [🖿] " + mod->fileinfo().completeBaseName() + " (folder)";
continue;
}
- if(mod.enabled()) {
- out << u8" [✔️] " + mod.fileinfo().completeBaseName();
+ if(mod->enabled()) {
+ out << u8" [✔] " + mod->fileinfo().completeBaseName();
}
else {
- out << u8" [❌] " + mod.fileinfo().completeBaseName() + " (disabled)";
+ out << u8" [✘] " + mod->fileinfo().completeBaseName() + " (disabled)";
}
}
@@ -1139,16 +1143,16 @@ std::shared_ptr<GameOptions> MinecraftInstance::gameOptionsModel() const
return m_game_options;
}
-QList< Mod > MinecraftInstance::getJarMods() const
+QList<Mod*> MinecraftInstance::getJarMods() const
{
auto profile = m_components->getProfile();
- QList<Mod> mods;
+ QList<Mod*> mods;
for (auto jarmod : profile->getJarMods())
{
QStringList jar, temp1, temp2, temp3;
jarmod->getApplicableFiles(currentSystem, jar, temp1, temp2, temp3, jarmodsPath().absolutePath());
// QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem));
- mods.push_back(Mod(QFileInfo(jar[0])));
+ mods.push_back(new Mod(QFileInfo(jar[0])));
}
return mods;
}
diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h
index 6eadedfd..7a75f452 100644
--- a/launcher/minecraft/MinecraftInstance.h
+++ b/launcher/minecraft/MinecraftInstance.h
@@ -83,7 +83,7 @@ public:
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
QStringList extraArguments() override;
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
- QList<Mod> getJarMods() const;
+ QList<Mod*> getJarMods() const;
QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
/// get arguments passed to java
QStringList javaArguments();
diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp
index 0ce0c347..3a3aa864 100644
--- a/launcher/minecraft/MinecraftUpdate.cpp
+++ b/launcher/minecraft/MinecraftUpdate.cpp
@@ -43,7 +43,7 @@ void MinecraftUpdate::executeTask()
m_tasks.clear();
// create folders
{
- m_tasks.append(std::make_shared<FoldersTask>(m_inst));
+ m_tasks.append(new FoldersTask(m_inst));
}
// add metadata update task if necessary
@@ -53,23 +53,23 @@ void MinecraftUpdate::executeTask()
auto task = components->getCurrentTask();
if(task)
{
- m_tasks.append(task.unwrap());
+ m_tasks.append(task);
}
}
// libraries download
{
- m_tasks.append(std::make_shared<LibrariesTask>(m_inst));
+ m_tasks.append(new LibrariesTask(m_inst));
}
// FML libraries download and copy into the instance
{
- m_tasks.append(std::make_shared<FMLLibrariesTask>(m_inst));
+ m_tasks.append(new FMLLibrariesTask(m_inst));
}
// assets update
{
- m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst));
+ m_tasks.append(new AssetUpdateTask(m_inst));
}
if(!m_preFailure.isEmpty())
diff --git a/launcher/minecraft/MinecraftUpdate.h b/launcher/minecraft/MinecraftUpdate.h
index acf2eb86..c9cf8624 100644
--- a/launcher/minecraft/MinecraftUpdate.h
+++ b/launcher/minecraft/MinecraftUpdate.h
@@ -50,7 +50,7 @@ private:
private:
MinecraftInstance *m_inst = nullptr;
- QList<std::shared_ptr<Task>> m_tasks;
+ QList<Task::Ptr> m_tasks;
QString m_preFailure;
int m_currentTask = -1;
bool m_abort = false;
diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp
index 879f18c1..cec4a55b 100644
--- a/launcher/minecraft/OneSixVersionFormat.cpp
+++ b/launcher/minecraft/OneSixVersionFormat.cpp
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 "OneSixVersionFormat.h"
#include <Json.h>
#include "minecraft/Agent.h"
@@ -296,7 +331,7 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch
}
writeString(root, "appletClass", patch->appletClass);
writeStringList(root, "+tweakers", patch->addTweakers);
- writeStringList(root, "+traits", patch->traits.toList());
+ writeStringList(root, "+traits", patch->traits.values());
if (!patch->libraries.isEmpty())
{
QJsonArray array;
diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp
index 01d42b00..5e76b892 100644
--- a/launcher/minecraft/PackProfile.cpp
+++ b/launcher/minecraft/PackProfile.cpp
@@ -1,16 +1,36 @@
-/* Copyright 2013-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 <QFile>
@@ -688,7 +708,11 @@ void PackProfile::move(const int index, const MoveDirection direction)
return;
}
beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
+ d->components.swapItemsAt(index, theirIndex);
+#else
d->components.swap(index, theirIndex);
+#endif
endMoveRows();
invalidateLaunchProfile();
scheduleSave();
diff --git a/launcher/minecraft/ProfileUtils.cpp b/launcher/minecraft/ProfileUtils.cpp
index 8ca24cc8..03f8c198 100644
--- a/launcher/minecraft/ProfileUtils.cpp
+++ b/launcher/minecraft/ProfileUtils.cpp
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 "ProfileUtils.h"
#include "minecraft/VersionFilterData.h"
#include "minecraft/OneSixVersionFormat.h"
@@ -141,24 +176,6 @@ bool saveJsonFile(const QJsonDocument doc, const QString & filename)
return true;
}
-VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo)
-{
- QFile file(fileInfo.absoluteFilePath());
- if (!file.open(QFile::ReadOnly))
- {
- auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString());
- return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr);
- }
- QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll());
- file.close();
- if (doc.isNull())
- {
- file.remove();
- throw JSONValidationError(QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName()));
- }
- return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), false);
-}
-
void removeLwjglFromPatch(VersionFilePtr patch)
{
auto filter = [](QList<LibraryPtr>& libs)
diff --git a/launcher/minecraft/ProfileUtils.h b/launcher/minecraft/ProfileUtils.h
index 351c36cb..5b938784 100644
--- a/launcher/minecraft/ProfileUtils.h
+++ b/launcher/minecraft/ProfileUtils.h
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 "Library.h"
#include "VersionFile.h"
@@ -19,9 +54,6 @@ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder)
/// Save a JSON file (in any format)
bool saveJsonFile(const QJsonDocument doc, const QString & filename);
-/// Parse a version file in binary JSON format
-VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
-
/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files.
void removeLwjglFromPatch(VersionFilePtr patch);
diff --git a/launcher/minecraft/VersionFile.cpp b/launcher/minecraft/VersionFile.cpp
index f242fbe7..a9a0f7f4 100644
--- a/launcher/minecraft/VersionFile.cpp
+++ b/launcher/minecraft/VersionFile.cpp
@@ -89,14 +89,3 @@ void VersionFile::applyTo(LaunchProfile *profile)
}
profile->applyProblemSeverity(getProblemSeverity());
}
-
-/*
- auto theirVersion = profile->getMinecraftVersion();
- if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull())
- {
- if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1)
- {
- throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion);
- }
- }
-*/
diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp
index dc756e06..90fcf337 100644
--- a/launcher/minecraft/World.cpp
+++ b/launcher/minecraft/World.cpp
@@ -1,16 +1,36 @@
-/* Copyright 2015-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 <QDir>
@@ -33,12 +53,12 @@
#include <QCoreApplication>
-#include <nonstd/optional>
+#include <optional>
-using nonstd::optional;
-using nonstd::nullopt;
+using std::optional;
+using std::nullopt;
-GameType::GameType(nonstd::optional<int> original):
+GameType::GameType(std::optional<int> original):
original(original)
{
if(!original) {
@@ -321,7 +341,8 @@ bool World::install(const QString &to, const QString &name)
if(ok && !name.isEmpty() && m_actualName != name)
{
- World newWorld(finalPath);
+ QFileInfo finalPathInfo(finalPath);
+ World newWorld(finalPathInfo);
if(newWorld.isValid())
{
newWorld.rename(name);
diff --git a/launcher/minecraft/World.h b/launcher/minecraft/World.h
index 0f587620..8327253a 100644
--- a/launcher/minecraft/World.h
+++ b/launcher/minecraft/World.h
@@ -16,11 +16,11 @@
#pragma once
#include <QFileInfo>
#include <QDateTime>
-#include <nonstd/optional>
+#include <optional>
struct GameType {
GameType() = default;
- GameType (nonstd::optional<int> original);
+ GameType (std::optional<int> original);
QString toTranslatedString() const;
QString toLogString() const;
@@ -33,7 +33,7 @@ struct GameType {
Adventure,
Spectator
} type = Unknown;
- nonstd::optional<int> original;
+ std::optional<int> original;
};
class World
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 955609bf..aee7be35 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -1,16 +1,36 @@
-/* Copyright 2015-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 "WorldList.h"
@@ -195,7 +215,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
switch (column)
{
case SizeColumn:
- return qVariantFromValue<qlonglong>(world.bytes());
+ return QVariant::fromValue<qlonglong>(world.bytes());
default:
return data(index, Qt::DisplayRole);
@@ -215,7 +235,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
}
case SeedRole:
{
- return qVariantFromValue<qlonglong>(world.seed());
+ return QVariant::fromValue<qlonglong>(world.seed());
}
case NameRole:
{
@@ -227,7 +247,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
}
case SizeRole:
{
- return qVariantFromValue<qlonglong>(world.bytes());
+ return QVariant::fromValue<qlonglong>(world.bytes());
}
case IconFileRole:
{
@@ -301,7 +321,11 @@ public:
}
protected:
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ QVariant retrieveData(const QString &mimetype, QMetaType type) const
+#else
QVariant retrieveData(const QString &mimetype, QVariant::Type type) const
+#endif
{
QList<QUrl> urls;
for(auto &world: m_worlds)
diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp
index 3c7b193c..44f7e256 100644
--- a/launcher/minecraft/auth/AccountData.cpp
+++ b/launcher/minecraft/auth/AccountData.cpp
@@ -39,6 +39,7 @@
#include <QJsonArray>
#include <QDebug>
#include <QUuid>
+#include <QRegularExpression>
namespace {
void tokenToJSONV3(QJsonObject &parent, Katabasis::Token t, const char * tokenName) {
@@ -451,7 +452,7 @@ void AccountData::invalidateClientToken() {
if(type != AccountType::Mojang) {
return;
}
- yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{-}]"));
+ yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]"));
}
QString AccountData::profileId() const {
diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp
index 2b851e18..b3b57c74 100644
--- a/launcher/minecraft/auth/AccountList.cpp
+++ b/launcher/minecraft/auth/AccountList.cpp
@@ -109,8 +109,10 @@ QStringList AccountList::profileNames() const {
void AccountList::addAccount(const MinecraftAccountPtr account)
{
- // NOTE: Do not allow adding something that's already there
- if(m_accounts.contains(account)) {
+ // NOTE: Do not allow adding something that's already there. We shouldn't let it continue
+ // because of the signal / slot connections after this.
+ if (m_accounts.contains(account)) {
+ qDebug() << "Tried to add account that's already on the accounts list!";
return;
}
@@ -123,6 +125,8 @@ void AccountList::addAccount(const MinecraftAccountPtr account)
if(profileId.size()) {
auto existingAccount = findAccountByProfileId(profileId);
if(existingAccount != -1) {
+ qDebug() << "Replacing old account with a new one with the same profile ID!";
+
MinecraftAccountPtr existingAccountPtr = m_accounts[existingAccount];
m_accounts[existingAccount] = account;
if(m_defaultAccount == existingAccountPtr) {
@@ -138,9 +142,12 @@ void AccountList::addAccount(const MinecraftAccountPtr account)
// if we don't have this profileId yet, add the account to the end
int row = m_accounts.count();
+ qDebug() << "Inserting account at index" << row;
+
beginInsertRows(QModelIndex(), row, row);
m_accounts.append(account);
endInsertRows();
+
onListChanged();
}
diff --git a/launcher/minecraft/auth/AccountTask.cpp b/launcher/minecraft/auth/AccountTask.cpp
index 49b6e46f..4118c3c5 100644
--- a/launcher/minecraft/auth/AccountTask.cpp
+++ b/launcher/minecraft/auth/AccountTask.cpp
@@ -79,6 +79,8 @@ QString AccountTask::getStateMessage() const
bool AccountTask::changeState(AccountTaskState newState, QString reason)
{
m_taskState = newState;
+ // FIXME: virtual method invoked in constructor.
+ // We want that behavior, but maybe make it less weird?
setStatus(getStateMessage());
switch(newState) {
case AccountTaskState::STATE_CREATED: {
diff --git a/launcher/minecraft/auth/AuthRequest.cpp b/launcher/minecraft/auth/AuthRequest.cpp
index feface80..bb82e1e2 100644
--- a/launcher/minecraft/auth/AuthRequest.cpp
+++ b/launcher/minecraft/auth/AuthRequest.cpp
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 <cassert>
#include <QDebug>
@@ -20,7 +55,11 @@ void AuthRequest::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) {
reply_ = APPLICATION->network()->get(request_);
status_ = Requesting;
timedReplies_.add(new Katabasis::Reply(reply_, timeout));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ connect(reply_, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
+#else
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
+#endif
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()));
connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors);
}
@@ -31,7 +70,11 @@ void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int t
status_ = Requesting;
reply_ = APPLICATION->network()->post(request_, data_);
timedReplies_.add(new Katabasis::Reply(reply_, timeout));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ connect(reply_, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
+#else
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
+#endif
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()));
connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors);
connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64)));
diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp
index 9c8eb70b..73d570f1 100644
--- a/launcher/minecraft/auth/MinecraftAccount.cpp
+++ b/launcher/minecraft/auth/MinecraftAccount.cpp
@@ -40,7 +40,7 @@
#include <QUuid>
#include <QJsonObject>
#include <QJsonArray>
-#include <QRegExp>
+#include <QRegularExpression>
#include <QStringList>
#include <QJsonDocument>
@@ -53,7 +53,7 @@
#include "flows/Offline.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
- data.internalId = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
}
@@ -78,7 +78,7 @@ MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username
MinecraftAccountPtr account = new MinecraftAccount();
account->data.type = AccountType::Mojang;
account->data.yggdrasilToken.extra["userName"] = username;
- account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
return account;
}
@@ -97,10 +97,10 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
account->data.yggdrasilToken.extra["userName"] = username;
- account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
account->data.minecraftEntitlement.ownsMinecraft = true;
account->data.minecraftEntitlement.canPlayMinecraft = true;
- account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
account->data.minecraftProfile.name = username;
account->data.minecraftProfile.validity = Katabasis::Validity::Certain;
return account;
@@ -238,7 +238,7 @@ void MinecraftAccount::authFailed(QString reason)
}
bool MinecraftAccount::isActive() const {
- return m_currentTask;
+ return !m_currentTask.isNull();
}
bool MinecraftAccount::shouldRefresh() const {
diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp
index f5697223..8c53f037 100644
--- a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp
+++ b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp
@@ -5,6 +5,7 @@
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
#include "minecraft/auth/AccountTask.h"
+#include "net/NetUtils.h"
LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {
@@ -58,10 +59,18 @@ void LauncherLoginStep::onRequestDone(
#ifndef NDEBUG
qDebug() << data;
#endif
- emit finished(
- AccountTaskState::STATE_FAILED_SOFT,
- tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)
- );
+ if (Net::isApplicationError(error)) {
+ emit finished(
+ AccountTaskState::STATE_FAILED_SOFT,
+ tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)
+ );
+ }
+ else {
+ emit finished(
+ AccountTaskState::STATE_OFFLINE,
+ tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)
+ );
+ }
return;
}
diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp
index add91659..b39b9326 100644
--- a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp
+++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp
@@ -4,6 +4,7 @@
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
+#include "net/NetUtils.h"
MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {
@@ -64,10 +65,18 @@ void MinecraftProfileStep::onRequestDone(
qWarning() << " Response:";
qWarning() << QString::fromUtf8(data);
- emit finished(
- AccountTaskState::STATE_FAILED_SOFT,
- tr("Minecraft Java profile acquisition failed.")
- );
+ if (Net::isApplicationError(error)) {
+ emit finished(
+ AccountTaskState::STATE_FAILED_SOFT,
+ tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
+ );
+ }
+ else {
+ emit finished(
+ AccountTaskState::STATE_OFFLINE,
+ tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
+ );
+ }
return;
}
if(!Parsers::parseMinecraftProfile(data, m_data->minecraftProfile)) {
diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp
index d3035272..6a1eb7a0 100644
--- a/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp
+++ b/launcher/minecraft/auth/steps/MinecraftProfileStepMojang.cpp
@@ -4,6 +4,7 @@
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
+#include "net/NetUtils.h"
MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {
@@ -67,10 +68,18 @@ void MinecraftProfileStepMojang::onRequestDone(
qWarning() << " Response:";
qWarning() << QString::fromUtf8(data);
- emit finished(
- AccountTaskState::STATE_FAILED_SOFT,
- tr("Minecraft Java profile acquisition failed.")
- );
+ if (Net::isApplicationError(error)) {
+ emit finished(
+ AccountTaskState::STATE_FAILED_SOFT,
+ tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
+ );
+ }
+ else {
+ emit finished(
+ AccountTaskState::STATE_OFFLINE,
+ tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)
+ );
+ }
return;
}
if(!Parsers::parseMinecraftProfileMojang(data, m_data->minecraftProfile)) {
diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp
index 589768e3..14bde47e 100644
--- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp
+++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp
@@ -6,6 +6,7 @@
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
+#include "net/NetUtils.h"
XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Katabasis::Token *token, QString relyingParty, QString authorizationKind):
AuthStep(data),
@@ -62,10 +63,24 @@ void XboxAuthorizationStep::onRequestDone(
#endif
if (error != QNetworkReply::NoError) {
qWarning() << "Reply error:" << error;
- if(!processSTSError(error, data, headers)) {
+ if (Net::isApplicationError(error)) {
+ if(!processSTSError(error, data, headers)) {
+ emit finished(
+ AccountTaskState::STATE_FAILED_SOFT,
+ tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error)
+ );
+ }
+ else {
+ emit finished(
+ AccountTaskState::STATE_FAILED_SOFT,
+ tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)
+ );
+ }
+ }
+ else {
emit finished(
- AccountTaskState::STATE_FAILED_SOFT,
- tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error)
+ AccountTaskState::STATE_OFFLINE,
+ tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)
);
}
return;
diff --git a/launcher/minecraft/auth/steps/XboxProfileStep.cpp b/launcher/minecraft/auth/steps/XboxProfileStep.cpp
index 9f50138e..738fe1db 100644
--- a/launcher/minecraft/auth/steps/XboxProfileStep.cpp
+++ b/launcher/minecraft/auth/steps/XboxProfileStep.cpp
@@ -6,6 +6,7 @@
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
+#include "net/NetUtils.h"
XboxProfileStep::XboxProfileStep(AccountData* data) : AuthStep(data) {
@@ -58,10 +59,18 @@ void XboxProfileStep::onRequestDone(
#ifndef NDEBUG
qDebug() << data;
#endif
- finished(
- AccountTaskState::STATE_FAILED_SOFT,
- tr("Failed to retrieve the Xbox profile.")
- );
+ if (Net::isApplicationError(error)) {
+ emit finished(
+ AccountTaskState::STATE_FAILED_SOFT,
+ tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)
+ );
+ }
+ else {
+ emit finished(
+ AccountTaskState::STATE_OFFLINE,
+ tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)
+ );
+ }
return;
}
diff --git a/launcher/minecraft/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp
index a38a28e4..53069597 100644
--- a/launcher/minecraft/auth/steps/XboxUserStep.cpp
+++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp
@@ -4,6 +4,7 @@
#include "minecraft/auth/AuthRequest.h"
#include "minecraft/auth/Parsers.h"
+#include "net/NetUtils.h"
XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) {
@@ -53,7 +54,17 @@ void XboxUserStep::onRequestDone(
if (error != QNetworkReply::NoError) {
qWarning() << "Reply error:" << error;
- emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed."));
+ if (Net::isApplicationError(error)) {
+ emit finished(AccountTaskState::STATE_FAILED_SOFT,
+ tr("XBox user authentication failed: %1").arg(requestor->errorString_)
+ );
+ }
+ else {
+ emit finished(
+ AccountTaskState::STATE_OFFLINE,
+ tr("XBox user authentication failed: %1").arg(requestor->errorString_)
+ );
+ }
return;
}
diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp
index 9965ef89..63e4d90f 100644
--- a/launcher/minecraft/launch/LauncherPartLaunch.cpp
+++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp
@@ -36,7 +36,6 @@
#include "LauncherPartLaunch.h"
#include <QStandardPaths>
-#include <QRegularExpression>
#include "launch/LaunchTask.h"
#include "minecraft/MinecraftInstance.h"
diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h
index 56962818..39723b49 100644
--- a/launcher/minecraft/mod/MetadataHandler.h
+++ b/launcher/minecraft/mod/MetadataHandler.h
@@ -37,9 +37,9 @@ class Metadata {
return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version);
}
- static auto create(QDir& index_dir, Mod& internal_mod) -> ModStruct
+ static auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct
{
- return Packwiz::V1::createModFormat(index_dir, internal_mod);
+ return Packwiz::V1::createModFormat(index_dir, internal_mod, mod_slug);
}
static void update(QDir& index_dir, ModStruct& mod)
@@ -47,13 +47,23 @@ class Metadata {
Packwiz::V1::updateModIndex(index_dir, mod);
}
- static void remove(QDir& index_dir, QString& mod_name)
+ static void remove(QDir& index_dir, QString mod_slug)
{
- Packwiz::V1::deleteModIndex(index_dir, mod_name);
+ Packwiz::V1::deleteModIndex(index_dir, mod_slug);
}
- static auto get(QDir& index_dir, QString& mod_name) -> ModStruct
+ static void remove(QDir& index_dir, QVariant& mod_id)
{
- return Packwiz::V1::getIndexForMod(index_dir, mod_name);
+ Packwiz::V1::deleteModIndex(index_dir, mod_id);
+ }
+
+ static auto get(QDir& index_dir, QString mod_slug) -> ModStruct
+ {
+ return Packwiz::V1::getIndexForMod(index_dir, mod_slug);
+ }
+
+ static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct
+ {
+ return Packwiz::V1::getIndexForMod(index_dir, mod_id);
}
};
diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp
index 742709e3..588d76e3 100644
--- a/launcher/minecraft/mod/Mod.cpp
+++ b/launcher/minecraft/mod/Mod.cpp
@@ -150,25 +150,30 @@ void Mod::setStatus(ModStatus status)
m_temp_status = status;
}
}
-void Mod::setMetadata(Metadata::ModStruct* metadata)
+void Mod::setMetadata(const Metadata::ModStruct& metadata)
{
if (status() == ModStatus::NoMetadata)
setStatus(ModStatus::Installed);
if (m_localDetails) {
- m_localDetails->metadata.reset(metadata);
+ m_localDetails->metadata = std::make_shared<Metadata::ModStruct>(std::move(metadata));
} else {
- m_temp_metadata.reset(metadata);
+ m_temp_metadata = std::make_shared<Metadata::ModStruct>(std::move(metadata));
}
}
-auto Mod::destroy(QDir& index_dir) -> bool
+auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
{
- auto n = name();
- // FIXME: This can fail to remove the metadata if the
- // "ModMetadataDisabled" setting is on, since there could
- // be a name mismatch!
- Metadata::remove(index_dir, n);
+ if (!preserve_metadata) {
+ qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
+
+ if (metadata()) {
+ Metadata::remove(index_dir, metadata()->slug);
+ } else {
+ auto n = name();
+ Metadata::remove(index_dir, n);
+ }
+ }
m_type = MOD_UNKNOWN;
return FS::deletePath(m_file.filePath());
@@ -182,9 +187,12 @@ auto Mod::details() const -> const ModDetails&
auto Mod::name() const -> QString
{
auto d_name = details().name;
- if (!d_name.isEmpty()) {
+ if (!d_name.isEmpty())
return d_name;
- }
+
+ if (metadata())
+ return metadata()->name;
+
return m_name;
}
@@ -235,11 +243,10 @@ void Mod::finishResolvingWithDetails(std::shared_ptr<ModDetails> details)
m_resolved = true;
m_localDetails = details;
+ setStatus(m_temp_status);
+
if (m_localDetails && m_temp_metadata && m_temp_metadata->isValid()) {
- m_localDetails->metadata = m_temp_metadata;
- if (status() == ModStatus::NoMetadata)
- setStatus(ModStatus::Installed);
+ setMetadata(*m_temp_metadata);
+ m_temp_metadata.reset();
}
-
- setStatus(m_temp_status);
}
diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h
index 5f9c4684..7a13e44b 100644
--- a/launcher/minecraft/mod/Mod.h
+++ b/launcher/minecraft/mod/Mod.h
@@ -39,10 +39,12 @@
#include <QFileInfo>
#include <QList>
+#include "QObjectPtr.h"
#include "ModDetails.h"
-class Mod
+class Mod : public QObject
{
+ Q_OBJECT
public:
enum ModType
{
@@ -53,6 +55,8 @@ public:
MOD_LITEMOD, //!< The mod is a litemod
};
+ using Ptr = shared_qobject_ptr<Mod>;
+
Mod() = default;
Mod(const QFileInfo &file);
explicit Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata);
@@ -77,12 +81,12 @@ public:
auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>;
void setStatus(ModStatus status);
- void setMetadata(Metadata::ModStruct* metadata);
+ void setMetadata(const Metadata::ModStruct& metadata);
auto enable(bool value) -> bool;
// delete all the files of this mod
- auto destroy(QDir& index_dir) -> bool;
+ auto destroy(QDir& index_dir, bool preserve_metadata = false) -> bool;
// change the mod's filesystem path (used by mod lists for *MAGIC* purposes)
void repath(const QFileInfo &file);
@@ -111,7 +115,7 @@ protected:
std::shared_ptr<Metadata::ModStruct> m_temp_metadata;
/* Set the mod status while it doesn't have local details just yet */
- ModStatus m_temp_status = ModStatus::NotInstalled;
+ ModStatus m_temp_status = ModStatus::NoMetadata;
std::shared_ptr<ModDetails> m_localDetails;
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index ded2d3a2..d4c5e819 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -63,17 +63,26 @@ void ModFolderModel::startWatching()
if(is_watching)
return;
+ // Remove orphaned metadata next time
+ m_first_folder_load = true;
+
update();
+ // Watch the mods folder
is_watching = m_watcher->addPath(m_dir.absolutePath());
- if (is_watching)
- {
+ if (is_watching) {
qDebug() << "Started watching " << m_dir.absolutePath();
- }
- else
- {
+ } else {
qDebug() << "Failed to start watching " << m_dir.absolutePath();
}
+
+ // Watch the mods index folder
+ is_watching = m_watcher->addPath(indexDir().absolutePath());
+ if (is_watching) {
+ qDebug() << "Started watching " << indexDir().absolutePath();
+ } else {
+ qDebug() << "Failed to start watching " << indexDir().absolutePath();
+ }
}
void ModFolderModel::stopWatching()
@@ -82,14 +91,18 @@ void ModFolderModel::stopWatching()
return;
is_watching = !m_watcher->removePath(m_dir.absolutePath());
- if (!is_watching)
- {
+ if (!is_watching) {
qDebug() << "Stopped watching " << m_dir.absolutePath();
- }
- else
- {
+ } else {
qDebug() << "Failed to stop watching " << m_dir.absolutePath();
}
+
+ is_watching = !m_watcher->removePath(indexDir().absolutePath());
+ if (!is_watching) {
+ qDebug() << "Stopped watching " << indexDir().absolutePath();
+ } else {
+ qDebug() << "Failed to stop watching " << indexDir().absolutePath();
+ }
}
bool ModFolderModel::update()
@@ -103,7 +116,8 @@ bool ModFolderModel::update()
}
auto index_dir = indexDir();
- auto task = new ModFolderLoadTask(dir(), index_dir, m_is_indexed);
+ auto task = new ModFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load);
+ m_first_folder_load = false;
m_update = task->result();
@@ -116,27 +130,36 @@ bool ModFolderModel::update()
void ModFolderModel::finishUpdate()
{
- QSet<QString> currentSet = modsIndex.keys().toSet();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ auto currentList = modsIndex.keys();
+ QSet<QString> currentSet(currentList.begin(), currentList.end());
auto & newMods = m_update->mods;
+ auto newList = newMods.keys();
+ QSet<QString> newSet(newList.begin(), newList.end());
+#else
+ QSet<QString> currentSet = modsIndex.keys().toSet();
+ auto& newMods = m_update->mods;
QSet<QString> newSet = newMods.keys().toSet();
+#endif
// see if the kept mods changed in some way
{
QSet<QString> kept = currentSet;
kept.intersect(newSet);
- for(auto & keptMod: kept) {
- auto & newMod = newMods[keptMod];
+ for(auto& keptMod : kept) {
+ auto newMod = newMods[keptMod];
auto row = modsIndex[keptMod];
- auto & currentMod = mods[row];
- if(newMod.dateTimeChanged() == currentMod.dateTimeChanged()) {
+ auto currentMod = mods[row];
+ if(newMod->dateTimeChanged() == currentMod->dateTimeChanged()) {
// no significant change, ignore...
continue;
}
- auto & oldMod = mods[row];
- if(oldMod.isResolving()) {
- activeTickets.remove(oldMod.resolutionTicket());
+ auto oldMod = mods[row];
+ if(oldMod->isResolving()) {
+ activeTickets.remove(oldMod->resolutionTicket());
}
- oldMod = newMod;
+
+ mods[row] = newMod;
resolveMod(mods[row]);
emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1));
}
@@ -155,9 +178,10 @@ void ModFolderModel::finishUpdate()
int removedIndex = *iter;
beginRemoveRows(QModelIndex(), removedIndex, removedIndex);
auto removedIter = mods.begin() + removedIndex;
- if(removedIter->isResolving()) {
- activeTickets.remove(removedIter->resolutionTicket());
+ if((*removedIter)->isResolving()) {
+ activeTickets.remove((*removedIter)->resolutionTicket());
}
+
mods.erase(removedIter);
endRemoveRows();
}
@@ -167,20 +191,24 @@ void ModFolderModel::finishUpdate()
{
QSet<QString> added = newSet;
added.subtract(currentSet);
- beginInsertRows(QModelIndex(), mods.size(), mods.size() + added.size() - 1);
- for(auto & addedMod: added) {
- mods.append(newMods[addedMod]);
- resolveMod(mods.last());
+
+ // When you have a Qt build with assertions turned on, proceeding here will abort the application
+ if (added.size() > 0) {
+ beginInsertRows(QModelIndex(), mods.size(), mods.size() + added.size() - 1);
+ for (auto& addedMod : added) {
+ mods.append(newMods[addedMod]);
+ resolveMod(mods.last());
+ }
+ endInsertRows();
}
- endInsertRows();
}
// update index
{
modsIndex.clear();
int idx = 0;
- for(auto & mod: mods) {
- modsIndex[mod.internal_id()] = idx;
+ for(auto mod: mods) {
+ modsIndex[mod->internal_id()] = idx;
idx++;
}
}
@@ -195,17 +223,17 @@ void ModFolderModel::finishUpdate()
}
}
-void ModFolderModel::resolveMod(Mod& m)
+void ModFolderModel::resolveMod(Mod::Ptr m)
{
- if(!m.shouldResolve()) {
+ if(!m->shouldResolve()) {
return;
}
- auto task = new LocalModParseTask(nextResolutionTicket, m.type(), m.fileinfo());
+ auto task = new LocalModParseTask(nextResolutionTicket, m->type(), m->fileinfo());
auto result = task->result();
- result->id = m.internal_id();
+ result->id = m->internal_id();
activeTickets.insert(nextResolutionTicket, result);
- m.setResolving(true, nextResolutionTicket);
+ m->setResolving(true, nextResolutionTicket);
nextResolutionTicket++;
QThreadPool *threadPool = QThreadPool::globalInstance();
connect(task, &LocalModParseTask::finished, this, &ModFolderModel::finishModParse);
@@ -221,8 +249,8 @@ void ModFolderModel::finishModParse(int token)
auto result = *iter;
activeTickets.remove(token);
int row = modsIndex[result->id];
- auto & mod = mods[row];
- mod.finishResolvingWithDetails(result->details);
+ auto mod = mods[row];
+ mod->finishResolvingWithDetails(result->details);
emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1));
}
@@ -247,6 +275,18 @@ bool ModFolderModel::isValid()
return m_dir.exists() && m_dir.isReadable();
}
+auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList<Mod::Ptr>
+{
+ QList<Mod::Ptr> selected_mods;
+ for (auto i : indexes) {
+ if(i.column() != 0)
+ continue;
+
+ selected_mods.push_back(mods[i.row()]);
+ }
+ return selected_mods;
+}
+
// FIXME: this does not take disabled mod (with extra .disable extension) into account...
bool ModFolderModel::installMod(const QString &filename)
{
@@ -305,7 +345,8 @@ bool ModFolderModel::installMod(const QString &filename)
return false;
}
FS::updateTimestamp(newpath);
- installedMod.repath(newpath);
+ QFileInfo newpathInfo(newpath);
+ installedMod.repath(newpathInfo);
update();
return true;
}
@@ -323,13 +364,28 @@ bool ModFolderModel::installMod(const QString &filename)
qWarning() << "Copy of folder from" << originalPath << "to" << newpath << "has (potentially partially) failed.";
return false;
}
- installedMod.repath(newpath);
+ QFileInfo newpathInfo(newpath);
+ installedMod.repath(newpathInfo);
update();
return true;
}
return false;
}
+bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
+{
+
+ for(auto mod : allMods()){
+ if(mod->fileinfo().fileName() == filename){
+ auto index_dir = indexDir();
+ mod->destroy(index_dir, preserve_metadata);
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool ModFolderModel::setModStatus(const QModelIndexList& indexes, ModStatusAction enable)
{
if(interaction_disabled) {
@@ -363,9 +419,9 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
if(i.column() != 0) {
continue;
}
- Mod &m = mods[i.row()];
+ auto m = mods[i.row()];
auto index_dir = indexDir();
- m.destroy(index_dir);
+ m->destroy(index_dir);
}
return true;
}
@@ -392,9 +448,9 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
switch (column)
{
case NameColumn:
- return mods[row].name();
+ return mods[row]->name();
case VersionColumn: {
- switch(mods[row].type()) {
+ switch(mods[row]->type()) {
case Mod::MOD_FOLDER:
return tr("Folder");
case Mod::MOD_SINGLEFILE:
@@ -402,23 +458,23 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
default:
break;
}
- return mods[row].version();
+ return mods[row]->version();
}
case DateColumn:
- return mods[row].dateTimeChanged();
+ return mods[row]->dateTimeChanged();
default:
return QVariant();
}
case Qt::ToolTipRole:
- return mods[row].internal_id();
+ return mods[row]->internal_id();
case Qt::CheckStateRole:
switch (column)
{
case ActiveColumn:
- return mods[row].enabled() ? Qt::Checked : Qt::Unchecked;
+ return mods[row]->enabled() ? Qt::Checked : Qt::Unchecked;
default:
return QVariant();
}
@@ -458,20 +514,20 @@ bool ModFolderModel::setModStatus(int row, ModFolderModel::ModStatusAction actio
break;
case Toggle:
default:
- desiredStatus = !mod.enabled();
+ desiredStatus = !mod->enabled();
break;
}
- if(desiredStatus == mod.enabled()) {
+ if(desiredStatus == mod->enabled()) {
return true;
}
// preserve the row, but change its ID
- auto oldId = mod.internal_id();
- if(!mod.enable(!mod.enabled())) {
+ auto oldId = mod->internal_id();
+ if(!mod->enable(!mod->enabled())) {
return false;
}
- auto newId = mod.internal_id();
+ auto newId = mod->internal_id();
if(modsIndex.contains(newId)) {
// NOTE: this could handle a corner case, where we are overwriting a file, because the same 'mod' exists both enabled and disabled
// But is it necessary?
diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h
index 24b4d358..3d6efac3 100644
--- a/launcher/minecraft/mod/ModFolderModel.h
+++ b/launcher/minecraft/mod/ModFolderModel.h
@@ -101,13 +101,13 @@ public:
{
return size() == 0;
}
- Mod &operator[](size_t index)
+ Mod& operator[](size_t index)
{
- return mods[index];
+ return *mods[index];
}
- const Mod &at(size_t index) const
+ const Mod& at(size_t index) const
{
- return mods.at(index);
+ return *mods.at(index);
}
/// Reloads the mod list and returns true if the list changed.
@@ -118,6 +118,8 @@ public:
*/
bool installMod(const QString& filename);
+ bool uninstallMod(const QString& filename, bool preserve_metadata = false);
+
/// Deletes all the selected mods
bool deleteMods(const QModelIndexList &indexes);
@@ -139,11 +141,13 @@ public:
return { QString("%1/.index").arg(dir().absolutePath()) };
}
- const QList<Mod> & allMods()
+ const QList<Mod::Ptr>& allMods()
{
return mods;
}
+ auto selectedMods(QModelIndexList& indexes) -> QList<Mod::Ptr>;
+
public slots:
void disableInteraction(bool disabled);
@@ -157,7 +161,7 @@ signals:
void updateFinished();
private:
- void resolveMod(Mod& m);
+ void resolveMod(Mod::Ptr m);
bool setModStatus(int index, ModStatusAction action);
protected:
@@ -168,8 +172,9 @@ protected:
bool interaction_disabled = false;
QDir m_dir;
bool m_is_indexed;
+ bool m_first_folder_load = true;
QMap<QString, int> modsIndex;
QMap<int, LocalModParseTask::ResultPtr> activeTickets;
int nextResolutionTicket = 0;
- QList<Mod> mods;
+ QList<Mod::Ptr> mods;
};
diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
index 3354732b..1519f49d 100644
--- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
+++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp
@@ -4,6 +4,7 @@
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
+#include <QString>
#include <quazip/quazip.h>
#include <quazip/quazipfile.h>
#include <toml.h>
@@ -71,7 +72,13 @@ std::shared_ptr<ModDetails> ReadMCModInfo(QByteArray contents)
if(val.isUndefined()) {
val = jsonDoc.object().value("modListVersion");
}
- int version = val.toDouble();
+
+ int version = Json::ensureInteger(val, -1);
+
+ // Some mods set the number with "", so it's a String instead
+ if (version < 0)
+ version = Json::ensureString(val, "").toInt();
+
if (version != 2)
{
qCritical() << "BAD stuff happened to mod json:";
diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp
index 1bdecb8c..4b878918 100644
--- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp
+++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp
@@ -44,10 +44,21 @@ void LocalModUpdateTask::executeTask()
{
setStatus(tr("Updating index for mod:\n%1").arg(m_mod.name));
- auto pw_mod = Metadata::create(m_index_dir, m_mod, m_mod_version);
- Metadata::update(m_index_dir, pw_mod);
+ auto old_metadata = Metadata::get(m_index_dir, m_mod.addonId);
+ if (old_metadata.isValid()) {
+ emit hasOldMod(old_metadata.name, old_metadata.filename);
+ if (m_mod.slug.isEmpty())
+ m_mod.slug = old_metadata.slug;
+ }
- emitSucceeded();
+ auto pw_mod = Metadata::create(m_index_dir, m_mod, m_mod_version);
+ if (pw_mod.isValid()) {
+ Metadata::update(m_index_dir, pw_mod);
+ emitSucceeded();
+ } else {
+ qCritical() << "Tried to update an invalid mod!";
+ emitFailed(tr("Invalid metadata"));
+ }
}
auto LocalModUpdateTask::abort() -> bool
diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h
index 2db183e0..1d2f06a6 100644
--- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h
+++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h
@@ -37,6 +37,9 @@ class LocalModUpdateTask : public Task {
//! Entry point for tasks.
void executeTask() override;
+ signals:
+ void hasOldMod(QString name, QString filename);
+
private:
QDir m_index_dir;
ModPlatform::IndexedPack& m_mod;
diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
index 276414e4..015ead80 100644
--- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
+++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
@@ -36,11 +36,10 @@
#include "ModFolderLoadTask.h"
-#include "Application.h"
#include "minecraft/mod/MetadataHandler.h"
-ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir, bool is_indexed)
- : m_mods_dir(mods_dir), m_index_dir(index_dir), m_is_indexed(is_indexed), m_result(new Result())
+ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir, bool is_indexed, bool clean_orphan)
+ : m_mods_dir(mods_dir), m_index_dir(index_dir), m_is_indexed(is_indexed), m_clean_orphan(clean_orphan), m_result(new Result())
{}
void ModFolderLoadTask::run()
@@ -53,33 +52,46 @@ void ModFolderLoadTask::run()
// Read JAR files that don't have metadata
m_mods_dir.refresh();
for (auto entry : m_mods_dir.entryInfoList()) {
- Mod mod(entry);
+ Mod::Ptr mod(new Mod(entry));
- if (mod.enabled()) {
- if (m_result->mods.contains(mod.internal_id())) {
- m_result->mods[mod.internal_id()].setStatus(ModStatus::Installed);
+ if (mod->enabled()) {
+ if (m_result->mods.contains(mod->internal_id())) {
+ m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed);
}
else {
- m_result->mods[mod.internal_id()] = mod;
- m_result->mods[mod.internal_id()].setStatus(ModStatus::NoMetadata);
+ m_result->mods[mod->internal_id()] = mod;
+ m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata);
}
}
else {
- QString chopped_id = mod.internal_id().chopped(9);
+ QString chopped_id = mod->internal_id().chopped(9);
if (m_result->mods.contains(chopped_id)) {
- m_result->mods[mod.internal_id()] = mod;
+ m_result->mods[mod->internal_id()] = mod;
- auto metadata = m_result->mods[chopped_id].metadata();
+ auto metadata = m_result->mods[chopped_id]->metadata();
if (metadata) {
- mod.setMetadata(new Metadata::ModStruct(*metadata));
+ mod->setMetadata(*metadata);
- m_result->mods[mod.internal_id()].setStatus(ModStatus::Installed);
+ m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed);
m_result->mods.remove(chopped_id);
}
}
else {
- m_result->mods[mod.internal_id()] = mod;
- m_result->mods[mod.internal_id()].setStatus(ModStatus::NoMetadata);
+ m_result->mods[mod->internal_id()] = mod;
+ m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata);
+ }
+ }
+ }
+
+ // Remove orphan metadata to prevent issues
+ // See https://github.com/PolyMC/PolyMC/issues/996
+ if (m_clean_orphan) {
+ QMutableMapIterator<QString, Mod::Ptr> iter(m_result->mods);
+ while (iter.hasNext()) {
+ auto mod = iter.next().value();
+ if (mod->status() == ModStatus::NotInstalled) {
+ mod->destroy(m_index_dir, false);
+ iter.remove();
}
}
}
@@ -97,8 +109,8 @@ void ModFolderLoadTask::getFromMetadata()
return;
}
- Mod mod(m_mods_dir, metadata);
- mod.setStatus(ModStatus::NotInstalled);
- m_result->mods[mod.internal_id()] = mod;
+ auto* mod = new Mod(m_mods_dir, metadata);
+ mod->setStatus(ModStatus::NotInstalled);
+ m_result->mods[mod->internal_id()] = mod;
}
}
diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h
index 088f873e..1f2015d2 100644
--- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h
+++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h
@@ -48,7 +48,7 @@ class ModFolderLoadTask : public QObject, public QRunnable
Q_OBJECT
public:
struct Result {
- QMap<QString, Mod> mods;
+ QMap<QString, Mod::Ptr> mods;
};
using ResultPtr = std::shared_ptr<Result>;
ResultPtr result() const {
@@ -56,7 +56,7 @@ public:
}
public:
- ModFolderLoadTask(QDir& mods_dir, QDir& index_dir, bool is_indexed);
+ ModFolderLoadTask(QDir& mods_dir, QDir& index_dir, bool is_indexed, bool clean_orphan = false);
void run();
signals:
void succeeded();
@@ -67,5 +67,6 @@ private:
private:
QDir& m_mods_dir, m_index_dir;
bool m_is_indexed;
+ bool m_clean_orphan;
ResultPtr m_result;
};
diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp
index e49c166a..c73a11b6 100644
--- a/launcher/minecraft/services/CapeChange.cpp
+++ b/launcher/minecraft/services/CapeChange.cpp
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 "CapeChange.h"
#include <QNetworkRequest>
@@ -34,7 +69,11 @@ void CapeChange::clearCape() {
m_reply = shared_qobject_ptr<QNetworkReply>(rep);
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+#else
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+#endif
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
}
diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp
index cce8364e..921bd094 100644
--- a/launcher/minecraft/services/SkinDelete.cpp
+++ b/launcher/minecraft/services/SkinDelete.cpp
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 "SkinDelete.h"
#include <QNetworkRequest>
@@ -19,7 +54,11 @@ void SkinDelete::executeTask()
setStatus(tr("Deleting skin"));
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+#else
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+#endif
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
}
diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp
index 7c2e8337..c7987875 100644
--- a/launcher/minecraft/services/SkinUpload.cpp
+++ b/launcher/minecraft/services/SkinUpload.cpp
@@ -1,3 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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 "SkinUpload.h"
#include <QNetworkRequest>
@@ -44,7 +79,11 @@ void SkinUpload::executeTask()
setStatus(tr("Uploading skin"));
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ connect(rep, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+#else
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
+#endif
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
}