Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
df943c9
chore: bump patch version to 11.0.4
Trial97 Jul 14, 2026
9fb778e
chore: bump patch version to 11.0.4 (#5787)
Trial97 Jul 20, 2026
cbab259
fix: crash when trying to install a disabled modloader
Trial97 Jul 12, 2026
bbadd30
fix: enable the installed modloader
Trial97 Jul 12, 2026
8b57a5f
[Backport release-11.x] fix: crash when trying to install a disabled …
Trial97 Jul 22, 2026
d406e00
fix existing instance shortcuts not being detected
Octol1ttle Jul 22, 2026
828cbc6
Fix instance shortcuts breaking when the instance is renamed
Vishrut2403 Jul 22, 2026
8962e60
Regenerate UUID on instance duplication
Vishrut2403 Jul 22, 2026
7ea3dbb
Use regenerateUuid() in the constructor to avioid duplicating code
Vishrut2403 Jul 22, 2026
d86b708
[Backport release-11.x] Fix instance shortcuts breaking when the inst…
Octol1ttle Jul 22, 2026
3a357b7
[Backport release-11.x] Fix existing instance shortcuts not being det…
Octol1ttle Jul 22, 2026
ce60e88
Prevent renaming instance folder while instance is running
Vishrut2403 Jul 23, 2026
b718261
[Backport release-11.x] Prevent renaming instance folder while instan…
Octol1ttle Jul 25, 2026
598fe62
removed menu bar appearance change
frigtear Feb 10, 2026
449e7a1
[Backport release-11.x] Fix hard to read menu bar colors on MacOS (#5…
Octol1ttle Jul 28, 2026
54082b0
Fix building HardwareInfo.cpp on OpenBSD
TheKodeToad Jul 30, 2026
d861a8c
Cache resource hard link count
TheKodeToad Jul 30, 2026
18eab90
[Backport release-11.x] Fix building HardwareInfo.cpp on OpenBSD (#5861)
TheKodeToad Jul 31, 2026
2626e9c
[Backport release-11.x] Cache resource hard link count (#5862)
TheKodeToad Jul 31, 2026
84d5715
Assert against double suspendSave/resumeSave call
TheKodeToad Jul 30, 2026
1515584
Avoid suspendSave reentrance in MinecraftSettingsWidget
TheKodeToad Jul 30, 2026
dfa2db0
Avoid triggering saves in INISettingsObject::reload
TheKodeToad Aug 7, 2026
2debb8c
Avoid always saving when loading instance settings
TheKodeToad Aug 7, 2026
eaf2fa0
Add Saving INI settings log message
TheKodeToad Aug 7, 2026
f0bdb09
[Backport release-11.x] Avoid excessive config writes (less freezing …
TheKodeToad Aug 16, 2026
bb6646b
[Backport release-11.x] Tackle more excessive config saves (#5952)
TheKodeToad Aug 16, 2026
3cd518c
fix: remove unnecessary resets during startup
Octol1ttle Aug 13, 2026
a35de94
fix: don't emit SettingChanged if the value didn't actually change
Octol1ttle Aug 13, 2026
4251203
[Backport release-11.x] Less settings saving (#5953)
TheKodeToad Aug 16, 2026
a07a263
Pass flag to InstanceImportTask to indicate trusted source
TheKodeToad Aug 4, 2026
961ba34
Add warning for untrusted mods in Modrinth/CurseForge pack
TheKodeToad Aug 5, 2026
4a24709
Bolden bottom warning text
TheKodeToad Aug 5, 2026
5ffe6a0
Update launcher/ui/dialogs/UntrustedModsDialog.ui
TheKodeToad Aug 5, 2026
7a847bb
backport: fix build
Octol1ttle Aug 16, 2026
29d4031
[Backport release-11.x] Add warning for untrusted mods in imported mo…
Octol1ttle Aug 16, 2026
fb6cc77
fix: set entity state before emitting success
Trial97 Aug 15, 2026
b7174c0
[Backport release-11.x] fix: set entity state before emitting success…
Trial97 Aug 17, 2026
a8ed5aa
Bump to 11.1.0
TheKodeToad Aug 17, 2026
77df2c2
Fix formatting
TheKodeToad Aug 17, 2026
811568e
backport: fix build (for real)
Octol1ttle Aug 17, 2026
4d2b711
Bump to 11.1.0 (#5962)
Octol1ttle Aug 17, 2026
8888acc
Improve performance of BaseInstance::uuid
TheKodeToad Aug 4, 2026
d92e04b
fix: regenerate instance UUID on import
Vishrut2403 Aug 11, 2026
9a3c39b
[Backport release-11.x] Improve performance of BaseInstance::uuid (#5…
Octol1ttle Aug 18, 2026
857dff9
Merge branch 'release-11.x' into backport-5930-to-release-11.x
TheKodeToad Aug 18, 2026
d29fd65
[Backport release-11.x] fix: regenerate instance UUID on import (#5971)
TheKodeToad Aug 18, 2026
df67e58
fix(actions/winget): bump winget-releaser to fix failures
Octol1ttle Jul 21, 2026
d9abdb7
actions/winget: add commit pin explanation comment
Octol1ttle Jul 22, 2026
0113982
JavaSettingsWidget: available memory -> free memory
Octol1ttle Apr 28, 2026
422aa85
[Backport release-11.x] Bump winget-releaser to fix autopublishing (#…
Octol1ttle Aug 29, 2026
1c37a2a
Merge branch 'release-11.x' into backport-5470-to-release-11.x
Octol1ttle Aug 29, 2026
ea87ffc
[Backport release-11.x] JavaSettingsWidget: available memory -> free …
Octol1ttle Aug 29, 2026
567293b
Merge tag '11.1.0' into feat/upstream-11.1.0
so5iso4ka Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ jobs:

steps:
- name: Publish on Winget
uses: vedantmgoyal2009/winget-releaser@v2
# @Octol1ttle: Pinned to a commit because no release has been published that works on ubuntu-slim
uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # docs: add description to inputs (#335)
with:
identifier: FreesTeam.FreesmLauncher
version: ${{ github.event.release.tag_name }}
Expand Down
18 changes: 6 additions & 12 deletions launcher/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,8 +924,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
{
auto resetIfInvalid = [this](const Setting* setting) {
if (const QUrl url(setting->get().toString()); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https")) {
m_settings->reset(setting->id());
if (const auto value = setting->get().toString(); !value.isEmpty()) {
if (const QUrl url(value); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https")) {
m_settings->reset(setting->id());
}
}
};

Expand All @@ -948,17 +950,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("MSAClientIDOverride", "");

// Custom Flame API Key
{
m_settings->registerSetting("CFKeyOverride", "");
m_settings->registerSetting("FlameKeyOverride", "");

QString flameKey = m_settings->get("CFKeyOverride").toString();

if (!flameKey.isEmpty())
m_settings->set("FlameKeyOverride", flameKey);
m_settings->reset("CFKeyOverride");
}
m_settings->registerSetting({ "FlameKeyOverride", "CFKeyOverride" }, "");
m_settings->registerSetting("FlameKeyShouldBeFetchedOnStartup", true);

m_settings->registerSetting("FallbackMRBlockedMods", true);
m_settings->registerSetting("ModrinthToken", "");
m_settings->registerSetting("UserAgentOverride", "");
Expand Down
18 changes: 17 additions & 1 deletion launcher/BaseInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUuid>

#include "Application.h"
#include "Json.h"
Expand Down Expand Up @@ -89,6 +90,14 @@ BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr<Setti

m_settings->registerSetting("linkedInstances", "[]");
m_settings->registerSetting("shortcuts", QString());
m_settings->registerSetting("uuid", QString());

const auto savedUUID = m_settings->get("uuid").toString();
if (savedUUID.isEmpty()) {
regenerateUuid();
} else {
m_uuid = savedUUID;
}

// Game time override
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
Expand Down Expand Up @@ -272,6 +281,13 @@ QString BaseInstance::id() const
return QFileInfo(instanceRoot()).fileName();
}

void BaseInstance::regenerateUuid()
{
const auto newUUID = QUuid::createUuid().toString(QUuid::Id128);
m_settings->set("uuid", newUUID);
m_uuid = newUUID;
}

bool BaseInstance::isRunning() const
{
return m_isRunning;
Expand Down Expand Up @@ -449,7 +465,7 @@ QList<ShortcutData> BaseInstance::shortcuts() const

QString shortcutName = dict["name"].toString();
QString filePath = dict["filePath"].toString();
if (!QDir(filePath).exists()) {
if (!QFileInfo::exists(filePath)) {
qWarning() << "Shortcut" << shortcutName << "for instance" << name() << "have non-existent path" << filePath;
continue;
}
Expand Down
5 changes: 4 additions & 1 deletion launcher/BaseInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ class BaseInstance : public QObject {

/// The instance's ID. The ID SHALL be determined by LAUNCHER internally. The ID IS guaranteed to
/// be unique.
virtual QString id() const;
QString id() const;
QString uuid() const { return m_uuid; }
void regenerateUuid();

void setMinecraftRunning(bool running);
void setRunning(bool running);
Expand Down Expand Up @@ -317,6 +319,7 @@ class BaseInstance : public QObject {
RuntimeContext m_runtimeContext;

private: /* data */
QString m_uuid;
Status m_status = Status::Present;
bool m_crashed = false;
bool m_hasUpdate = false;
Expand Down
3 changes: 3 additions & 0 deletions launcher/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ChooseOfflineNameDialog.h
ui/dialogs/ModToggleConfirmDialog.cpp
ui/dialogs/ModToggleConfirmDialog.h
ui/dialogs/UntrustedModsDialog.cpp
ui/dialogs/UntrustedModsDialog.h

ui/dialogs/skins/SkinManageDialog.cpp
ui/dialogs/skins/SkinManageDialog.h
Expand Down Expand Up @@ -1337,6 +1339,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/skins/SkinManageDialog.ui
ui/dialogs/ChooseOfflineNameDialog.ui
ui/dialogs/ModToggleConfirmDialog.ui
ui/dialogs/UntrustedModsDialog.ui
)

qt_wrap_ui(PRISM_UPDATE_UI
Expand Down
4 changes: 3 additions & 1 deletion launcher/HardwareInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
#include <QDebug>
#include <QStringList>

#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
namespace {
#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
QString afterColon(QString str)
{
return str.remove(0, str.indexOf(':') + 2).trimmed();
}
#endif

template <typename F>
bool readFromOutput(const char* command, F function)
Expand Down
1 change: 1 addition & 0 deletions launcher/InstanceCopyTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ void InstanceCopyTask::copyFinished()
BaseInstance* inst(new NullInstance(m_globalSettings, std::move(instanceSettings), m_stagingPath));
inst->setName(name());
inst->setIconKey(m_instIcon);
inst->regenerateUuid();
if (!m_keepPlaytime) {
inst->resetTimePlayed();
}
Expand Down
7 changes: 7 additions & 0 deletions launcher/InstanceDirUpdate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ QString askToUpdateInstanceDirName(BaseInstance* instance, const QString& oldNam
return QString();
}

if (instance->isRunning()) {
QMessageBox::warning(parent, QObject::tr("Cannot rename instance folder"),
QObject::tr("The instance folder cannot be renamed while the instance is running.\n\n"
"Only the instance name will be changed. The folder will keep its current name."));
return QString();
}

// Ask if we should rename
if (renamingMode == "AskEverytime") {
auto checkBox = new QCheckBox(QObject::tr("&Remember my choice"), parent);
Expand Down
20 changes: 12 additions & 8 deletions launcher/InstanceImportTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
#include <QtConcurrentRun>
#include <memory>

InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, bool trustedSource, QWidget* parent, QMap<QString, QString>&& extra_info)
: m_sourceUrl(sourceUrl), m_trustedSource(trustedSource), m_extra_info(extra_info), m_parent(parent)
{}

bool InstanceImportTask::abort()
Expand Down Expand Up @@ -287,11 +287,12 @@ void InstanceImportTask::processFlame()
if (original_instance_id_it != m_extra_info.constEnd())
original_instance_id = original_instance_id_it.value();

inst_creation_task =
makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, pack_id,
pack_version_id, original_instance_id);
} else {
// FIXME: Find a way to get IDs in directly imported ZIPs
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
inst_creation_task =
makeShared<FlameCreationTask>(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, QString(), QString());
}

inst_creation_task->setName(*this);
Expand Down Expand Up @@ -349,6 +350,9 @@ void InstanceImportTask::processMultiMC()
// reset time played on import... because packs.
instance.resetTimePlayed();

// UUID is carried over on export, but this is a distinct instance, so give it its own
instance.regenerateUuid();

// set a new nice name
instance.setName(name());

Expand Down Expand Up @@ -381,8 +385,8 @@ void InstanceImportTask::processModrinth()
if (original_instance_id_it != m_extra_info.constEnd())
original_instance_id = original_instance_id_it.value();

inst_creation_task =
makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
inst_creation_task = makeShared<ModrinthCreationTask>(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, pack_id,
pack_version_id, original_instance_id);
} else {
QString pack_id;
if (!m_sourceUrl.isEmpty()) {
Expand All @@ -391,7 +395,7 @@ void InstanceImportTask::processModrinth()
}

// FIXME: Find a way to get the ID in directly imported ZIPs
inst_creation_task = makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id);
inst_creation_task = makeShared<ModrinthCreationTask>(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, pack_id);
}

inst_creation_task->setName(*this);
Expand Down
6 changes: 5 additions & 1 deletion launcher/InstanceImportTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
class InstanceImportTask : public InstanceTask {
Q_OBJECT
public:
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
explicit InstanceImportTask(const QUrl& sourceUrl,
bool trustedSource,
QWidget* parent = nullptr,
QMap<QString, QString>&& extra_info = {});
virtual ~InstanceImportTask() = default;
bool abort() override;

Expand All @@ -63,6 +66,7 @@ class InstanceImportTask : public InstanceTask {

private: /* data */
QUrl m_sourceUrl;
bool m_trustedSource;
QString m_archivePath;
Task::Ptr m_task;
enum class ModpackType {
Expand Down
2 changes: 1 addition & 1 deletion launcher/InstanceList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ BaseInstance* InstanceList::getInstanceById(QString instId) const
if (instId.isEmpty())
return nullptr;
for (auto& inst : m_instances) {
if (inst->id() == instId) {
if (inst->id() == instId || inst->uuid() == instId) {
return inst.get();
}
}
Expand Down
4 changes: 2 additions & 2 deletions launcher/meta/BaseEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ void BaseEntityLoadTask::executeTask()
}

} catch (const Exception& e) {
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
qCritical() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
// just make sure it's gone and we never consider it again.
FS::deletePath(fname);
m_entity->m_load_status = BaseEntity::LoadStatus::NotLoaded;
Expand Down Expand Up @@ -177,10 +177,10 @@ void BaseEntityLoadTask::executeTask()
m_task->addNetAction(dl);
m_task->setAskRetry(false);
connect(m_task.get(), &Task::failed, this, &BaseEntityLoadTask::emitFailed);
connect(m_task.get(), &Task::succeeded, this, &BaseEntityLoadTask::emitSucceeded);
connect(m_task.get(), &Task::succeeded, this, [this]() {
m_entity->m_load_status = BaseEntity::LoadStatus::Remote;
m_entity->m_file_sha256 = m_entity->m_sha256;
emitSucceeded();
});

connect(m_task.get(), &Task::progress, this, &Task::setProgress);
Expand Down
4 changes: 3 additions & 1 deletion launcher/minecraft/MinecraftInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,9 @@ void MinecraftInstance::loadSpecificSettings()
auto envSetting = m_settings->registerSetting("OverrideEnv", false);
m_settings->registerOverride(global_settings->getSetting("Env"), envSetting);

m_settings->set("InstanceType", "OneSix");
if (m_settings->get("InstanceType").toString() != "OneSix") {
m_settings->set("InstanceType", "OneSix");
}
}

// Join server on launch, this does not have a global override
Expand Down
2 changes: 1 addition & 1 deletion launcher/minecraft/ShortcutUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ bool createInstanceShortcut(const Shortcut& shortcut, const QString& filePath)
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Not supported on your platform!"));
return false;
#endif
args.append({ "--launch", shortcut.instance->id() });
args.append({ "--launch", shortcut.instance->uuid() });
args.append(shortcut.extraArgs);

QString shortcutPath = FS::createShortcut(filePath, appPath, args, shortcut.name, iconPath);
Expand Down
3 changes: 2 additions & 1 deletion launcher/minecraft/mod/Resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ void Resource::parseFile()
m_internal_id = file_name;

std::tie(m_size_str, m_size_info) = calculateFileSize(m_file_info);
m_hardLinkCount = FS::hardLinkCount(m_file_info.absoluteFilePath());
if (m_file_info.isDir()) {
m_type = ResourceType::FOLDER;
m_name = file_name;
Expand Down Expand Up @@ -287,7 +288,7 @@ bool Resource::isSymLinkUnder(const QString& instPath) const

bool Resource::isMoreThanOneHardLink() const
{
return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1;
return m_hardLinkCount > 1;
}

auto Resource::getOriginalFileName() const -> QString
Expand Down
3 changes: 3 additions & 0 deletions launcher/minecraft/mod/Resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@

#pragma once

#include <cstdint>

#include <QDateTime>
#include <QFileInfo>
#include <QObject>
Expand Down Expand Up @@ -210,4 +212,5 @@ class Resource : public QObject {
int m_resolution_ticket = 0;
QString m_size_str;
qint64 m_size_info;
std::uintmax_t m_hardLinkCount = 0;
};
2 changes: 1 addition & 1 deletion launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static Version mcVersion(BaseInstance* inst)

static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
{
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value();
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value_or(ModPlatform::ModLoaderTypes(0));
}

static bool checkDependencies(std::shared_ptr<GetModDependenciesTask::PackDependency> sel,
Expand Down
32 changes: 32 additions & 0 deletions launcher/modplatform/flame/FlameInstanceCreationTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#include "minecraft/World.h"
#include "minecraft/mod/tasks/LocalResourceParse.h"
#include "net/ApiDownload.h"
#include "ui/dialogs/UntrustedModsDialog.h"
#include "ui/pages/modplatform/OptionalModDialog.h"

static const FlameAPI api;
Expand Down Expand Up @@ -307,6 +308,31 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType,
return loaderVersion;
}

bool FlameCreationTask::promptForUntrustedMods()
{
if (m_trustedSource) {
return true;
}

QStringList untrustedMods;

const QDir mcDir{ FS::PathCombine(m_stagingPath, "minecraft") };
const QString modsPath{ FS::PathCombine(m_stagingPath, "minecraft/mods") };
if (QDir(modsPath).exists()) {
QDirIterator iter{ modsPath, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks };
while (iter.hasNext()) {
untrustedMods.append(mcDir.relativeFilePath(iter.next()));
}
}

if (untrustedMods.empty()) {
return true;
}

UntrustedModsDialog dialog{ untrustedMods, m_parent };
return dialog.exec() == QDialog::Accepted;
}

std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
{
QEventLoop loop;
Expand Down Expand Up @@ -345,6 +371,12 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
}
}

if (!promptForUntrustedMods()) {
m_abort = true;
emitAborted();
return nullptr;
}

QString loaderType;
QString loaderUid;
QString loaderVersion;
Expand Down
Loading
Loading