From 0a9ba53dc131c3f658177f01a34bcda4a5622b2f Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Tue, 20 Jan 2026 12:00:26 -0600 Subject: [PATCH 1/4] fix error when the host not accessible --- src/proxy/mumesocket.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proxy/mumesocket.cpp b/src/proxy/mumesocket.cpp index c04776227..b507d8605 100644 --- a/src/proxy/mumesocket.cpp +++ b/src/proxy/mumesocket.cpp @@ -205,6 +205,8 @@ void MumeFallbackSocket::onSocketError(const QString &errorString) m_outputs.onSocketStatus("Attempting insecure plain text..."); break; case SocketTypeEnum::SSL: + m_outputs.onSocketError("Unable to connect to MUME."); + return; default: assert(false); }; From bb4995e308b111ea164143ec723c6be13d6eb834 Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Tue, 20 Jan 2026 13:05:55 -0600 Subject: [PATCH 2/4] remove doorhelp --- src/parser/AbstractParser-Commands.cpp | 11 ----------- src/parser/abstractparser.cpp | 1 - 2 files changed, 12 deletions(-) diff --git a/src/parser/AbstractParser-Commands.cpp b/src/parser/AbstractParser-Commands.cpp index d1b724cb3..a9be51b91 100644 --- a/src/parser/AbstractParser-Commands.cpp +++ b/src/parser/AbstractParser-Commands.cpp @@ -45,7 +45,6 @@ const Abbrev cmdConfig{"config", 4}; const Abbrev cmdConnect{"connect", 4}; const Abbrev cmdDirections{"dirs", 3}; const Abbrev cmdDisconnect{"disconnect", 4}; -const Abbrev cmdDoorHelp{"doorhelp", 5}; const Abbrev cmdGenerateBaseMap{"generate-base-map"}; const Abbrev cmdGroup{"group", 2}; const Abbrev cmdHelp{"help", 2}; @@ -921,16 +920,6 @@ void AbstractParser::initSpecialCommandMap() }, // TODO: create a parse tree, and show all of the help topics. makeSimpleHelp("Provides help.")); - add( - cmdDoorHelp, - [this](const std::vector & /*s*/, StringView rest) { - if (!rest.isEmpty()) { - return false; - } - this->showDoorCommandHelp(); - return true; - }, - makeSimpleHelp("Help for door console commands.")); // door actions for (const DoorActionEnum x : ALL_DOOR_ACTION_TYPES) { diff --git a/src/parser/abstractparser.cpp b/src/parser/abstractparser.cpp index d047156d5..5e8e86982 100644 --- a/src/parser/abstractparser.cpp +++ b/src/parser/abstractparser.cpp @@ -653,7 +653,6 @@ void AbstractParser::showHelp() "Help commands:\n" " %1help - this help text\n" " %1help ?? - full syntax help for the help command\n" - " %1doorhelp - help for door console commands\n" "\n" "Other commands:\n" " %1dirs [-options] pattern - directions to matching rooms\n" From 6941cf62e896c33c033af497cb940fababe16822 Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Tue, 20 Jan 2026 12:03:10 -0600 Subject: [PATCH 3/4] add "_config file edit" Co-authored-by: KasparMetsa --- src/configuration/configuration.cpp | 16 ++++- src/configuration/configuration.h | 3 + src/parser/AbstractParser-Config.cpp | 87 ++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) diff --git a/src/configuration/configuration.cpp b/src/configuration/configuration.cpp index b6a12c394..da0fab3fe 100644 --- a/src/configuration/configuration.cpp +++ b/src/configuration/configuration.cpp @@ -487,11 +487,22 @@ NODISCARD static uint16_t sanitizeUint16(const int input, const uint16_t default } while (false) void Configuration::read() +{ + SETTINGS(conf); + readFrom(conf); +} + +void Configuration::write() const +{ + SETTINGS(conf); + writeTo(conf); +} + +void Configuration::readFrom(QSettings &conf) { // reset to defaults before reading colors that might override them colorSettings.resetToDefaults(); - SETTINGS(conf); FOREACH_CONFIG_GROUP(read); // This logic only runs once on a MMapper fresh install (or factory reset) @@ -514,9 +525,8 @@ void Configuration::read() && !colorSettings.BACKGROUND.getColor().isTransparent()); } -void Configuration::write() const +void Configuration::writeTo(QSettings &conf) const { - SETTINGS(conf); FOREACH_CONFIG_GROUP(write); } diff --git a/src/configuration/configuration.h b/src/configuration/configuration.h index fe5126529..12bf5f909 100644 --- a/src/configuration/configuration.h +++ b/src/configuration/configuration.h @@ -42,6 +42,9 @@ class NODISCARD Configuration final void write() const; void reset(); + void readFrom(QSettings &conf); + void writeTo(QSettings &conf) const; + public: struct NODISCARD GeneralSettings final { diff --git a/src/parser/AbstractParser-Config.cpp b/src/parser/AbstractParser-Config.cpp index ac7d308e5..f021db16c 100644 --- a/src/parser/AbstractParser-Config.cpp +++ b/src/parser/AbstractParser-Config.cpp @@ -10,6 +10,7 @@ #include "../global/Consts.h" #include "../global/NamedColors.h" #include "../global/PrintUtils.h" +#include "../mpi/remoteeditwidget.h" #include "../proxy/proxy.h" #include "../syntax/SyntaxArgs.h" #include "../syntax/TreeParser.h" @@ -19,6 +20,11 @@ #include #include +#include +#include +#include +#include +#include class NODISCARD ArgNamedColor final : public syntax::IArgument { @@ -344,6 +350,87 @@ void AbstractParser::doConfig(const StringView cmd) send_ok(os); }, "read config file")), + syn("edit", + Accept( + [this](User &user, auto) { + auto &os = user.getOstream(); + if (isConnected()) { + os << "You must disconnect before you can edit the saved configuration.\n"; + return; + } + os << "Opening configuration editor...\n"; + + QString content; + QString fileName; + { + QTemporaryFile temp(QDir::tempPath() + "/mmapper_XXXXXX.ini"); + temp.setAutoRemove(false); + if (!temp.open()) { + os << "Failed to create temporary file.\n"; + return; + } + fileName = temp.fileName(); + temp.close(); + + { + QSettings settings(fileName, QSettings::IniFormat); + getConfig().writeTo(settings); + settings.sync(); + } + + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + content = QString::fromUtf8(file.readAll()); + file.close(); + } + QFile::remove(fileName); + } + + if (content.isEmpty()) { + os << "Configuration is empty or failed to export.\n"; + return; + } + + // REVISIT: Ideally we support external editor as well + auto *editor = new RemoteEditWidget(true, + "MMapper Client Configuration", + content, + nullptr); + QObject::connect(editor, + &RemoteEditWidget::sig_save, + [weakParser = QPointer(this)]( + const QString &edited) { + if (weakParser.isNull()) { + return; + } + + QTemporaryFile tempRead(QDir::tempPath() + + "/mmapper_XXXXXX.ini"); + tempRead.setAutoRemove(true); + if (tempRead.open()) { + QString name = tempRead.fileName(); + tempRead.write(edited.toUtf8()); + tempRead.close(); + + { + auto &cfg = setConfig(); + QSettings settings(name, QSettings::IniFormat); + cfg.readFrom(settings); + cfg.write(); + } + + weakParser->sendToUser( + SendToUserSourceEnum::FromMMapper, + "\nConfiguration imported and persisted.\n"); + weakParser->sendOkToUser(); + } + }); + + editor->setAttribute(Qt::WA_DeleteOnClose); + editor->show(); + editor->activateWindow(); + }, + "edit client configuration")), syn("factory", "reset", TokenMatcher::alloc("Yes, I'm sure!"), From 1337323da55901c6353d06441cc6d4c8b9d94660 Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Tue, 20 Jan 2026 22:37:29 +0000 Subject: [PATCH 4/4] add configuration export and import buttons to general settings --- src/preferences/configdialog.cpp | 5 +-- src/preferences/generalpage.cpp | 60 +++++++++++++++++++++++++++++++- src/preferences/generalpage.h | 2 +- src/preferences/generalpage.ui | 47 ++++++++++++++++--------- 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/preferences/configdialog.cpp b/src/preferences/configdialog.cpp index 1c94d6fc1..6dda8e946 100644 --- a/src/preferences/configdialog.cpp +++ b/src/preferences/configdialog.cpp @@ -62,10 +62,7 @@ ConfigDialog::ConfigDialog(QWidget *const parent) &ConfigDialog::slot_changePage); connect(ui->closeButton, &QAbstractButton::clicked, this, &QWidget::close); - connect(generalPage, &GeneralPage::sig_factoryReset, this, [this]() { - qDebug() << "Reloading config due to factory reset"; - emit sig_loadConfig(); - }); + connect(generalPage, &GeneralPage::sig_reloadConfig, this, [this]() { emit sig_loadConfig(); }); connect(this, &ConfigDialog::sig_loadConfig, generalPage, &GeneralPage::slot_loadConfig); connect(this, &ConfigDialog::sig_loadConfig, graphicsPage, &GraphicsPage::slot_loadConfig); connect(this, &ConfigDialog::sig_loadConfig, parserPage, &ParserPage::slot_loadConfig); diff --git a/src/preferences/generalpage.cpp b/src/preferences/generalpage.cpp index ee603db19..ed075620b 100644 --- a/src/preferences/generalpage.cpp +++ b/src/preferences/generalpage.cpp @@ -108,10 +108,68 @@ GeneralPage::GeneralPage(QWidget *parent) QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { setConfig().reset(); - emit sig_factoryReset(); + emit sig_reloadConfig(); } }); + connect(ui->configurationExportButton, &QAbstractButton::clicked, this, []() { + QTemporaryFile temp(QDir::tempPath() + "/mmapper_XXXXXX.ini"); + temp.setAutoRemove(false); + if (!temp.open()) { + qWarning() << "Failed to create temporary file for export"; + return; + } + const QString fileName = temp.fileName(); + temp.close(); + + { + QSettings settings(fileName, QSettings::IniFormat); + getConfig().writeTo(settings); + settings.sync(); + } + + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + const QByteArray content = file.readAll(); + file.close(); + QFileDialog::saveFileContent(content, "mmapper.ini"); + } + + QFile::remove(fileName); + }); + + connect(ui->configurationImportButton, &QAbstractButton::clicked, this, [this]() { + auto importFile = [this](const QString &fileName, + const std::optional &fileContent) { + if (fileName.isEmpty() || !fileContent.has_value()) { + return; + } + + const QByteArray &content = fileContent.value(); + if (content.isEmpty()) { + return; + } + + QTemporaryFile temp(QDir::tempPath() + "/mmapper_import_XXXXXX.ini"); + temp.setAutoRemove(true); + if (temp.open()) { + temp.write(content); + temp.close(); + + { + auto &cfg = setConfig(); + QSettings settings(temp.fileName(), QSettings::IniFormat); + cfg.readFrom(settings); + cfg.write(); + } + emit sig_reloadConfig(); + } + }; + + const auto nameFilter = QStringLiteral("Configuration (*.ini);;All files (*)"); + QFileDialog::getOpenFileContent(nameFilter, importFile); + }); + connect(ui->autoLogin, &QCheckBox::stateChanged, this, [this]() { setConfig().account.rememberLogin = ui->autoLogin->isChecked(); }); diff --git a/src/preferences/generalpage.h b/src/preferences/generalpage.h index 3b60bb1f1..2a1e9fe50 100644 --- a/src/preferences/generalpage.h +++ b/src/preferences/generalpage.h @@ -29,7 +29,7 @@ class NODISCARD_QOBJECT GeneralPage final : public QWidget ~GeneralPage() final; signals: - void sig_factoryReset(); + void sig_reloadConfig(); public slots: void slot_loadConfig(); diff --git a/src/preferences/generalpage.ui b/src/preferences/generalpage.ui index b0bb4176f..99fbcd166 100644 --- a/src/preferences/generalpage.ui +++ b/src/preferences/generalpage.ui @@ -397,23 +397,10 @@ Advanced - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + - Reset to Default Settings + Import Settings... @@ -427,6 +414,13 @@ + + + + Export Settings... + + + @@ -437,6 +431,26 @@ + + + + Reset to Default Settings + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -477,7 +491,6 @@ showNotesCheckBox proxyListensOnAnyInterfaceCheckBox proxyConnectionStatusCheckBox - configurationResetButton