summaryrefslogtreecommitdiff
path: root/src/instancemanager.h
diff options
context:
space:
mode:
authorQudix <17361645+Qudix@users.noreply.github.com>2020-11-09 13:02:01 -0600
committerQudix <17361645+Qudix@users.noreply.github.com>2020-11-09 13:02:01 -0600
commit0a3bdad8afd18a21a3697804ae344abd09e3712e (patch)
tree0bf234b41dff43cdfe39afea48c7c1d8bdd995c2 /src/instancemanager.h
parentb7b843d42badab83b262260035658259f24e4f9a (diff)
parentfef8543a58b4226242ce26c4c8876abaa64394cb (diff)
Merge branch 'master' of https://github.com/ModOrganizer2/modorganizer into master
Diffstat (limited to 'src/instancemanager.h')
-rw-r--r--src/instancemanager.h312
1 files changed, 294 insertions, 18 deletions
diff --git a/src/instancemanager.h b/src/instancemanager.h
index 79f1f30b..4f8b01c7 100644
--- a/src/instancemanager.h
+++ b/src/instancemanager.h
@@ -10,87 +10,363 @@ class Settings;
class PluginContainer;
+// represents an instance, either global or portable
+//
+// if setup() is not called, the game plugin is not available and the INI is
+// not processed at all, so name(), directory() and isPortable() really are the
+// only meaningful functions
+//
+// setup() must be called when MO wants to use the instance, it will read the
+// INI, figure out the game plugin to use and set it up by calling
+// setGameVaraint(), setGamePath(), etc. on it
+//
+// when setup() fails because the game name/directory or variant are missing,
+// setGame() and setVariant() can be called before retrying setup(); this
+// happens on startup if that information is missing
+//
class Instance
{
public:
+ // returned by setup()
+ //
enum class SetupResults
{
- Ok,
+ // instance is ready to be used
+ Okay,
+
+ // error while reading the INI
BadIni,
+
+ // both the game name and directory are missing from the ini; setup() will
+ // attempt to recover if either are missing, but not when both are
IniMissingGame,
+
+ // either:
+ // 1) there is no plugin with the given name, or
+ // 2) if the name is missing, no plugin can handle the game directory
PluginGone,
+
+ // the selected plugin does not consider the game directory as being valid
GameGone,
+
+ // there is no game variant specified in the INI, but the plugin requires
+ // one
MissingVariant
};
- Instance(QDir dir, bool portable, QString profileName={});
+ // a file or directory owned by this instance, used by objectsForDeletion()
+ //
+ struct Object
+ {
+ // path to the file or directory
+ QString path;
+
+ // whether this object must be deleted to properly delete the instance;
+ // typically true only for the instance directory itself, but not for the
+ // base directory, etc.
+ bool mandatoryDelete;
+
+
+ Object(QString p, bool d=false)
+ : path(std::move(p)), mandatoryDelete(d)
+ {
+ }
+
+ // puts mandatory delete on top
+ //
+ bool operator<(const Object& o) const
+ {
+ if (mandatoryDelete && !o.mandatoryDelete) {
+ return true;
+ } else if (!mandatoryDelete && o.mandatoryDelete) {
+ return false;
+ }
+
+ return false;
+ }
+ };
+
+
+
+ // an instance that lives in the given directory; `portable` must be `true`
+ // if this is a portable instance
+ //
+ // `profileName` can be given to override what's in the INI; this typically
+ // happens when the profile is overriden on the command line
+ //
+ 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
+ //
+ // setup() tries to recover from some errors, but can fail for a variety of
+ // reasons, see SetupResults
+ //
SetupResults setup(PluginContainer& plugins);
+
+ // overrides the game name and directory
+ //
void setGame(const QString& name, const QString& dir);
+
+ // overrides the game variant
+ //
void setVariant(const QString& name);
+
+ // returns the instance name; this is the directory name or "Portable" for
+ // portable instances
+ //
+ // can be called without setup()
+ //
QString name() const;
+
+ // returns either:
+ // 1) the game name from the INI, if readFromIni() was called;
+ // 2) gameName() from the game plugin if it was missing and setup() was
+ // called; or
+ // 3) whatever was given in setGame()
+ //
QString gameName() const;
+
+ // returns either:
+ // 1) the game directory from the INI, if readFromIni() was called;
+ // 2) gameDirectory() from the game plugin if it was missing and setup()
+ // was called; or
+ // 3) whatever was given in setGame()
+ //
QString gameDirectory() const;
- QDir directory() const;
+
+ // returns the instance directory as given in the constructor
+ //
+ QString directory() const;
+
+ // returns the base directory; empty if readFromIni() hasn't been called
+ //
+ QString baseDirectory() const;
+
+ // returns whether this is a portable instance, as given in the constructor
+ //
+ bool isPortable() const;
+
+ // returns the selected game plugin; will return null if setup() hasn't been
+ // called, or if it failed
+ //
MOBase::IPluginGame* gamePlugin() const;
+
+ // returns either:
+ // 1) the profile name given in the constructor if not empty;
+ // 2) the profile name from the INI if readFromIni() was called, or
+ // 3) the default profile name if it's missing (see
+ // AppConfig::defaultProfileName())
+ //
QString profileName() const;
+
+ // returns the path to the INI file for this instance; the file may not
+ // exist
+ //
QString iniPath() const;
- bool isPortable() const;
+
+ // whether this is the currently active instance in MO
+ //
+ bool isActive() const;
+
+ // returns a list of files and directories that must be deleted when deleting
+ // this instance; this will read the INI and fail if it's not accessible
+ //
+ // returns an empty list on failure
+ //
+ std::vector<Object> objectsForDeletion() const;
private:
- QDir m_dir;
+ QString m_dir;
bool m_portable;
- QString m_gameName, m_gameDir, m_gameVariant;
+ QString m_gameName, m_gameDir, m_gameVariant, m_baseDir;
MOBase::IPluginGame* m_plugin;
QString m_profile;
+ // figures out the game plugin for this instance
+ //
SetupResults getGamePlugin(PluginContainer& plugins);
+
+ // figures out the profile name for this instance
+ //
void getProfile(const Settings& s);
+
+ // updates the ini with the given values and the ones found by setup()
+ //
+ void updateIni();
};
+// manages global and portable instances
+//
class InstanceManager
{
public:
+ // there is only one manager; this isn't called instance() because it's hella
+ // confusing
+ //
static InstanceManager& singleton();
+ // overrides instance name found in registry
+ //
void overrideInstance(const QString& instanceName);
+
+ // overrides profile name from INI for currentInstance()
+ //
void overrideProfile(const QString& profileName);
+ // returns a game plugin that considers the given directory valid
+ //
+ // this will check for an INI file in the directory and use its game name
+ // and directory if available
+ //
+ // if there is no INI, if it's missing these values or if there are no game
+ // plugins that can handle these values, this returns the first plugin that
+ // considers the given directory valid
+ //
+ // returns null if all of this fails
+ //
const MOBase::IPluginGame* gamePluginForDirectory(
- const QDir& dir, const PluginContainer& plugins) const;
+ const QString& dir, const PluginContainer& plugins) const;
+ // clears the instance name from the registry; on restart, this will make MO
+ // either select the portable instance if it exists, or display the instance
+ // selection/creation dialog
+ //
void clearCurrentInstance();
+
+ // returns the current instance from the registry; this may be empty if the
+ // instance name in the registry is empty or non-existent and there is no
+ // portable instance set up
+ //
std::optional<Instance> currentInstance() const;
+
+ // sets the instance name in the registry so the same instance is opened next
+ // time MO runs
+ //
void setCurrentInstance(const QString &name);
+ // whether MO should allow the user to change the current instance from the
+ // user interface
+ //
bool allowedToChangeInstance() const;
- static bool isPortablePath(const QString& dataPath);
- static QString portablePath();
+
+ // whether a portable instance exists; this basically checks for an INI in
+ // the application directory
+ //
bool portableInstanceExists() const;
- QString instancesPath() const;
- QStringList instanceNames() const;
- std::vector<QDir> instancePaths() const;
+ // whether any instance exists, whether global or portable
+ //
+ bool hasAnyInstances() const;
+
+ // returns the absolute path to the portable instance, regardless of whether
+ // one exists
+ //
+ QString portablePath() const;
+ // returns the absolute path to the directory that contains global instances
+ // (typically AppData/Local/ModOrganizer)
+ //
+ QString globalInstancesRootPath() const;
+
+ // returns the list of absolute path to all existing global instances; this
+ // does not include the portable instance
+ //
+ std::vector<QString> globalInstancePaths() const;
+
+ // returns `name` modified so that it is a valid instance name
+ //
QString sanitizeInstanceName(const QString &name) 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
+ //
+ // may return an empty string if no unique name can be found
+ //
QString makeUniqueName(const QString& instanceName) const;
+
+ // returns whether a global instance with this name already exists
+ //
bool instanceExists(const QString& instanceName) const;
+
+ // returns whether the given instance name would be a valid name; this does
+ // not check whether the instance already exists, it's basiscally just a check
+ // against what sanitizeInstanceName() returns
+ //
bool validInstanceName(const QString& instanceName) const;
+
+ // returns the absolute path of a global instance with the given name; this
+ // does not check if the name is valid or if exists
+ //
QString instancePath(const QString& instanceName) const;
- static QString iniPath(const QDir& instanceDir);
+
+ // returns the absolute path to the INI file for the given instance directory;
+ // the file may not exist
+ //
+ QString iniPath(const QString& instanceDir) const;
private:
InstanceManager();
- bool portableInstallIsLocked() const;
private:
- bool m_overrideInstance{false};
- QString m_overrideInstanceName;
- bool m_overrideProfile{false};
- QString m_overrideProfileName;
+ std::optional<QString> m_overrideInstanceName;
+ std::optional<QString> m_overrideProfileName;
};
+
+// see setupInstance()
+//
+enum class SetupInstanceResults
+{
+ Okay,
+ TryAgain,
+ SelectAnother,
+ Exit
+};
+
+// if there are no instances configured, global or portable, shows the
+// create instance dialog and returns the new instance or empty if the user
+// cancelled
+//
+// if there is at least one instance available, unconditionally show the
+// instance manager dialog and returns the selected instance or empty if the
+// user cancelled
+//
+std::optional<Instance> selectInstance();
+
+// calls instance.setup() tries to handle problems by itself:
+//
+// - if the ini is missing some information, will show dialogs and ask the user
+// to fill in what's required (such as the game directory, variant, etc.);
+// if successful, returns TryAgain and setupInstance() can be called again
+// with the same instance
+//
+// - if the instance cannot be used (no game plugin found for it, ini can't
+// be read, etc.), returns SelectAnother
+//
+// - if the user cancels at any point, returns Exit
+//
+// - if the instance has been set up correctly, returns Okay
+//
+SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc);
+
#endif // MODORGANIZER_INSTANCEMANAGER_INCLUDED