diff options
Diffstat (limited to 'meshmc/launcher/settings')
| -rw-r--r-- | meshmc/launcher/settings/INIFile.cpp | 172 | ||||
| -rw-r--r-- | meshmc/launcher/settings/INIFile.h | 59 | ||||
| -rw-r--r-- | meshmc/launcher/settings/INIFile_test.cpp | 80 | ||||
| -rw-r--r-- | meshmc/launcher/settings/INISettingsObject.cpp | 120 | ||||
| -rw-r--r-- | meshmc/launcher/settings/INISettingsObject.h | 87 | ||||
| -rw-r--r-- | meshmc/launcher/settings/OverrideSetting.cpp | 77 | ||||
| -rw-r--r-- | meshmc/launcher/settings/OverrideSetting.h | 70 | ||||
| -rw-r--r-- | meshmc/launcher/settings/PassthroughSetting.cpp | 88 | ||||
| -rw-r--r-- | meshmc/launcher/settings/PassthroughSetting.h | 69 | ||||
| -rw-r--r-- | meshmc/launcher/settings/Setting.cpp | 73 | ||||
| -rw-r--r-- | meshmc/launcher/settings/Setting.h | 141 | ||||
| -rw-r--r-- | meshmc/launcher/settings/SettingsObject.cpp | 166 | ||||
| -rw-r--r-- | meshmc/launcher/settings/SettingsObject.h | 246 |
13 files changed, 1448 insertions, 0 deletions
diff --git a/meshmc/launcher/settings/INIFile.cpp b/meshmc/launcher/settings/INIFile.cpp new file mode 100644 index 0000000000..08a9538fea --- /dev/null +++ b/meshmc/launcher/settings/INIFile.cpp @@ -0,0 +1,172 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 "settings/INIFile.h" +#include <FileSystem.h> + +#include <QFile> +#include <QTextStream> +#include <QStringList> +#include <QSaveFile> +#include <QDebug> + +INIFile::INIFile() {} + +QString INIFile::unescape(QString orig) +{ + QString out; + QChar prev = QChar(); + for (auto c : orig) { + if (prev == QLatin1Char('\\')) { + if (c == 'n') + out += '\n'; + else if (c == 't') + out += '\t'; + else if (c == '#') + out += '#'; + else + out += c; + prev = QChar(); + } else { + if (c == '\\') { + prev = c; + continue; + } + out += c; + prev = QChar(); + } + } + return out; +} + +QString INIFile::escape(QString orig) +{ + QString out; + for (auto c : orig) { + if (c == '\n') + out += "\\n"; + else if (c == '\t') + out += "\\t"; + else if (c == '\\') + out += "\\\\"; + else if (c == '#') + out += "\\#"; + else + out += c; + } + return out; +} + +bool INIFile::saveFile(QString fileName) +{ + QByteArray outArray; + for (Iterator iter = begin(); iter != end(); iter++) { + QString value = iter.value().toString(); + value = escape(value); + outArray.append(iter.key().toUtf8()); + outArray.append('='); + outArray.append(value.toUtf8()); + outArray.append('\n'); + } + + try { + FS::write(fileName, outArray); + } catch (const Exception& e) { + qCritical() << e.what(); + return false; + } + + return true; +} + +bool INIFile::loadFile(QString fileName) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + return false; + bool success = loadFile(file.readAll()); + file.close(); + return success; +} + +bool INIFile::loadFile(QByteArray file) +{ + QTextStream in(file); + // Qt6 uses UTF-8 by default + + QStringList lines = in.readAll().split('\n'); + for (int i = 0; i < lines.count(); i++) { + QString& lineRaw = lines[i]; + // Ignore comments. + int commentIndex = 0; + QString line = lineRaw; + // Search for comments until no more escaped # are available + while ((commentIndex = line.indexOf('#', commentIndex + 1)) != -1) { + if (commentIndex > 0 && line.at(commentIndex - 1) == '\\') { + continue; + } + line = line.left(lineRaw.indexOf('#')).trimmed(); + } + + int eqPos = line.indexOf('='); + if (eqPos == -1) + continue; + QString key = line.left(eqPos).trimmed(); + QString valueStr = line.right(line.length() - eqPos - 1).trimmed(); + + valueStr = unescape(valueStr); + + QVariant value(valueStr); + this->operator[](key) = value; + } + + return true; +} + +QVariant INIFile::get(QString key, QVariant def) const +{ + if (!this->contains(key)) + return def; + else + return this->operator[](key); +} + +void INIFile::set(QString key, QVariant val) +{ + this->operator[](key) = val; +} diff --git a/meshmc/launcher/settings/INIFile.h b/meshmc/launcher/settings/INIFile.h new file mode 100644 index 0000000000..e5ae65cea6 --- /dev/null +++ b/meshmc/launcher/settings/INIFile.h @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 <QVariant> +#include <QIODevice> + +// Sectionless INI parser (for instance config files) +class INIFile : public QMap<QString, QVariant> +{ + public: + explicit INIFile(); + + bool loadFile(QByteArray file); + bool loadFile(QString fileName); + bool saveFile(QString fileName); + + QVariant get(QString key, QVariant def) const; + void set(QString key, QVariant val); + static QString unescape(QString orig); + static QString escape(QString orig); +}; diff --git a/meshmc/launcher/settings/INIFile_test.cpp b/meshmc/launcher/settings/INIFile_test.cpp new file mode 100644 index 0000000000..6190ae99f2 --- /dev/null +++ b/meshmc/launcher/settings/INIFile_test.cpp @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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/>. + */ + +#include <QTest> +#include "TestUtil.h" + +#include "settings/INIFile.h" + +class IniFileTest : public QObject +{ + Q_OBJECT + private slots: + void initTestCase() {} + void cleanupTestCase() {} + + void test_Escape_data() + { + QTest::addColumn<QString>("through"); + + QTest::newRow("unix path") << "/abc/def/ghi/jkl"; + QTest::newRow("windows path") + << "C:\\Program files\\terrible\\name\\of something\\"; + QTest::newRow("Plain text") << "Lorem ipsum dolor sit amet."; + QTest::newRow("Escape sequences") + << "Lorem\n\t\n\\n\\tAAZ\nipsum dolor\n\nsit amet."; + QTest::newRow("Escape sequences 2") << "\"\n\n\""; + QTest::newRow("Hashtags") << "some data#something"; + } + void test_Escape() + { + QFETCH(QString, through); + + QString there = INIFile::escape(through); + QString back = INIFile::unescape(there); + + QCOMPARE(back, through); + } + + void test_SaveLoad() + { + QString a = "a"; + QString b = "a\nb\t\n\\\\\\C:\\Program files\\terrible\\name\\of " + "something\\#thisIsNotAComment"; + QString filename = "test_SaveLoad.ini"; + + // save + INIFile f; + f.set("a", a); + f.set("b", b); + f.saveFile(filename); + + // load + INIFile f2; + f2.loadFile(filename); + QCOMPARE(a, f2.get("a", "NOT SET").toString()); + QCOMPARE(b, f2.get("b", "NOT SET").toString()); + } +}; + +QTEST_GUILESS_MAIN(IniFileTest) + +#include "INIFile_test.moc" diff --git a/meshmc/launcher/settings/INISettingsObject.cpp b/meshmc/launcher/settings/INISettingsObject.cpp new file mode 100644 index 0000000000..ea10e3e50f --- /dev/null +++ b/meshmc/launcher/settings/INISettingsObject.cpp @@ -0,0 +1,120 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 "INISettingsObject.h" +#include "Setting.h" + +INISettingsObject::INISettingsObject(const QString& path, QObject* parent) + : SettingsObject(parent) +{ + m_filePath = path; + m_ini.loadFile(path); +} + +void INISettingsObject::setFilePath(const QString& filePath) +{ + m_filePath = filePath; +} + +bool INISettingsObject::reload() +{ + return m_ini.loadFile(m_filePath) && SettingsObject::reload(); +} + +void INISettingsObject::suspendSave() +{ + m_suspendSave = true; +} + +void INISettingsObject::resumeSave() +{ + m_suspendSave = false; + if (m_doSave) { + m_ini.saveFile(m_filePath); + } +} + +void INISettingsObject::changeSetting(const Setting& setting, QVariant value) +{ + if (contains(setting.id())) { + // valid value -> set the main config, remove all the sysnonyms + if (value.isValid()) { + auto list = setting.configKeys(); + m_ini.set(list.takeFirst(), value); + for (auto iter : list) + m_ini.remove(iter); + } + // invalid -> remove all (just like resetSetting) + else { + for (auto iter : setting.configKeys()) + m_ini.remove(iter); + } + doSave(); + } +} + +void INISettingsObject::doSave() +{ + if (m_suspendSave) { + m_doSave = true; + } else { + m_ini.saveFile(m_filePath); + } +} + +void INISettingsObject::resetSetting(const Setting& setting) +{ + // if we have the setting, remove all the synonyms. ALL OF THEM + if (contains(setting.id())) { + for (auto iter : setting.configKeys()) + m_ini.remove(iter); + doSave(); + } +} + +QVariant INISettingsObject::retrieveValue(const Setting& setting) +{ + // if we have the setting, return value of the first matching synonym + if (contains(setting.id())) { + for (auto iter : setting.configKeys()) { + if (m_ini.contains(iter)) + return m_ini[iter]; + } + } + return QVariant(); +} diff --git a/meshmc/launcher/settings/INISettingsObject.h b/meshmc/launcher/settings/INISettingsObject.h new file mode 100644 index 0000000000..d0fca5f783 --- /dev/null +++ b/meshmc/launcher/settings/INISettingsObject.h @@ -0,0 +1,87 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 <QObject> + +#include "settings/INIFile.h" + +#include "settings/SettingsObject.h" + +/*! + * \brief A settings object that stores its settings in an INIFile. + */ +class INISettingsObject : public SettingsObject +{ + Q_OBJECT + public: + explicit INISettingsObject(const QString& path, QObject* parent = 0); + + /*! + * \brief Gets the path to the INI file. + * \return The path to the INI file. + */ + virtual QString filePath() const + { + return m_filePath; + } + + /*! + * \brief Sets the path to the INI file and reloads it. + * \param filePath The INI file's new path. + */ + virtual void setFilePath(const QString& filePath); + + bool reload() override; + + void suspendSave() override; + void resumeSave() override; + + protected slots: + virtual void changeSetting(const Setting& setting, QVariant value) override; + virtual void resetSetting(const Setting& setting) override; + + protected: + virtual QVariant retrieveValue(const Setting& setting) override; + void doSave(); + + protected: + INIFile m_ini; + QString m_filePath; +}; diff --git a/meshmc/launcher/settings/OverrideSetting.cpp b/meshmc/launcher/settings/OverrideSetting.cpp new file mode 100644 index 0000000000..1de3e6e9e2 --- /dev/null +++ b/meshmc/launcher/settings/OverrideSetting.cpp @@ -0,0 +1,77 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 "OverrideSetting.h" + +OverrideSetting::OverrideSetting(std::shared_ptr<Setting> other, + std::shared_ptr<Setting> gate) + : Setting(other->configKeys(), QVariant()) +{ + Q_ASSERT(other); + Q_ASSERT(gate); + m_other = other; + m_gate = gate; +} + +bool OverrideSetting::isOverriding() const +{ + return m_gate->get().toBool(); +} + +QVariant OverrideSetting::defValue() const +{ + return m_other->get(); +} + +QVariant OverrideSetting::get() const +{ + if (isOverriding()) { + return Setting::get(); + } + return m_other->get(); +} + +void OverrideSetting::reset() +{ + Setting::reset(); +} + +void OverrideSetting::set(QVariant value) +{ + Setting::set(value); +} diff --git a/meshmc/launcher/settings/OverrideSetting.h b/meshmc/launcher/settings/OverrideSetting.h new file mode 100644 index 0000000000..0c279ddf5c --- /dev/null +++ b/meshmc/launcher/settings/OverrideSetting.h @@ -0,0 +1,70 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 <QObject> +#include <memory> + +#include "Setting.h" + +/*! + * \brief A setting that 'overrides another.' + * This means that the setting's default value will be the value of another + * setting. The other setting can be (and usually is) a part of a different + * SettingsObject than this one. + */ +class OverrideSetting : public Setting +{ + Q_OBJECT + public: + explicit OverrideSetting(std::shared_ptr<Setting> overriden, + std::shared_ptr<Setting> gate); + + virtual QVariant defValue() const; + virtual QVariant get() const; + virtual void set(QVariant value); + virtual void reset(); + + private: + bool isOverriding() const; + + protected: + std::shared_ptr<Setting> m_other; + std::shared_ptr<Setting> m_gate; +}; diff --git a/meshmc/launcher/settings/PassthroughSetting.cpp b/meshmc/launcher/settings/PassthroughSetting.cpp new file mode 100644 index 0000000000..e56fa0d138 --- /dev/null +++ b/meshmc/launcher/settings/PassthroughSetting.cpp @@ -0,0 +1,88 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 "PassthroughSetting.h" + +PassthroughSetting::PassthroughSetting(std::shared_ptr<Setting> other, + std::shared_ptr<Setting> gate) + : Setting(other->configKeys(), QVariant()) +{ + Q_ASSERT(other); + m_other = other; + m_gate = gate; +} + +bool PassthroughSetting::isOverriding() const +{ + if (!m_gate) { + return false; + } + return m_gate->get().toBool(); +} + +QVariant PassthroughSetting::defValue() const +{ + if (isOverriding()) { + return m_other->get(); + } + return m_other->defValue(); +} + +QVariant PassthroughSetting::get() const +{ + if (isOverriding()) { + return Setting::get(); + } + return m_other->get(); +} + +void PassthroughSetting::reset() +{ + if (isOverriding()) { + Setting::reset(); + } + m_other->reset(); +} + +void PassthroughSetting::set(QVariant value) +{ + if (isOverriding()) { + Setting::set(value); + } + m_other->set(value); +} diff --git a/meshmc/launcher/settings/PassthroughSetting.h b/meshmc/launcher/settings/PassthroughSetting.h new file mode 100644 index 0000000000..f9e5a577d8 --- /dev/null +++ b/meshmc/launcher/settings/PassthroughSetting.h @@ -0,0 +1,69 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 <QObject> +#include <memory> + +#include "Setting.h" + +/*! + * \brief A setting that 'overrides another.' based on the value of a 'gate' + * setting If 'gate' evaluates to true, the override stores and returns data If + * 'gate' evaluates to false, the original does, + */ +class PassthroughSetting : public Setting +{ + Q_OBJECT + public: + explicit PassthroughSetting(std::shared_ptr<Setting> overriden, + std::shared_ptr<Setting> gate); + + virtual QVariant defValue() const; + virtual QVariant get() const; + virtual void set(QVariant value); + virtual void reset(); + + private: + bool isOverriding() const; + + protected: + std::shared_ptr<Setting> m_other; + std::shared_ptr<Setting> m_gate; +}; diff --git a/meshmc/launcher/settings/Setting.cpp b/meshmc/launcher/settings/Setting.cpp new file mode 100644 index 0000000000..f3c9d78700 --- /dev/null +++ b/meshmc/launcher/settings/Setting.cpp @@ -0,0 +1,73 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 "Setting.h" +#include "settings/SettingsObject.h" + +Setting::Setting(QStringList synonyms, QVariant defVal) + : QObject(), m_synonyms(synonyms), m_defVal(defVal) +{ +} + +QVariant Setting::get() const +{ + SettingsObject* sbase = m_storage; + if (!sbase) { + return defValue(); + } else { + QVariant test = sbase->retrieveValue(*this); + if (!test.isValid()) + return defValue(); + return test; + } +} + +QVariant Setting::defValue() const +{ + return m_defVal; +} + +void Setting::set(QVariant value) +{ + emit SettingChanged(*this, value); +} + +void Setting::reset() +{ + emit settingReset(*this); +} diff --git a/meshmc/launcher/settings/Setting.h b/meshmc/launcher/settings/Setting.h new file mode 100644 index 0000000000..536512686e --- /dev/null +++ b/meshmc/launcher/settings/Setting.h @@ -0,0 +1,141 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 <QObject> +#include <QVariant> +#include <QStringList> +#include <memory> + +class SettingsObject; + +/*! + * + */ +class Setting : public QObject +{ + Q_OBJECT + public: + /** + * Construct a Setting + * + * Synonyms are all the possible names used in the settings object, in order + * of preference. First synonym is the ID, which identifies the setting in + * MeshMC. + * + * defVal is the default value that will be returned when the settings + * object doesn't have any value for this setting. + */ + explicit Setting(QStringList synonyms, QVariant defVal = QVariant()); + + /*! + * \brief Gets this setting's ID. + * This is used to refer to the setting within the application. + * \warning Changing the ID while the setting is registered with a + * SettingsObject results in undefined behavior. + * \return The ID of the setting. + */ + virtual QString id() const + { + return m_synonyms.first(); + } + + /*! + * \brief Gets this setting's config file key. + * This is used to store the setting's value in the config file. It is + * usually the same as the setting's ID, but it can be different. + * \return The setting's config file key. + */ + virtual QStringList configKeys() const + { + return m_synonyms; + } + + /*! + * \brief Gets this setting's value as a QVariant. + * This is done by calling the SettingsObject's retrieveValue() function. + * If this Setting doesn't have a SettingsObject, this returns an invalid + * QVariant. + * \return QVariant containing this setting's value. + * \sa value() + */ + virtual QVariant get() const; + + /*! + * \brief Gets this setting's default value. + * \return The default value of this setting. + */ + virtual QVariant defValue() const; + + signals: + /*! + * \brief Signal emitted when this Setting object's value changes. + * \param setting A reference to the Setting that changed. + * \param value This Setting object's new value. + */ + void SettingChanged(const Setting& setting, QVariant value); + + /*! + * \brief Signal emitted when this Setting object's value resets to default. + * \param setting A reference to the Setting that changed. + */ + void settingReset(const Setting& setting); + + public slots: + /*! + * \brief Changes the setting's value. + * This is done by emitting the SettingChanged() signal which will then be + * handled by the SettingsObject object and cause the setting to change. + * \param value The new value. + */ + virtual void set(QVariant value); + + /*! + * \brief Reset the setting to default + * This is done by emitting the settingReset() signal which will then be + * handled by the SettingsObject object and cause the setting to change. + */ + virtual void reset(); + + protected: + friend class SettingsObject; + SettingsObject* m_storage; + QStringList m_synonyms; + QVariant m_defVal; +}; diff --git a/meshmc/launcher/settings/SettingsObject.cpp b/meshmc/launcher/settings/SettingsObject.cpp new file mode 100644 index 0000000000..a73a2fd87c --- /dev/null +++ b/meshmc/launcher/settings/SettingsObject.cpp @@ -0,0 +1,166 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 "settings/SettingsObject.h" +#include "settings/Setting.h" +#include "settings/OverrideSetting.h" +#include "PassthroughSetting.h" +#include <QDebug> + +#include <QVariant> + +SettingsObject::SettingsObject(QObject* parent) : QObject(parent) {} + +SettingsObject::~SettingsObject() +{ + m_settings.clear(); +} + +std::shared_ptr<Setting> +SettingsObject::registerOverride(std::shared_ptr<Setting> original, + std::shared_ptr<Setting> gate) +{ + if (contains(original->id())) { + qCritical() << QString( + "Failed to register setting %1. ID already exists.") + .arg(original->id()); + return nullptr; // Fail + } + auto override = std::make_shared<OverrideSetting>(original, gate); + override->m_storage = this; + connectSignals(*override); + m_settings.insert(override->id(), override); + return override; +} + +std::shared_ptr<Setting> +SettingsObject::registerPassthrough(std::shared_ptr<Setting> original, + std::shared_ptr<Setting> gate) +{ + if (contains(original->id())) { + qCritical() << QString( + "Failed to register setting %1. ID already exists.") + .arg(original->id()); + return nullptr; // Fail + } + auto passthrough = std::make_shared<PassthroughSetting>(original, gate); + passthrough->m_storage = this; + connectSignals(*passthrough); + m_settings.insert(passthrough->id(), passthrough); + return passthrough; +} + +std::shared_ptr<Setting> SettingsObject::registerSetting(QStringList synonyms, + QVariant defVal) +{ + if (synonyms.empty()) + return nullptr; + if (contains(synonyms.first())) { + qCritical() << QString( + "Failed to register setting %1. ID already exists.") + .arg(synonyms.first()); + return nullptr; // Fail + } + auto setting = std::make_shared<Setting>(synonyms, defVal); + setting->m_storage = this; + connectSignals(*setting); + m_settings.insert(setting->id(), setting); + return setting; +} + +std::shared_ptr<Setting> SettingsObject::getSetting(const QString& id) const +{ + // Make sure there is a setting with the given ID. + if (!m_settings.contains(id)) + return NULL; + + return m_settings[id]; +} + +QVariant SettingsObject::get(const QString& id) const +{ + auto setting = getSetting(id); + return (setting ? setting->get() : QVariant()); +} + +bool SettingsObject::set(const QString& id, QVariant value) +{ + auto setting = getSetting(id); + if (!setting) { + qCritical() << QString( + "Error changing setting %1. Setting doesn't exist.") + .arg(id); + return false; + } else { + setting->set(value); + return true; + } +} + +void SettingsObject::reset(const QString& id) const +{ + auto setting = getSetting(id); + if (setting) + setting->reset(); +} + +bool SettingsObject::contains(const QString& id) +{ + return m_settings.contains(id); +} + +bool SettingsObject::reload() +{ + for (auto setting : m_settings.values()) { + setting->set(setting->get()); + } + return true; +} + +void SettingsObject::connectSignals(const Setting& setting) +{ + connect(&setting, SIGNAL(SettingChanged(const Setting&, QVariant)), + SLOT(changeSetting(const Setting&, QVariant))); + connect(&setting, SIGNAL(SettingChanged(const Setting&, QVariant)), + SIGNAL(SettingChanged(const Setting&, QVariant))); + + connect(&setting, SIGNAL(settingReset(Setting)), + SLOT(resetSetting(const Setting&))); + connect(&setting, SIGNAL(settingReset(Setting)), + SIGNAL(settingReset(const Setting&))); +} diff --git a/meshmc/launcher/settings/SettingsObject.h b/meshmc/launcher/settings/SettingsObject.h new file mode 100644 index 0000000000..ca7b73e9aa --- /dev/null +++ b/meshmc/launcher/settings/SettingsObject.h @@ -0,0 +1,246 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 <QObject> +#include <QMap> +#include <QStringList> +#include <QVariant> +#include <memory> + +class Setting; +class SettingsObject; + +typedef std::shared_ptr<SettingsObject> SettingsObjectPtr; + +/*! + * \brief The SettingsObject handles communicating settings between the + * application and a settings file. The class keeps a list of Setting objects. + * Each Setting object represents one of the application's settings. These + * Setting objects are registered with a SettingsObject and can be managed + * similarly to the way a list works. + * + * \author Andrew Okin + * \date 2/22/2013 + * + * \sa Setting + */ +class SettingsObject : public QObject +{ + Q_OBJECT + public: + class Lock + { + public: + Lock(SettingsObjectPtr locked) : m_locked(locked) + { + m_locked->suspendSave(); + } + ~Lock() + { + m_locked->resumeSave(); + } + + private: + SettingsObjectPtr m_locked; + }; + + public: + explicit SettingsObject(QObject* parent = 0); + virtual ~SettingsObject(); + /*! + * Registers an override setting for the given original setting in this + * settings object gate decides if the passthrough (true) or the original + * (false) is used for value + * + * This will fail if there is already a setting with the same ID as + * the one that is being registered. + * \return A valid Setting shared pointer if successful. + */ + std::shared_ptr<Setting> registerOverride(std::shared_ptr<Setting> original, + std::shared_ptr<Setting> gate); + + /*! + * Registers a passthorugh setting for the given original setting in this + * settings object gate decides if the passthrough (true) or the original + * (false) is used for value + * + * This will fail if there is already a setting with the same ID as + * the one that is being registered. + * \return A valid Setting shared pointer if successful. + */ + std::shared_ptr<Setting> + registerPassthrough(std::shared_ptr<Setting> original, + std::shared_ptr<Setting> gate); + + /*! + * Registers the given setting with this SettingsObject and connects the + * necessary signals. + * + * This will fail if there is already a setting with the same ID as + * the one that is being registered. + * \return A valid Setting shared pointer if successful. + */ + std::shared_ptr<Setting> registerSetting(QStringList synonyms, + QVariant defVal = QVariant()); + + /*! + * Registers the given setting with this SettingsObject and connects the + * necessary signals. + * + * This will fail if there is already a setting with the same ID as + * the one that is being registered. + * \return A valid Setting shared pointer if successful. + */ + std::shared_ptr<Setting> registerSetting(QString id, + QVariant defVal = QVariant()) + { + return registerSetting(QStringList(id), defVal); + } + + /*! + * \brief Gets the setting with the given ID. + * \param id The ID of the setting to get. + * \return A pointer to the setting with the given ID. + * Returns null if there is no setting with the given ID. + * \sa operator []() + */ + std::shared_ptr<Setting> getSetting(const QString& id) const; + + /*! + * \brief Gets the value of the setting with the given ID. + * \param id The ID of the setting to get. + * \return The setting's value as a QVariant. + * If no setting with the given ID exists, returns an invalid QVariant. + */ + QVariant get(const QString& id) const; + + /*! + * \brief Sets the value of the setting with the given ID. + * If no setting with the given ID exists, returns false + * \param id The ID of the setting to change. + * \param value The new value of the setting. + * \return True if successful, false if it failed. + */ + bool set(const QString& id, QVariant value); + + /*! + * \brief Reverts the setting with the given ID to default. + * \param id The ID of the setting to reset. + */ + void reset(const QString& id) const; + + /*! + * \brief Checks if this SettingsObject contains a setting with the given + * ID. + * \param id The ID to check for. + * \return True if the SettingsObject has a setting with the given ID. + */ + bool contains(const QString& id); + + /*! + * \brief Reloads the settings and emit signals for changed settings + * \return True if reloading was successful + */ + virtual bool reload(); + + virtual void suspendSave() = 0; + virtual void resumeSave() = 0; + signals: + /*! + * \brief Signal emitted when one of this SettingsObject object's settings + * changes. This is usually just connected directly to each Setting object's + * SettingChanged() signals. + * \param setting A reference to the Setting object that changed. + * \param value The Setting object's new value. + */ + void SettingChanged(const Setting& setting, QVariant value); + + /*! + * \brief Signal emitted when one of this SettingsObject object's settings + * resets. This is usually just connected directly to each Setting object's + * settingReset() signals. + * \param setting A reference to the Setting object that changed. + */ + void settingReset(const Setting& setting); + + protected slots: + /*! + * \brief Changes a setting. + * This slot is usually connected to each Setting object's + * SettingChanged() signal. The signal is emitted, causing this slot + * to update the setting's value in the config file. + * \param setting A reference to the Setting object that changed. + * \param value The setting's new value. + */ + virtual void changeSetting(const Setting& setting, QVariant value) = 0; + + /*! + * \brief Resets a setting. + * This slot is usually connected to each Setting object's + * settingReset() signal. The signal is emitted, causing this slot + * to update the setting's value in the config file. + * \param setting A reference to the Setting object that changed. + */ + virtual void resetSetting(const Setting& setting) = 0; + + protected: + /*! + * \brief Connects the necessary signals to the given Setting. + * \param setting The setting to connect. + */ + void connectSignals(const Setting& setting); + + /*! + * \brief Function used by Setting objects to get their values from the + * SettingsObject. + * \param setting The + * \return + */ + virtual QVariant retrieveValue(const Setting& setting) = 0; + + friend class Setting; + + private: + QMap<QString, std::shared_ptr<Setting>> m_settings; + + protected: + bool m_suspendSave = false; + bool m_doSave = false; +}; |
