diff options
| author | Jonathan Feenstra <26406078+JonathanFeenstra@users.noreply.github.com> | 2026-02-08 10:08:10 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-08 10:08:10 +0100 |
| commit | 4da0bffeee05c4589ae4d0addc7c695e31669990 (patch) | |
| tree | 9d6c793899935f3687d5df64f4daafcec4ebd36c /src | |
| parent | a394f02e97ad0bf328fa735cbdbfbe026c03655d (diff) | |
Add instance manager to plugin API (#2335)
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/envshortcut.cpp | 2 | ||||
| -rw-r--r-- | src/instancemanager.cpp | 65 | ||||
| -rw-r--r-- | src/instancemanager.h | 50 | ||||
| -rw-r--r-- | src/instancemanagerdialog.cpp | 5 | ||||
| -rw-r--r-- | src/instancemanagerproxy.cpp | 51 | ||||
| -rw-r--r-- | src/instancemanagerproxy.h | 50 | ||||
| -rw-r--r-- | src/moapplication.cpp | 2 | ||||
| -rw-r--r-- | src/moapplication.h | 4 | ||||
| -rw-r--r-- | src/organizerproxy.cpp | 8 | ||||
| -rw-r--r-- | src/organizerproxy.h | 3 |
11 files changed, 206 insertions, 37 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 123e876b..8b425272 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -109,6 +109,7 @@ mo2_add_filter(NAME src/application GROUPS multiprocess sanitychecks selfupdater + systemtraymanager updatedialog ) @@ -273,7 +274,9 @@ mo2_add_filter(NAME src/profiles GROUPS mo2_add_filter(NAME src/proxies GROUPS downloadmanagerproxy + executableslistproxy gamefeaturesproxy + instancemanagerproxy modlistproxy organizerproxy pluginlistproxy diff --git a/src/envshortcut.cpp b/src/envshortcut.cpp index 204435b7..be696105 100644 --- a/src/envshortcut.cpp +++ b/src/envshortcut.cpp @@ -132,7 +132,7 @@ Shortcut::Shortcut() : m_iconIndex(0) {} Shortcut::Shortcut(const Executable& exe) : Shortcut() { - const auto i = *InstanceManager::singleton().currentInstance(); + const auto& i = *InstanceManager::singleton().currentInstance(); m_name = MOBase::sanitizeFileName(exe.title()); m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath(); diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 90e9e519..fcb7bf67 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -39,6 +39,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QMessageBox> #include <QStandardPaths> #include <cstdint> +#include <memory> +#include <mutex> using namespace MOBase; @@ -57,11 +59,25 @@ QString Instance::displayName() const QString Instance::gameName() const { + if (!m_iniValuesRead && m_gameName.isEmpty()) { + std::scoped_lock lock(m_iniValuesMutex); + if (!m_iniValuesRead) { + readFromIni(); + } + } + return m_gameName; } QString Instance::gameDirectory() const { + if (!m_iniValuesRead && m_gameDir.isEmpty()) { + std::scoped_lock lock(m_iniValuesMutex); + if (!m_iniValuesRead) { + readFromIni(); + } + } + return m_gameDir; } @@ -72,6 +88,13 @@ QString Instance::directory() const QString Instance::baseDirectory() const { + if (!m_iniValuesRead) { + std::scoped_lock lock(m_iniValuesMutex); + if (!m_iniValuesRead) { + readFromIni(); + } + } + return m_baseDir; } @@ -82,6 +105,13 @@ MOBase::IPluginGame* Instance::gamePlugin() const QString Instance::profileName() const { + if (!m_iniValuesRead) { + std::scoped_lock lock(m_iniValuesMutex); + if (!m_iniValuesRead) { + readFromIni(); + } + } + return m_profile; } @@ -110,12 +140,13 @@ bool Instance::isActive() const return false; } -bool Instance::readFromIni() +bool Instance::readFromIni() const { Settings s(iniPath()); if (s.iniStatus() != QSettings::NoError) { log::error("can't read ini {}", iniPath()); + m_iniValuesRead = true; return false; } @@ -143,14 +174,18 @@ bool Instance::readFromIni() // figuring out profile from ini if it's missing getProfile(s); + m_iniValuesRead = true; return true; } Instance::SetupResults Instance::setup(PluginContainer& plugins) { - // read initial values from the ini - if (!readFromIni()) { - return SetupResults::BadIni; + if (!m_iniValuesRead) { + std::scoped_lock lock(m_iniValuesMutex); + // read initial values from the ini + if (!m_iniValuesRead && !readFromIni()) { + return SetupResults::BadIni; + } } // getting game plugin @@ -310,7 +345,7 @@ Instance::SetupResults Instance::getGamePlugin(PluginContainer& plugins) } } -void Instance::getProfile(const Settings& s) +void Instance::getProfile(const Settings& s) const { if (!m_profile.isEmpty()) { // there's already a profile set up, probably an override @@ -511,7 +546,7 @@ void InstanceManager::clearOverrides() m_overrideProfileName = {}; } -std::unique_ptr<Instance> InstanceManager::currentInstance() const +std::shared_ptr<Instance> InstanceManager::currentInstance() const { const QString profile = m_overrideProfileName ? *m_overrideProfileName : ""; @@ -520,20 +555,20 @@ std::unique_ptr<Instance> InstanceManager::currentInstance() const if (!allowedToChangeInstance()) { // force portable instance - return std::make_unique<Instance>(portablePath(), true, profile); + return std::make_shared<Instance>(portablePath(), true, profile); } if (name.isEmpty()) { if (portableInstanceExists()) { // use portable - return std::make_unique<Instance>(portablePath(), true, profile); + return std::make_shared<Instance>(portablePath(), true, profile); } else { // no instance set return {}; } } - return std::make_unique<Instance>(instancePath(name), false, profile); + return std::make_shared<Instance>(instancePath(name), false, profile); } void InstanceManager::clearCurrentInstance() @@ -583,6 +618,16 @@ std::vector<QString> InstanceManager::globalInstancePaths() const return list; } +std::shared_ptr<const Instance> +InstanceManager::getGlobalInstance(const QString& instanceName) const +{ + if (!instanceExists(instanceName)) { + return nullptr; + } + + return std::make_shared<const Instance>(instancePath(instanceName), false, ""); +} + bool InstanceManager::hasAnyInstances() const { return portableInstanceExists() || !globalInstancePaths().empty(); @@ -696,7 +741,7 @@ bool InstanceManager::instanceExists(const QString& instanceName) const return root.exists(instanceName); } -std::unique_ptr<Instance> selectInstance() +std::shared_ptr<Instance> selectInstance() { auto& m = InstanceManager::singleton(); diff --git a/src/instancemanager.h b/src/instancemanager.h index 176033e8..61cee327 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -1,9 +1,16 @@ #ifndef MODORGANIZER_INSTANCEMANAGER_INCLUDED #define MODORGANIZER_INSTANCEMANAGER_INCLUDED +#include <QDir> #include <QSettings> #include <QString> +#include <atomic> +#include <memory> +#include <mutex> + +#include <uibase/iinstance.h> + namespace MOBase { class IPluginGame; @@ -26,7 +33,7 @@ class PluginContainer; // setGame() and setVariant() can be called before retrying setup(); this // happens on startup if that information is missing // -class Instance +class Instance : public MOBase::IInstance { public: // returned by setup() @@ -92,18 +99,6 @@ public: // Instance(QString dir, bool portable, QString profileName = {}); - // reads in values from the INI if they were not given yet: - // - game name - // - game directory - // - game variant - // - profile name - // - // note that setup() already calls this - // - // returns false if the ini couldn't be read from - // - bool readFromIni(); - // finds the appropriate game plugin and sets it up so MO can use it; this // calls readFromIni() first // @@ -192,9 +187,24 @@ public: private: QString m_dir; bool m_portable; - QString m_gameName, m_gameDir, m_gameVariant, m_baseDir; + mutable QString m_gameName, m_gameDir, m_gameVariant, m_baseDir; MOBase::IPluginGame* m_plugin; - QString m_profile; + mutable QString m_profile; + + mutable std::mutex m_iniValuesMutex; + mutable std::atomic<bool> m_iniValuesRead{false}; + + // reads in values from the INI if they were not given yet: + // - game name + // - game directory + // - game variant + // - profile name + // + // note that setup() already calls this + // + // returns false if the ini couldn't be read from + // + bool readFromIni() const; // figures out the game plugin for this instance // @@ -202,7 +212,7 @@ private: // figures out the profile name for this instance // - void getProfile(const Settings& s); + void getProfile(const Settings& s) const; // updates the ini with the given values and the ones found by setup() // @@ -259,7 +269,7 @@ public: // instance name in the registry is empty or non-existent and there is no // portable instance set up // - std::unique_ptr<Instance> currentInstance() const; + std::shared_ptr<Instance> currentInstance() const; // sets the instance name in the registry so the same instance is opened next // time MO runs @@ -295,6 +305,10 @@ public: // std::vector<QString> globalInstancePaths() const; + // returns the global Instance with the given name, or null if it doesn't exist + // + std::shared_ptr<const Instance> getGlobalInstance(const QString& instanceName) const; + // sanitizes the given instance name and either // 1) returns it if there is no instance with this name // 2) tries to add " (N)" at the end until it works @@ -343,7 +357,7 @@ enum class SetupInstanceResults // instance manager dialog and returns the selected instance or empty if the // user cancelled // -std::unique_ptr<Instance> selectInstance(); +std::shared_ptr<Instance> selectInstance(); // calls instance.setup() tries to handle problems by itself: // diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index b5139e90..06e6d796 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -257,11 +257,6 @@ void InstanceManagerDialog::updateInstances() m_instances.insert(m_instances.begin(), std::make_unique<Instance>(m.portablePath(), true)); } - - // read all inis, ignore errors - for (auto&& i : m_instances) { - i->readFromIni(); - } } void InstanceManagerDialog::updateList() diff --git a/src/instancemanagerproxy.cpp b/src/instancemanagerproxy.cpp new file mode 100644 index 00000000..2ff7f714 --- /dev/null +++ b/src/instancemanagerproxy.cpp @@ -0,0 +1,51 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer 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. + +Mod Organizer 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 Mod Organizer. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "instancemanagerproxy.h" + +#include "instancemanager.h" + +#include <memory> +#include <vector> + +#include <QDir> +#include <QString> + +#include <uibase/iinstance.h> + +InstanceManagerProxy::InstanceManagerProxy(InstanceManager* instanceManager) + : m_Proxied(instanceManager) +{} + +std::shared_ptr<MOBase::IInstance> InstanceManagerProxy::currentInstance() const +{ + return m_Proxied->currentInstance(); +} + +std::vector<QDir> InstanceManagerProxy::globalInstancePaths() const +{ + const auto globalPaths = m_Proxied->globalInstancePaths(); + return std::vector<QDir>(globalPaths.begin(), globalPaths.end()); +} + +std::shared_ptr<const MOBase::IInstance> +InstanceManagerProxy::getGlobalInstance(const QString& instanceName) const +{ + return m_Proxied->getGlobalInstance(instanceName); +} diff --git a/src/instancemanagerproxy.h b/src/instancemanagerproxy.h new file mode 100644 index 00000000..3091db09 --- /dev/null +++ b/src/instancemanagerproxy.h @@ -0,0 +1,50 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer 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. + +Mod Organizer 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 Mod Organizer. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INSTANCEMANAGERPROXY_H +#define INSTANCEMANAGERPROXY_H + +#include "instancemanager.h" + +#include <memory> +#include <vector> + +#include <QDir> +#include <QString> + +#include <uibase/iinstancemanager.h> + +namespace MOBase +{ +class IInstance; +} + +class InstanceManagerProxy : public MOBase::IInstanceManager +{ +public: + InstanceManagerProxy(InstanceManager* instanceManager); + std::shared_ptr<MOBase::IInstance> currentInstance() const override; + std::vector<QDir> globalInstancePaths() const override; + std::shared_ptr<const MOBase::IInstance> + getGlobalInstance(const QString& instanceName) const override; + +private: + InstanceManager* m_Proxied; +}; +#endif // INSTANCEMANAGERPROXY_H diff --git a/src/moapplication.cpp b/src/moapplication.cpp index a92be85e..e676436a 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -439,7 +439,7 @@ void MOApplication::externalMessage(const QString& message) } } -std::unique_ptr<Instance> MOApplication::getCurrentInstance(bool forceSelect) +std::shared_ptr<Instance> MOApplication::getCurrentInstance(bool forceSelect) { auto& m = InstanceManager::singleton(); auto currentInstance = m.currentInstance(); diff --git a/src/moapplication.h b/src/moapplication.h index 498242f3..f3805545 100644 --- a/src/moapplication.h +++ b/src/moapplication.h @@ -81,14 +81,14 @@ private: QString m_defaultStyle; std::unique_ptr<env::ModuleNotification> m_modules; - std::unique_ptr<Instance> m_instance; + std::shared_ptr<Instance> m_instance; std::unique_ptr<Settings> m_settings; std::unique_ptr<NexusInterface> m_nexus; std::unique_ptr<PluginContainer> m_plugins; std::unique_ptr<OrganizerCore> m_core; void externalMessage(const QString& message); - std::unique_ptr<Instance> getCurrentInstance(bool forceSelect); + std::shared_ptr<Instance> getCurrentInstance(bool forceSelect); std::optional<int> setupInstanceLoop(Instance& currentInstance, PluginContainer& pc); void purgeOldFiles(); }; diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 973af3d1..90ab900a 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -5,6 +5,7 @@ #include "gamefeaturesproxy.h" #include "glob_matching.h" #include "instancemanager.h" +#include "instancemanagerproxy.h" #include "modlistproxy.h" #include "organizercore.h" #include "plugincontainer.h" @@ -26,6 +27,8 @@ OrganizerProxy::OrganizerProxy(OrganizerCore* organizer, PluginContainer* pluginContainer, MOBase::IPlugin* plugin) : m_Proxied(organizer), m_PluginContainer(pluginContainer), m_Plugin(plugin), + m_InstanceManagerProxy( + std::make_unique<InstanceManagerProxy>(&InstanceManager::singleton())), m_DownloadManagerProxy( std::make_unique<DownloadManagerProxy>(this, organizer->downloadManager())), m_ModListProxy(std::make_unique<ModListProxy>(this, organizer->modList())), @@ -353,6 +356,11 @@ std::shared_ptr<const MOBase::IFileTree> OrganizerProxy::virtualFileTree() const return m_Proxied->m_VirtualFileTree.value(); } +MOBase::IInstanceManager* OrganizerProxy::instanceManager() const +{ + return m_InstanceManagerProxy.get(); +} + MOBase::IDownloadManager* OrganizerProxy::downloadManager() const { return m_DownloadManagerProxy.get(); diff --git a/src/organizerproxy.h b/src/organizerproxy.h index 8382bd24..01271990 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -12,6 +12,7 @@ class GameFeaturesProxy; class PluginContainer; class DownloadManagerProxy; class ExecutablesListProxy; +class InstanceManagerProxy; class ModListProxy; class PluginListProxy; @@ -63,6 +64,7 @@ public: // IOrganizer interface const std::function<bool(const FileInfo&)>& filter) const override; std::shared_ptr<const MOBase::IFileTree> virtualFileTree() const override; + MOBase::IInstanceManager* instanceManager() const override; MOBase::IDownloadManager* downloadManager() const override; MOBase::IPluginList* pluginList() const override; MOBase::IModList* modList() const override; @@ -156,6 +158,7 @@ private: std::vector<boost::signals2::connection> m_Connections; + std::unique_ptr<InstanceManagerProxy> m_InstanceManagerProxy; std::unique_ptr<DownloadManagerProxy> m_DownloadManagerProxy; std::unique_ptr<ModListProxy> m_ModListProxy; std::unique_ptr<PluginListProxy> m_PluginListProxy; |
