NOISSUE tabs -> spaces

This commit is contained in:
Petr Mrázek
2018-07-15 14:51:05 +02:00
parent 03280cc62e
commit bbb3b3e6f6
577 changed files with 51938 additions and 51938 deletions

View File

@ -25,37 +25,37 @@ BaseInstaller::BaseInstaller()
bool BaseInstaller::isApplied(MinecraftInstance *on)
{
return QFile::exists(filename(on->instanceRoot()));
return QFile::exists(filename(on->instanceRoot()));
}
bool BaseInstaller::add(MinecraftInstance *to)
{
if (!patchesDir(to->instanceRoot()).exists())
{
QDir(to->instanceRoot()).mkdir("patches");
}
if (!patchesDir(to->instanceRoot()).exists())
{
QDir(to->instanceRoot()).mkdir("patches");
}
if (isApplied(to))
{
if (!remove(to))
{
return false;
}
}
if (isApplied(to))
{
if (!remove(to))
{
return false;
}
}
return true;
return true;
}
bool BaseInstaller::remove(MinecraftInstance *from)
{
return QFile::remove(filename(from->instanceRoot()));
return QFile::remove(filename(from->instanceRoot()));
}
QString BaseInstaller::filename(const QString &root) const
{
return patchesDir(root).absoluteFilePath(id() + ".json");
return patchesDir(root).absoluteFilePath(id() + ".json");
}
QDir BaseInstaller::patchesDir(const QString &root) const
{
return QDir(root + "/patches/");
return QDir(root + "/patches/");
}

View File

@ -30,17 +30,17 @@ typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
class MULTIMC_LOGIC_EXPORT BaseInstaller
{
public:
BaseInstaller();
virtual ~BaseInstaller(){};
bool isApplied(MinecraftInstance *on);
BaseInstaller();
virtual ~BaseInstaller(){};
bool isApplied(MinecraftInstance *on);
virtual bool add(MinecraftInstance *to);
virtual bool remove(MinecraftInstance *from);
virtual bool add(MinecraftInstance *to);
virtual bool remove(MinecraftInstance *from);
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
protected:
virtual QString id() const = 0;
QString filename(const QString &root) const;
QDir patchesDir(const QString &root) const;
virtual QString id() const = 0;
QString filename(const QString &root) const;
QDir patchesDir(const QString &root) const;
};

View File

@ -27,281 +27,281 @@
#include "Commandline.h"
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
: QObject()
: QObject()
{
m_settings = settings;
m_rootDir = rootDir;
m_settings = settings;
m_rootDir = rootDir;
m_settings->registerSetting("name", "Unnamed Instance");
m_settings->registerSetting("iconKey", "default");
m_settings->registerSetting("notes", "");
m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0);
m_settings->registerSetting("name", "Unnamed Instance");
m_settings->registerSetting("iconKey", "default");
m_settings->registerSetting("notes", "");
m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0);
// Custom Commands
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
// Custom Commands
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
// Console
auto consoleSetting = m_settings->registerSetting("OverrideConsole", false);
m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("ShowConsoleOnError"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting);
// Console
auto consoleSetting = m_settings->registerSetting("OverrideConsole", false);
m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("ShowConsoleOnError"), consoleSetting);
m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
}
QString BaseInstance::getPreLaunchCommand()
{
return settings()->get("PreLaunchCommand").toString();
return settings()->get("PreLaunchCommand").toString();
}
QString BaseInstance::getWrapperCommand()
{
return settings()->get("WrapperCommand").toString();
return settings()->get("WrapperCommand").toString();
}
QString BaseInstance::getPostExitCommand()
{
return settings()->get("PostExitCommand").toString();
return settings()->get("PostExitCommand").toString();
}
int BaseInstance::getConsoleMaxLines() const
{
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk);
if(!conversionOk)
{
maxLines = lineSetting->defValue().toInt();
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
}
return maxLines;
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
bool conversionOk = false;
int maxLines = lineSetting->get().toInt(&conversionOk);
if(!conversionOk)
{
maxLines = lineSetting->defValue().toInt();
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
}
return maxLines;
}
bool BaseInstance::shouldStopOnConsoleOverflow() const
{
return settings()->get("ConsoleOverflowStop").toBool();
return settings()->get("ConsoleOverflowStop").toBool();
}
void BaseInstance::iconUpdated(QString key)
{
if(iconKey() == key)
{
emit propertiesChanged(this);
}
if(iconKey() == key)
{
emit propertiesChanged(this);
}
}
void BaseInstance::invalidate()
{
changeStatus(Status::Gone);
qDebug() << "Instance" << id() << "has been invalidated.";
changeStatus(Status::Gone);
qDebug() << "Instance" << id() << "has been invalidated.";
}
void BaseInstance::nuke()
{
changeStatus(Status::Gone);
qDebug() << "Instance" << id() << "has been deleted by MultiMC.";
FS::deletePath(instanceRoot());
changeStatus(Status::Gone);
qDebug() << "Instance" << id() << "has been deleted by MultiMC.";
FS::deletePath(instanceRoot());
}
void BaseInstance::changeStatus(BaseInstance::Status newStatus)
{
Status status = currentStatus();
if(status != newStatus)
{
m_status = newStatus;
emit statusChanged(status, newStatus);
}
Status status = currentStatus();
if(status != newStatus)
{
m_status = newStatus;
emit statusChanged(status, newStatus);
}
}
BaseInstance::Status BaseInstance::currentStatus() const
{
return m_status;
return m_status;
}
QString BaseInstance::id() const
{
return QFileInfo(instanceRoot()).fileName();
return QFileInfo(instanceRoot()).fileName();
}
bool BaseInstance::isRunning() const
{
return m_isRunning;
return m_isRunning;
}
void BaseInstance::setRunning(bool running)
{
if(running == m_isRunning)
return;
if(running == m_isRunning)
return;
m_isRunning = running;
m_isRunning = running;
if(running)
{
m_timeStarted = QDateTime::currentDateTime();
}
else
{
qint64 current = settings()->get("totalTimePlayed").toLongLong();
QDateTime timeEnded = QDateTime::currentDateTime();
settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
emit propertiesChanged(this);
}
if(running)
{
m_timeStarted = QDateTime::currentDateTime();
}
else
{
qint64 current = settings()->get("totalTimePlayed").toLongLong();
QDateTime timeEnded = QDateTime::currentDateTime();
settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
emit propertiesChanged(this);
}
emit runningStatusChanged(running);
emit runningStatusChanged(running);
}
int64_t BaseInstance::totalTimePlayed() const
{
qint64 current = settings()->get("totalTimePlayed").toLongLong();
if(m_isRunning)
{
QDateTime timeNow = QDateTime::currentDateTime();
return current + m_timeStarted.secsTo(timeNow);
}
return current;
qint64 current = settings()->get("totalTimePlayed").toLongLong();
if(m_isRunning)
{
QDateTime timeNow = QDateTime::currentDateTime();
return current + m_timeStarted.secsTo(timeNow);
}
return current;
}
void BaseInstance::resetTimePlayed()
{
settings()->reset("totalTimePlayed");
settings()->reset("totalTimePlayed");
}
QString BaseInstance::instanceType() const
{
return m_settings->get("InstanceType").toString();
return m_settings->get("InstanceType").toString();
}
QString BaseInstance::instanceRoot() const
{
return m_rootDir;
return m_rootDir;
}
InstancePtr BaseInstance::getSharedPtr()
{
return shared_from_this();
return shared_from_this();
}
SettingsObjectPtr BaseInstance::settings() const
{
return m_settings;
return m_settings;
}
bool BaseInstance::canLaunch() const
{
return (!hasVersionBroken() && !isRunning());
return (!hasVersionBroken() && !isRunning());
}
bool BaseInstance::reloadSettings()
{
return m_settings->reload();
return m_settings->reload();
}
qint64 BaseInstance::lastLaunch() const
{
return m_settings->get("lastLaunchTime").value<qint64>();
return m_settings->get("lastLaunchTime").value<qint64>();
}
void BaseInstance::setLastLaunch(qint64 val)
{
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("lastLaunchTime", val);
emit propertiesChanged(this);
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("lastLaunchTime", val);
emit propertiesChanged(this);
}
void BaseInstance::setGroupInitial(QString val)
{
if(m_group == val)
{
return;
}
m_group = val;
emit propertiesChanged(this);
if(m_group == val)
{
return;
}
m_group = val;
emit propertiesChanged(this);
}
void BaseInstance::setGroupPost(QString val)
{
if(m_group == val)
{
return;
}
setGroupInitial(val);
emit groupChanged();
if(m_group == val)
{
return;
}
setGroupInitial(val);
emit groupChanged();
}
QString BaseInstance::group() const
{
return m_group;
return m_group;
}
void BaseInstance::setNotes(QString val)
{
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("notes", val);
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("notes", val);
}
QString BaseInstance::notes() const
{
return m_settings->get("notes").toString();
return m_settings->get("notes").toString();
}
void BaseInstance::setIconKey(QString val)
{
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("iconKey", val);
emit propertiesChanged(this);
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("iconKey", val);
emit propertiesChanged(this);
}
QString BaseInstance::iconKey() const
{
return m_settings->get("iconKey").toString();
return m_settings->get("iconKey").toString();
}
void BaseInstance::setName(QString val)
{
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("name", val);
emit propertiesChanged(this);
//FIXME: if no change, do not set. setting involves saving a file.
m_settings->set("name", val);
emit propertiesChanged(this);
}
QString BaseInstance::name() const
{
return m_settings->get("name").toString();
return m_settings->get("name").toString();
}
QString BaseInstance::windowTitle() const
{
return "MultiMC: " + name();
return "MultiMC: " + name();
}
// FIXME: why is this here? move it to MinecraftInstance!!!
QStringList BaseInstance::extraArguments() const
{
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
}
std::shared_ptr<LaunchTask> BaseInstance::getLaunchTask()
{
return m_launchProcess;
return m_launchProcess;
}
void BaseInstance::setProvider(BaseInstanceProvider* provider)
{
// only once.
assert(!m_provider);
if(m_provider)
{
qWarning() << "Provider set more than once for instance" << id();
}
m_provider = provider;
// only once.
assert(!m_provider);
if(m_provider)
{
qWarning() << "Provider set more than once for instance" << id();
}
m_provider = provider;
}
BaseInstanceProvider* BaseInstance::provider() const
{
return m_provider;
return m_provider;
}

View File

@ -53,229 +53,229 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
*/
class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
{
Q_OBJECT
Q_OBJECT
protected:
/// no-touchy!
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
/// no-touchy!
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
public: /* types */
enum class Status
{
Present,
Gone // either nuked or invalidated
};
enum class Status
{
Present,
Gone // either nuked or invalidated
};
public:
/// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance() {};
/// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance() {};
virtual void init() = 0;
virtual void saveNow() = 0;
virtual void init() = 0;
virtual void saveNow() = 0;
/// nuke thoroughly - deletes the instance contents, notifies the list/model which is
/// responsible of cleaning up the husk
void nuke();
/// nuke thoroughly - deletes the instance contents, notifies the list/model which is
/// responsible of cleaning up the husk
void nuke();
/***
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
* but it has not necessarily been deleted.
*
* Happens when the instance folder changes to some other location, or the instance is removed by external means.
*/
void invalidate();
/***
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
* but it has not necessarily been deleted.
*
* Happens when the instance folder changes to some other location, or the instance is removed by external means.
*/
void invalidate();
/// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
/// be unique.
virtual QString id() const;
/// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
/// be unique.
virtual QString id() const;
void setRunning(bool running);
bool isRunning() const;
int64_t totalTimePlayed() const;
void resetTimePlayed();
void setRunning(bool running);
bool isRunning() const;
int64_t totalTimePlayed() const;
void resetTimePlayed();
void setProvider(BaseInstanceProvider * provider);
BaseInstanceProvider * provider() const;
void setProvider(BaseInstanceProvider * provider);
BaseInstanceProvider * provider() const;
/// get the type of this instance
QString instanceType() const;
/// get the type of this instance
QString instanceType() const;
/// Path to the instance's root directory.
QString instanceRoot() const;
/// Path to the instance's root directory.
QString instanceRoot() const;
QString name() const;
void setName(QString val);
QString name() const;
void setName(QString val);
/// Value used for instance window titles
QString windowTitle() const;
/// Value used for instance window titles
QString windowTitle() const;
QString iconKey() const;
void setIconKey(QString val);
QString iconKey() const;
void setIconKey(QString val);
QString notes() const;
void setNotes(QString val);
QString notes() const;
void setNotes(QString val);
QString group() const;
void setGroupInitial(QString val);
void setGroupPost(QString val);
QString group() const;
void setGroupInitial(QString val);
void setGroupPost(QString val);
QString getPreLaunchCommand();
QString getPostExitCommand();
QString getWrapperCommand();
QString getPreLaunchCommand();
QString getPostExitCommand();
QString getWrapperCommand();
/// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
{
return level;
};
/// guess log level from a line of game log
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
{
return level;
};
virtual QStringList extraArguments() const;
virtual QStringList extraArguments() const;
/// Traits. Normally inside the version, depends on instance implementation.
virtual QSet <QString> traits() const = 0;
/// Traits. Normally inside the version, depends on instance implementation.
virtual QSet <QString> traits() const = 0;
/**
* Gets the time that the instance was last launched.
* Stored in milliseconds since epoch.
*/
qint64 lastLaunch() const;
/// Sets the last launched time to 'val' milliseconds since epoch
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
/**
* Gets the time that the instance was last launched.
* Stored in milliseconds since epoch.
*/
qint64 lastLaunch() const;
/// Sets the last launched time to 'val' milliseconds since epoch
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
InstancePtr getSharedPtr();
InstancePtr getSharedPtr();
/*!
* \brief Gets this instance's settings object.
* This settings object stores instance-specific settings.
* \return A pointer to this instance's settings object.
*/
virtual SettingsObjectPtr settings() const;
/*!
* \brief Gets this instance's settings object.
* This settings object stores instance-specific settings.
* \return A pointer to this instance's settings object.
*/
virtual SettingsObjectPtr settings() const;
/// returns a valid update task
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
/// returns a valid update task
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container)
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
/// returns a valid launcher (task container)
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
/// returns the current launch task (if any)
std::shared_ptr<LaunchTask> getLaunchTask();
/// returns the current launch task (if any)
std::shared_ptr<LaunchTask> getLaunchTask();
/*!
* Create envrironment variables for running the instance
*/
virtual QProcessEnvironment createEnvironment() = 0;
/*!
* Create envrironment variables for running the instance
*/
virtual QProcessEnvironment createEnvironment() = 0;
/*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
*/
virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
/*!
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
*/
virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
/*!
* Returns the root folder to use for looking up log files
*/
virtual QString getLogFileRoot() = 0;
/*!
* Returns the root folder to use for looking up log files
*/
virtual QString getLogFileRoot() = 0;
virtual QString getStatusbarDescription() = 0;
virtual QString getStatusbarDescription() = 0;
/// FIXME: this really should be elsewhere...
virtual QString instanceConfigFolder() const = 0;
/// FIXME: this really should be elsewhere...
virtual QString instanceConfigFolder() const = 0;
/// get variables this instance exports
virtual QMap<QString, QString> getVariables() const = 0;
/// get variables this instance exports
virtual QMap<QString, QString> getVariables() const = 0;
virtual QString typeName() const = 0;
virtual QString typeName() const = 0;
bool hasVersionBroken() const
{
return m_hasBrokenVersion;
}
void setVersionBroken(bool value)
{
if(m_hasBrokenVersion != value)
{
m_hasBrokenVersion = value;
emit propertiesChanged(this);
}
}
bool hasVersionBroken() const
{
return m_hasBrokenVersion;
}
void setVersionBroken(bool value)
{
if(m_hasBrokenVersion != value)
{
m_hasBrokenVersion = value;
emit propertiesChanged(this);
}
}
bool hasUpdateAvailable() const
{
return m_hasUpdate;
}
void setUpdateAvailable(bool value)
{
if(m_hasUpdate != value)
{
m_hasUpdate = value;
emit propertiesChanged(this);
}
}
bool hasUpdateAvailable() const
{
return m_hasUpdate;
}
void setUpdateAvailable(bool value)
{
if(m_hasUpdate != value)
{
m_hasUpdate = value;
emit propertiesChanged(this);
}
}
bool hasCrashed() const
{
return m_crashed;
}
void setCrashed(bool value)
{
if(m_crashed != value)
{
m_crashed = value;
emit propertiesChanged(this);
}
}
bool hasCrashed() const
{
return m_crashed;
}
void setCrashed(bool value)
{
if(m_crashed != value)
{
m_crashed = value;
emit propertiesChanged(this);
}
}
virtual bool canLaunch() const;
virtual bool canEdit() const = 0;
virtual bool canExport() const = 0;
virtual bool canLaunch() const;
virtual bool canEdit() const = 0;
virtual bool canExport() const = 0;
bool reloadSettings();
bool reloadSettings();
/**
* 'print' a verbose desription of the instance into a QStringList
*/
virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
/**
* 'print' a verbose desription of the instance into a QStringList
*/
virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
Status currentStatus() const;
Status currentStatus() const;
int getConsoleMaxLines() const;
bool shouldStopOnConsoleOverflow() const;
int getConsoleMaxLines() const;
bool shouldStopOnConsoleOverflow() const;
protected:
void changeStatus(Status newStatus);
void changeStatus(Status newStatus);
signals:
/*!
* \brief Signal emitted when properties relevant to the instance view change
*/
void propertiesChanged(BaseInstance *inst);
/*!
* \brief Signal emitted when groups are affected in any way
*/
void groupChanged();
/*!
* \brief Signal emitted when properties relevant to the instance view change
*/
void propertiesChanged(BaseInstance *inst);
/*!
* \brief Signal emitted when groups are affected in any way
*/
void groupChanged();
void launchTaskChanged(std::shared_ptr<LaunchTask>);
void launchTaskChanged(std::shared_ptr<LaunchTask>);
void runningStatusChanged(bool running);
void runningStatusChanged(bool running);
void statusChanged(Status from, Status to);
void statusChanged(Status from, Status to);
protected slots:
void iconUpdated(QString key);
void iconUpdated(QString key);
protected: /* data */
QString m_rootDir;
QString m_group;
SettingsObjectPtr m_settings;
// InstanceFlags m_flags;
bool m_isRunning = false;
std::shared_ptr<LaunchTask> m_launchProcess;
QDateTime m_timeStarted;
BaseInstanceProvider * m_provider = nullptr;
QString m_rootDir;
QString m_group;
SettingsObjectPtr m_settings;
// InstanceFlags m_flags;
bool m_isRunning = false;
std::shared_ptr<LaunchTask> m_launchProcess;
QDateTime m_timeStarted;
BaseInstanceProvider * m_provider = nullptr;
private: /* data */
Status m_status = Status::Present;
bool m_crashed = false;
bool m_hasUpdate = false;
bool m_hasBrokenVersion = false;
Status m_status = Status::Present;
bool m_crashed = false;
bool m_hasUpdate = false;
bool m_hasBrokenVersion = false;
};
Q_DECLARE_METATYPE(std::shared_ptr<BaseInstance>)

View File

@ -12,46 +12,46 @@ using InstanceLocator = std::pair<InstancePtr, int>;
enum class InstCreateError
{
NoCreateError = 0,
NoSuchVersion,
UnknownCreateError,
InstExists,
CantCreateDir
NoCreateError = 0,
NoSuchVersion,
UnknownCreateError,
InstExists,
CantCreateDir
};
class MULTIMC_LOGIC_EXPORT BaseInstanceProvider : public QObject
{
Q_OBJECT
Q_OBJECT
public:
BaseInstanceProvider(SettingsObjectPtr settings) : m_globalSettings(settings)
{
// nil
}
BaseInstanceProvider(SettingsObjectPtr settings) : m_globalSettings(settings)
{
// nil
}
public:
virtual QList<InstanceId> discoverInstances() = 0;
virtual InstancePtr loadInstance(const InstanceId &id) = 0;
virtual void loadGroupList() = 0;
virtual void saveGroupList() = 0;
virtual QList<InstanceId> discoverInstances() = 0;
virtual InstancePtr loadInstance(const InstanceId &id) = 0;
virtual void loadGroupList() = 0;
virtual void saveGroupList() = 0;
virtual QString getStagedInstancePath()
{
return QString();
}
virtual bool commitStagedInstance(const QString & path, const QString& instanceName, const QString & groupName)
{
return false;
}
virtual bool destroyStagingPath(const QString & path)
{
return true;
}
virtual QString getStagedInstancePath()
{
return QString();
}
virtual bool commitStagedInstance(const QString & path, const QString& instanceName, const QString & groupName)
{
return false;
}
virtual bool destroyStagingPath(const QString & path)
{
return true;
}
signals:
// Emit this when the list of provided instances changed
void instancesChanged();
// Emit when the set of groups your provider supplies changes.
void groupsChanged(QSet<QString> groups);
// Emit this when the list of provided instances changed
void instancesChanged();
// Emit when the set of groups your provider supplies changes.
void groupsChanged(QSet<QString> groups);
protected:
SettingsObjectPtr m_globalSettings;
SettingsObjectPtr m_globalSettings;
};

View File

@ -25,33 +25,33 @@
class BaseVersion
{
public:
virtual ~BaseVersion() {}
/*!
* A string used to identify this version in config files.
* This should be unique within the version list or shenanigans will occur.
*/
virtual QString descriptor() = 0;
virtual ~BaseVersion() {}
/*!
* A string used to identify this version in config files.
* This should be unique within the version list or shenanigans will occur.
*/
virtual QString descriptor() = 0;
/*!
* The name of this version as it is displayed to the user.
* For example: "1.5.1"
*/
virtual QString name() = 0;
/*!
* The name of this version as it is displayed to the user.
* For example: "1.5.1"
*/
virtual QString name() = 0;
/*!
* This should return a string that describes
* the kind of version this is (Stable, Beta, Snapshot, whatever)
*/
virtual QString typeString() const = 0;
/*!
* This should return a string that describes
* the kind of version this is (Stable, Beta, Snapshot, whatever)
*/
virtual QString typeString() const = 0;
virtual bool operator<(BaseVersion &a)
{
return name() < a.name();
};
virtual bool operator>(BaseVersion &a)
{
return name() > a.name();
};
virtual bool operator<(BaseVersion &a)
{
return name() < a.name();
};
virtual bool operator>(BaseVersion &a)
{
return name() > a.name();
};
};
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;

View File

@ -22,78 +22,78 @@ BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
{
for (int i = 0; i < count(); i++)
{
if (at(i)->descriptor() == descriptor)
return at(i);
}
return BaseVersionPtr();
for (int i = 0; i < count(); i++)
{
if (at(i)->descriptor() == descriptor)
return at(i);
}
return BaseVersionPtr();
}
BaseVersionPtr BaseVersionList::getRecommended() const
{
if (count() <= 0)
return BaseVersionPtr();
else
return at(0);
if (count() <= 0)
return BaseVersionPtr();
else
return at(0);
}
QVariant BaseVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (!index.isValid())
return QVariant();
if (index.row() > count())
return QVariant();
if (index.row() > count())
return QVariant();
BaseVersionPtr version = at(index.row());
BaseVersionPtr version = at(index.row());
switch (role)
{
case VersionPointerRole:
return qVariantFromValue(version);
switch (role)
{
case VersionPointerRole:
return qVariantFromValue(version);
case VersionRole:
return version->name();
case VersionRole:
return version->name();
case VersionIdRole:
return version->descriptor();
case VersionIdRole:
return version->descriptor();
case TypeRole:
return version->typeString();
case TypeRole:
return version->typeString();
default:
return QVariant();
}
default:
return QVariant();
}
}
BaseVersionList::RoleList BaseVersionList::providesRoles() const
{
return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
}
int BaseVersionList::rowCount(const QModelIndex &parent) const
{
// Return count
return count();
// Return count
return count();
}
int BaseVersionList::columnCount(const QModelIndex &parent) const
{
return 1;
return 1;
}
QHash<int, QByteArray> BaseVersionList::roleNames() const
{
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
roles.insert(VersionRole, "version");
roles.insert(VersionIdRole, "versionId");
roles.insert(ParentVersionRole, "parentGameVersion");
roles.insert(RecommendedRole, "recommended");
roles.insert(LatestRole, "latest");
roles.insert(TypeRole, "type");
roles.insert(BranchRole, "branch");
roles.insert(PathRole, "path");
roles.insert(ArchitectureRole, "architecture");
return roles;
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
roles.insert(VersionRole, "version");
roles.insert(VersionIdRole, "versionId");
roles.insert(ParentVersionRole, "parentGameVersion");
roles.insert(RecommendedRole, "recommended");
roles.insert(LatestRole, "latest");
roles.insert(TypeRole, "type");
roles.insert(BranchRole, "branch");
roles.insert(PathRole, "path");
roles.insert(ArchitectureRole, "architecture");
return roles;
}

View File

@ -38,85 +38,85 @@
*/
class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel
{
Q_OBJECT
Q_OBJECT
public:
enum ModelRoles
{
VersionPointerRole = Qt::UserRole,
VersionRole,
VersionIdRole,
ParentVersionRole,
RecommendedRole,
LatestRole,
TypeRole,
BranchRole,
PathRole,
ArchitectureRole,
SortRole
};
typedef QList<int> RoleList;
enum ModelRoles
{
VersionPointerRole = Qt::UserRole,
VersionRole,
VersionIdRole,
ParentVersionRole,
RecommendedRole,
LatestRole,
TypeRole,
BranchRole,
PathRole,
ArchitectureRole,
SortRole
};
typedef QList<int> RoleList;
explicit BaseVersionList(QObject *parent = 0);
explicit BaseVersionList(QObject *parent = 0);
/*!
* \brief Gets a task that will reload the version list.
* Simply execute the task to load the list.
* The task returned by this function should reset the model when it's done.
* \return A pointer to a task that reloads the version list.
*/
virtual shared_qobject_ptr<Task> getLoadTask() = 0;
/*!
* \brief Gets a task that will reload the version list.
* Simply execute the task to load the list.
* The task returned by this function should reset the model when it's done.
* \return A pointer to a task that reloads the version list.
*/
virtual shared_qobject_ptr<Task> getLoadTask() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be
//loaded.
virtual bool isLoaded() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be
//loaded.
virtual bool isLoaded() = 0;
//! Gets the version at the given index.
virtual const BaseVersionPtr at(int i) const = 0;
//! Gets the version at the given index.
virtual const BaseVersionPtr at(int i) const = 0;
//! Returns the number of versions in the list.
virtual int count() const = 0;
//! Returns the number of versions in the list.
virtual int count() const = 0;
//////// List Model Functions ////////
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QHash<int, QByteArray> roleNames() const override;
//////// List Model Functions ////////
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QHash<int, QByteArray> roleNames() const override;
//! which roles are provided by this version list?
virtual RoleList providesRoles() const;
//! which roles are provided by this version list?
virtual RoleList providesRoles() const;
/*!
* \brief Finds a version by its descriptor.
* \param descriptor The descriptor of the version to find.
* \return A const pointer to the version with the given descriptor. NULL if
* one doesn't exist.
*/
virtual BaseVersionPtr findVersion(const QString &descriptor);
/*!
* \brief Finds a version by its descriptor.
* \param descriptor The descriptor of the version to find.
* \return A const pointer to the version with the given descriptor. NULL if
* one doesn't exist.
*/
virtual BaseVersionPtr findVersion(const QString &descriptor);
/*!
* \brief Gets the recommended version from this list
* If the list doesn't support recommended versions, this works exactly as getLatestStable
*/
virtual BaseVersionPtr getRecommended() const;
/*!
* \brief Gets the recommended version from this list
* If the list doesn't support recommended versions, this works exactly as getLatestStable
*/
virtual BaseVersionPtr getRecommended() const;
/*!
* Sorts the version list.
*/
virtual void sortVersions() = 0;
/*!
* Sorts the version list.
*/
virtual void sortVersions() = 0;
protected
slots:
/*!
* Updates this list with the given list of versions.
* This is done by copying each version in the given list and inserting it
* into this one.
* We need to do this so that we can set the parents of the versions are set to this
* version list. This can't be done in the load task, because the versions the load
* task creates are on the load task's thread and Qt won't allow their parents
* to be set to something created on another thread.
* To get around that problem, we invoke this method on the GUI thread, which
* then copies the versions and sets their parents correctly.
* \param versions List of versions whose parents should be set.
*/
virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
/*!
* Updates this list with the given list of versions.
* This is done by copying each version in the given list and inserting it
* into this one.
* We need to do this so that we can set the parents of the versions are set to this
* version list. This can't be done in the load task, because the versions the load
* task creates are on the load task's thread and Qt won't allow their parents
* to be set to something created on another thread.
* To get around that problem, we invoke this method on the GUI thread, which
* then copies the versions and sets their parents correctly.
* \param versions List of versions whose parents should be set.
*/
virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
};

View File

@ -3,446 +3,446 @@ project(MultiMC_logic)
include (UnitTest)
set(CORE_SOURCES
# LOGIC - Base classes and infrastructure
BaseInstaller.h
BaseInstaller.cpp
BaseVersionList.h
BaseVersionList.cpp
InstanceList.h
InstanceList.cpp
InstanceTask.h
InstanceTask.cpp
LoggedProcess.h
LoggedProcess.cpp
MessageLevel.cpp
MessageLevel.h
BaseInstanceProvider.h
FolderInstanceProvider.h
FolderInstanceProvider.cpp
BaseVersion.h
BaseInstance.h
BaseInstance.cpp
NullInstance.h
MMCZip.h
MMCZip.cpp
MMCStrings.h
MMCStrings.cpp
# LOGIC - Base classes and infrastructure
BaseInstaller.h
BaseInstaller.cpp
BaseVersionList.h
BaseVersionList.cpp
InstanceList.h
InstanceList.cpp
InstanceTask.h
InstanceTask.cpp
LoggedProcess.h
LoggedProcess.cpp
MessageLevel.cpp
MessageLevel.h
BaseInstanceProvider.h
FolderInstanceProvider.h
FolderInstanceProvider.cpp
BaseVersion.h
BaseInstance.h
BaseInstance.cpp
NullInstance.h
MMCZip.h
MMCZip.cpp
MMCStrings.h
MMCStrings.cpp
# Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h
InstanceCreationTask.cpp
InstanceCopyTask.h
InstanceCopyTask.cpp
InstanceImportTask.h
InstanceImportTask.cpp
# Basic instance manipulation tasks (derived from InstanceTask)
InstanceCreationTask.h
InstanceCreationTask.cpp
InstanceCopyTask.h
InstanceCopyTask.cpp
InstanceImportTask.h
InstanceImportTask.cpp
# Use tracking separate from memory management
Usable.h
# Use tracking separate from memory management
Usable.h
# Prefix tree where node names are strings between separators
SeparatorPrefixTree.h
# Prefix tree where node names are strings between separators
SeparatorPrefixTree.h
# WARNING: globals live here
Env.h
Env.cpp
# WARNING: globals live here
Env.h
Env.cpp
# String filters
Filter.h
Filter.cpp
# String filters
Filter.h
Filter.cpp
# JSON parsing helpers
Json.h
Json.cpp
# JSON parsing helpers
Json.h
Json.cpp
FileSystem.h
FileSystem.cpp
FileSystem.h
FileSystem.cpp
Exception.h
Exception.h
# RW lock protected map
RWStorage.h
# RW lock protected map
RWStorage.h
# A variable that has an implicit default value and keeps track of changes
DefaultVariable.h
# A variable that has an implicit default value and keeps track of changes
DefaultVariable.h
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
QObjectPtr.h
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
QObjectPtr.h
# Compression support
GZip.h
GZip.cpp
# Compression support
GZip.h
GZip.cpp
# Command line parameter parsing
Commandline.h
Commandline.cpp
# Command line parameter parsing
Commandline.h
Commandline.cpp
# Version number string support
Version.h
Version.cpp
# Version number string support
Version.h
Version.cpp
# A Recursive file system watcher
RecursiveFileSystemWatcher.h
RecursiveFileSystemWatcher.cpp
# A Recursive file system watcher
RecursiveFileSystemWatcher.h
RecursiveFileSystemWatcher.cpp
)
add_unit_test(FileSystem
SOURCES FileSystem_test.cpp
LIBS MultiMC_logic
DATA testdata
)
SOURCES FileSystem_test.cpp
LIBS MultiMC_logic
DATA testdata
)
add_unit_test(GZip
SOURCES GZip_test.cpp
LIBS MultiMC_logic
)
SOURCES GZip_test.cpp
LIBS MultiMC_logic
)
set(PATHMATCHER_SOURCES
# Path matchers
pathmatcher/FSTreeMatcher.h
pathmatcher/IPathMatcher.h
pathmatcher/MultiMatcher.h
pathmatcher/RegexpMatcher.h
# Path matchers
pathmatcher/FSTreeMatcher.h
pathmatcher/IPathMatcher.h
pathmatcher/MultiMatcher.h
pathmatcher/RegexpMatcher.h
)
set(NET_SOURCES
# network stuffs
net/ByteArraySink.h
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
net/HttpMetaCache.h
net/MetaCacheSink.cpp
net/MetaCacheSink.h
net/NetAction.h
net/NetJob.cpp
net/NetJob.h
net/PasteUpload.cpp
net/PasteUpload.h
net/Sink.h
net/URLConstants.cpp
net/URLConstants.h
net/Validator.h
# network stuffs
net/ByteArraySink.h
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
net/HttpMetaCache.h
net/MetaCacheSink.cpp
net/MetaCacheSink.h
net/NetAction.h
net/NetJob.cpp
net/NetJob.h
net/PasteUpload.cpp
net/PasteUpload.h
net/Sink.h
net/URLConstants.cpp
net/URLConstants.h
net/Validator.h
)
# Game launch logic
set(LAUNCH_SOURCES
launch/steps/PostLaunchCommand.cpp
launch/steps/PostLaunchCommand.h
launch/steps/PreLaunchCommand.cpp
launch/steps/PreLaunchCommand.h
launch/steps/TextPrint.cpp
launch/steps/TextPrint.h
launch/steps/Update.cpp
launch/steps/Update.h
launch/LaunchStep.cpp
launch/LaunchStep.h
launch/LaunchTask.cpp
launch/LaunchTask.h
launch/LogModel.cpp
launch/LogModel.h
launch/steps/PostLaunchCommand.cpp
launch/steps/PostLaunchCommand.h
launch/steps/PreLaunchCommand.cpp
launch/steps/PreLaunchCommand.h
launch/steps/TextPrint.cpp
launch/steps/TextPrint.h
launch/steps/Update.cpp
launch/steps/Update.h
launch/LaunchStep.cpp
launch/LaunchStep.h
launch/LaunchTask.cpp
launch/LaunchTask.h
launch/LogModel.cpp
launch/LogModel.h
)
# Old update system
set(UPDATE_SOURCES
updater/GoUpdate.h
updater/GoUpdate.cpp
updater/UpdateChecker.h
updater/UpdateChecker.cpp
updater/DownloadTask.h
updater/DownloadTask.cpp
updater/GoUpdate.h
updater/GoUpdate.cpp
updater/UpdateChecker.h
updater/UpdateChecker.cpp
updater/DownloadTask.h
updater/DownloadTask.cpp
)
add_unit_test(UpdateChecker
SOURCES updater/UpdateChecker_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
SOURCES updater/UpdateChecker_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
add_unit_test(DownloadTask
SOURCES updater/DownloadTask_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
SOURCES updater/DownloadTask_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
# Rarely used notifications
set(NOTIFICATIONS_SOURCES
# Notifications - short warning messages
notifications/NotificationChecker.h
notifications/NotificationChecker.cpp
# Notifications - short warning messages
notifications/NotificationChecker.h
notifications/NotificationChecker.cpp
)
# Backend for the news bar... there's usually no news.
set(NEWS_SOURCES
# News System
news/NewsChecker.h
news/NewsChecker.cpp
news/NewsEntry.h
news/NewsEntry.cpp
# News System
news/NewsChecker.h
news/NewsChecker.cpp
news/NewsEntry.h
news/NewsEntry.cpp
)
# Icon interface
set(ICONS_SOURCES
# News System
icons/IIconList.h
icons/IIconList.cpp
# News System
icons/IIconList.h
icons/IIconList.cpp
)
# Minecraft services status checker
set(STATUS_SOURCES
# Status system
status/StatusChecker.h
status/StatusChecker.cpp
# Status system
status/StatusChecker.h
status/StatusChecker.cpp
)
# Support for Minecraft instances and launch
set(MINECRAFT_SOURCES
# Minecraft support
minecraft/auth/AuthSession.h
minecraft/auth/AuthSession.cpp
minecraft/auth/MojangAccountList.h
minecraft/auth/MojangAccountList.cpp
minecraft/auth/MojangAccount.h
minecraft/auth/MojangAccount.cpp
minecraft/auth/YggdrasilTask.h
minecraft/auth/YggdrasilTask.cpp
minecraft/auth/flows/AuthenticateTask.h
minecraft/auth/flows/AuthenticateTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/ValidateTask.h
minecraft/auth/flows/ValidateTask.cpp
minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp
minecraft/update/FMLLibrariesTask.cpp
minecraft/update/FMLLibrariesTask.h
minecraft/update/FoldersTask.cpp
minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp
minecraft/update/LibrariesTask.h
minecraft/launch/ClaimAccount.cpp
minecraft/launch/ClaimAccount.h
minecraft/launch/CreateServerResourcePacksFolder.cpp
minecraft/launch/CreateServerResourcePacksFolder.h
minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h
minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/legacy/LegacyModList.h
minecraft/legacy/LegacyModList.cpp
minecraft/legacy/LegacyInstance.h
minecraft/legacy/LegacyInstance.cpp
minecraft/legacy/LegacyUpgradeTask.h
minecraft/legacy/LegacyUpgradeTask.cpp
minecraft/GradleSpecifier.h
minecraft/MinecraftInstance.cpp
minecraft/MinecraftInstance.h
minecraft/LaunchProfile.cpp
minecraft/LaunchProfile.h
minecraft/Component.cpp
minecraft/Component.h
minecraft/ComponentList.cpp
minecraft/ComponentList.h
minecraft/ComponentUpdateTask.cpp
minecraft/ComponentUpdateTask.h
minecraft/MinecraftLoadAndCheck.h
minecraft/MinecraftLoadAndCheck.cpp
minecraft/MinecraftUpdate.h
minecraft/MinecraftUpdate.cpp
minecraft/MojangVersionFormat.cpp
minecraft/MojangVersionFormat.h
minecraft/Rule.cpp
minecraft/Rule.h
minecraft/OneSixVersionFormat.cpp
minecraft/OneSixVersionFormat.h
minecraft/OpSys.cpp
minecraft/OpSys.h
minecraft/ParseUtils.cpp
minecraft/ParseUtils.h
minecraft/ProfileUtils.cpp
minecraft/ProfileUtils.h
minecraft/Library.cpp
minecraft/Library.h
minecraft/MojangDownloadInfo.h
minecraft/VersionFile.cpp
minecraft/VersionFile.h
minecraft/VersionFilterData.h
minecraft/VersionFilterData.cpp
minecraft/Mod.h
minecraft/Mod.cpp
minecraft/ModsModel.h
minecraft/ModsModel.cpp
minecraft/SimpleModList.h
minecraft/SimpleModList.cpp
minecraft/World.h
minecraft/World.cpp
minecraft/WorldList.h
minecraft/WorldList.cpp
# Minecraft support
minecraft/auth/AuthSession.h
minecraft/auth/AuthSession.cpp
minecraft/auth/MojangAccountList.h
minecraft/auth/MojangAccountList.cpp
minecraft/auth/MojangAccount.h
minecraft/auth/MojangAccount.cpp
minecraft/auth/YggdrasilTask.h
minecraft/auth/YggdrasilTask.cpp
minecraft/auth/flows/AuthenticateTask.h
minecraft/auth/flows/AuthenticateTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/ValidateTask.h
minecraft/auth/flows/ValidateTask.cpp
minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp
minecraft/update/FMLLibrariesTask.cpp
minecraft/update/FMLLibrariesTask.h
minecraft/update/FoldersTask.cpp
minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp
minecraft/update/LibrariesTask.h
minecraft/launch/ClaimAccount.cpp
minecraft/launch/ClaimAccount.h
minecraft/launch/CreateServerResourcePacksFolder.cpp
minecraft/launch/CreateServerResourcePacksFolder.h
minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h
minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/legacy/LegacyModList.h
minecraft/legacy/LegacyModList.cpp
minecraft/legacy/LegacyInstance.h
minecraft/legacy/LegacyInstance.cpp
minecraft/legacy/LegacyUpgradeTask.h
minecraft/legacy/LegacyUpgradeTask.cpp
minecraft/GradleSpecifier.h
minecraft/MinecraftInstance.cpp
minecraft/MinecraftInstance.h
minecraft/LaunchProfile.cpp
minecraft/LaunchProfile.h
minecraft/Component.cpp
minecraft/Component.h
minecraft/ComponentList.cpp
minecraft/ComponentList.h
minecraft/ComponentUpdateTask.cpp
minecraft/ComponentUpdateTask.h
minecraft/MinecraftLoadAndCheck.h
minecraft/MinecraftLoadAndCheck.cpp
minecraft/MinecraftUpdate.h
minecraft/MinecraftUpdate.cpp
minecraft/MojangVersionFormat.cpp
minecraft/MojangVersionFormat.h
minecraft/Rule.cpp
minecraft/Rule.h
minecraft/OneSixVersionFormat.cpp
minecraft/OneSixVersionFormat.h
minecraft/OpSys.cpp
minecraft/OpSys.h
minecraft/ParseUtils.cpp
minecraft/ParseUtils.h
minecraft/ProfileUtils.cpp
minecraft/ProfileUtils.h
minecraft/Library.cpp
minecraft/Library.h
minecraft/MojangDownloadInfo.h
minecraft/VersionFile.cpp
minecraft/VersionFile.h
minecraft/VersionFilterData.h
minecraft/VersionFilterData.cpp
minecraft/Mod.h
minecraft/Mod.cpp
minecraft/ModsModel.h
minecraft/ModsModel.cpp
minecraft/SimpleModList.h
minecraft/SimpleModList.cpp
minecraft/World.h
minecraft/World.cpp
minecraft/WorldList.h
minecraft/WorldList.cpp
# Assets
minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp
# Assets
minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp
# Forge and all things forge related
minecraft/forge/ForgeXzDownload.h
minecraft/forge/ForgeXzDownload.cpp
# Forge and all things forge related
minecraft/forge/ForgeXzDownload.h
minecraft/forge/ForgeXzDownload.cpp
# Skin upload utilities
minecraft/SkinUpload.cpp
minecraft/SkinUpload.h
)
# Skin upload utilities
minecraft/SkinUpload.cpp
minecraft/SkinUpload.h
)
add_unit_test(GradleSpecifier
SOURCES minecraft/GradleSpecifier_test.cpp
LIBS MultiMC_logic
)
SOURCES minecraft/GradleSpecifier_test.cpp
LIBS MultiMC_logic
)
add_unit_test(MojangVersionFormat
SOURCES minecraft/MojangVersionFormat_test.cpp
LIBS MultiMC_logic
DATA minecraft/testdata
)
SOURCES minecraft/MojangVersionFormat_test.cpp
LIBS MultiMC_logic
DATA minecraft/testdata
)
add_unit_test(Library
SOURCES minecraft/Library_test.cpp
LIBS MultiMC_logic
)
SOURCES minecraft/Library_test.cpp
LIBS MultiMC_logic
)
# FIXME: shares data with FileSystem test
add_unit_test(SimpleModList
SOURCES minecraft/SimpleModList_test.cpp
DATA testdata
LIBS MultiMC_logic
)
SOURCES minecraft/SimpleModList_test.cpp
DATA testdata
LIBS MultiMC_logic
)
add_unit_test(ParseUtils
SOURCES minecraft/ParseUtils_test.cpp
LIBS MultiMC_logic
)
SOURCES minecraft/ParseUtils_test.cpp
LIBS MultiMC_logic
)
# the screenshots feature
set(SCREENSHOTS_SOURCES
screenshots/Screenshot.h
screenshots/ImgurUpload.h
screenshots/ImgurUpload.cpp
screenshots/ImgurAlbumCreation.h
screenshots/ImgurAlbumCreation.cpp
screenshots/Screenshot.h
screenshots/ImgurUpload.h
screenshots/ImgurUpload.cpp
screenshots/ImgurAlbumCreation.h
screenshots/ImgurAlbumCreation.cpp
)
set(TASKS_SOURCES
# Tasks
tasks/Task.h
tasks/Task.cpp
tasks/SequentialTask.h
tasks/SequentialTask.cpp
# Tasks
tasks/Task.h
tasks/Task.cpp
tasks/SequentialTask.h
tasks/SequentialTask.cpp
)
set(SETTINGS_SOURCES
# Settings
settings/INIFile.cpp
settings/INIFile.h
settings/INISettingsObject.cpp
settings/INISettingsObject.h
settings/OverrideSetting.cpp
settings/OverrideSetting.h
settings/PassthroughSetting.cpp
settings/PassthroughSetting.h
settings/Setting.cpp
settings/Setting.h
settings/SettingsObject.cpp
settings/SettingsObject.h
# Settings
settings/INIFile.cpp
settings/INIFile.h
settings/INISettingsObject.cpp
settings/INISettingsObject.h
settings/OverrideSetting.cpp
settings/OverrideSetting.h
settings/PassthroughSetting.cpp
settings/PassthroughSetting.h
settings/Setting.cpp
settings/Setting.h
settings/SettingsObject.cpp
settings/SettingsObject.h
)
add_unit_test(INIFile
SOURCES settings/INIFile_test.cpp
LIBS MultiMC_logic
)
SOURCES settings/INIFile_test.cpp
LIBS MultiMC_logic
)
set(JAVA_SOURCES
# Java related code
java/launch/CheckJava.cpp
java/launch/CheckJava.h
java/JavaChecker.h
java/JavaChecker.cpp
java/JavaCheckerJob.h
java/JavaCheckerJob.cpp
java/JavaInstall.h
java/JavaInstall.cpp
java/JavaInstallList.h
java/JavaInstallList.cpp
java/JavaUtils.h
java/JavaUtils.cpp
java/JavaVersion.h
java/JavaVersion.cpp
# Java related code
java/launch/CheckJava.cpp
java/launch/CheckJava.h
java/JavaChecker.h
java/JavaChecker.cpp
java/JavaCheckerJob.h
java/JavaCheckerJob.cpp
java/JavaInstall.h
java/JavaInstall.cpp
java/JavaInstallList.h
java/JavaInstallList.cpp
java/JavaUtils.h
java/JavaUtils.cpp
java/JavaVersion.h
java/JavaVersion.cpp
)
add_unit_test(JavaVersion
SOURCES java/JavaVersion_test.cpp
LIBS MultiMC_logic
)
SOURCES java/JavaVersion_test.cpp
LIBS MultiMC_logic
)
set(TRANSLATIONS_SOURCES
translations/TranslationsModel.h
translations/TranslationsModel.cpp
translations/TranslationsModel.h
translations/TranslationsModel.cpp
)
set(TOOLS_SOURCES
# Tools
tools/BaseExternalTool.cpp
tools/BaseExternalTool.h
tools/BaseProfiler.cpp
tools/BaseProfiler.h
tools/JProfiler.cpp
tools/JProfiler.h
tools/JVisualVM.cpp
tools/JVisualVM.h
tools/MCEditTool.cpp
tools/MCEditTool.h
# Tools
tools/BaseExternalTool.cpp
tools/BaseExternalTool.h
tools/BaseProfiler.cpp
tools/BaseProfiler.h
tools/JProfiler.cpp
tools/JProfiler.h
tools/JVisualVM.cpp
tools/JVisualVM.h
tools/MCEditTool.cpp
tools/MCEditTool.h
)
set(META_SOURCES
# Metadata sources
meta/JsonFormat.cpp
meta/JsonFormat.h
meta/BaseEntity.cpp
meta/BaseEntity.h
meta/VersionList.cpp
meta/VersionList.h
meta/Version.cpp
meta/Version.h
meta/Index.cpp
meta/Index.h
# Metadata sources
meta/JsonFormat.cpp
meta/JsonFormat.h
meta/BaseEntity.cpp
meta/BaseEntity.h
meta/VersionList.cpp
meta/VersionList.h
meta/Version.cpp
meta/Version.h
meta/Index.cpp
meta/Index.h
)
set(FTB_SOURCES
modplatform/ftb/FtbPackFetchTask.h
modplatform/ftb/FtbPackFetchTask.cpp
modplatform/ftb/FtbPackInstallTask.h
modplatform/ftb/FtbPackInstallTask.cpp
modplatform/ftb/FtbPackFetchTask.h
modplatform/ftb/FtbPackFetchTask.cpp
modplatform/ftb/FtbPackInstallTask.h
modplatform/ftb/FtbPackInstallTask.cpp
modplatform/ftb/PackHelpers.h
modplatform/ftb/PackHelpers.h
)
set(FLAME_SOURCES
# Flame
modplatform/flame/PackManifest.h
modplatform/flame/PackManifest.cpp
modplatform/flame/FileResolvingTask.h
modplatform/flame/FileResolvingTask.cpp
# Flame
modplatform/flame/PackManifest.h
modplatform/flame/PackManifest.cpp
modplatform/flame/FileResolvingTask.h
modplatform/flame/FileResolvingTask.cpp
)
add_unit_test(Index
SOURCES meta/Index_test.cpp
LIBS MultiMC_logic
)
SOURCES meta/Index_test.cpp
LIBS MultiMC_logic
)
################################ COMPILE ################################
@ -450,25 +450,25 @@ add_unit_test(Index
find_package(ZLIB REQUIRED)
set(LOGIC_SOURCES
${CORE_SOURCES}
${PATHMATCHER_SOURCES}
${NET_SOURCES}
${LAUNCH_SOURCES}
${UPDATE_SOURCES}
${NOTIFICATIONS_SOURCES}
${NEWS_SOURCES}
${STATUS_SOURCES}
${MINECRAFT_SOURCES}
${SCREENSHOTS_SOURCES}
${TASKS_SOURCES}
${SETTINGS_SOURCES}
${JAVA_SOURCES}
${TRANSLATIONS_SOURCES}
${TOOLS_SOURCES}
${META_SOURCES}
${ICONS_SOURCES}
${FTB_SOURCES}
${FLAME_SOURCES}
${CORE_SOURCES}
${PATHMATCHER_SOURCES}
${NET_SOURCES}
${LAUNCH_SOURCES}
${UPDATE_SOURCES}
${NOTIFICATIONS_SOURCES}
${NEWS_SOURCES}
${STATUS_SOURCES}
${MINECRAFT_SOURCES}
${SCREENSHOTS_SOURCES}
${TASKS_SOURCES}
${SETTINGS_SOURCES}
${JAVA_SOURCES}
${TRANSLATIONS_SOURCES}
${TOOLS_SOURCES}
${META_SOURCES}
${ICONS_SOURCES}
${FTB_SOURCES}
${FLAME_SOURCES}
)
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
@ -485,7 +485,7 @@ target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "$
# Install it
install(
TARGETS MultiMC_logic
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
TARGETS MultiMC_logic
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
)

View File

@ -27,453 +27,453 @@ namespace Commandline
// commandline splitter
QStringList splitArgs(QString args)
{
QStringList argv;
QString current;
bool escape = false;
QChar inquotes;
for (int i = 0; i < args.length(); i++)
{
QChar cchar = args.at(i);
QStringList argv;
QString current;
bool escape = false;
QChar inquotes;
for (int i = 0; i < args.length(); i++)
{
QChar cchar = args.at(i);
// \ escaped
if (escape)
{
current += cchar;
escape = false;
// in "quotes"
}
else if (!inquotes.isNull())
{
if (cchar == '\\')
escape = true;
else if (cchar == inquotes)
inquotes = 0;
else
current += cchar;
// otherwise
}
else
{
if (cchar == ' ')
{
if (!current.isEmpty())
{
argv << current;
current.clear();
}
}
else if (cchar == '"' || cchar == '\'')
inquotes = cchar;
else
current += cchar;
}
}
if (!current.isEmpty())
argv << current;
return argv;
// \ escaped
if (escape)
{
current += cchar;
escape = false;
// in "quotes"
}
else if (!inquotes.isNull())
{
if (cchar == '\\')
escape = true;
else if (cchar == inquotes)
inquotes = 0;
else
current += cchar;
// otherwise
}
else
{
if (cchar == ' ')
{
if (!current.isEmpty())
{
argv << current;
current.clear();
}
}
else if (cchar == '"' || cchar == '\'')
inquotes = cchar;
else
current += cchar;
}
}
if (!current.isEmpty())
argv << current;
return argv;
}
Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
{
m_flagStyle = flagStyle;
m_argStyle = argStyle;
m_flagStyle = flagStyle;
m_argStyle = argStyle;
}
// styles setter/getter
void Parser::setArgumentStyle(ArgumentStyle::Enum style)
{
m_argStyle = style;
m_argStyle = style;
}
ArgumentStyle::Enum Parser::argumentStyle()
{
return m_argStyle;
return m_argStyle;
}
void Parser::setFlagStyle(FlagStyle::Enum style)
{
m_flagStyle = style;
m_flagStyle = style;
}
FlagStyle::Enum Parser::flagStyle()
{
return m_flagStyle;
return m_flagStyle;
}
// setup methods
void Parser::addSwitch(QString name, bool def)
{
if (m_params.contains(name))
throw "Name not unique";
if (m_params.contains(name))
throw "Name not unique";
OptionDef *param = new OptionDef;
param->type = otSwitch;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
OptionDef *param = new OptionDef;
param->type = otSwitch;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
}
void Parser::addOption(QString name, QVariant def)
{
if (m_params.contains(name))
throw "Name not unique";
if (m_params.contains(name))
throw "Name not unique";
OptionDef *param = new OptionDef;
param->type = otOption;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
OptionDef *param = new OptionDef;
param->type = otOption;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
}
void Parser::addArgument(QString name, bool required, QVariant def)
{
if (m_params.contains(name))
throw "Name not unique";
if (m_params.contains(name))
throw "Name not unique";
PositionalDef *param = new PositionalDef;
param->name = name;
param->def = def;
param->required = required;
param->metavar = name;
PositionalDef *param = new PositionalDef;
param->name = name;
param->def = def;
param->required = required;
param->metavar = name;
m_positionals.append(param);
m_params[name] = (CommonDef *)param;
m_positionals.append(param);
m_params[name] = (CommonDef *)param;
}
void Parser::addDocumentation(QString name, QString doc, QString metavar)
{
if (!m_params.contains(name))
throw "Name does not exist";
if (!m_params.contains(name))
throw "Name does not exist";
CommonDef *param = m_params[name];
param->doc = doc;
if (!metavar.isNull())
param->metavar = metavar;
CommonDef *param = m_params[name];
param->doc = doc;
if (!metavar.isNull())
param->metavar = metavar;
}
void Parser::addShortOpt(QString name, QChar flag)
{
if (!m_params.contains(name))
throw "Name does not exist";
if (!m_options.contains(name))
throw "Name is not an Option or Swtich";
if (!m_params.contains(name))
throw "Name does not exist";
if (!m_options.contains(name))
throw "Name is not an Option or Swtich";
OptionDef *param = m_options[name];
m_flags[flag] = param;
param->flag = flag;
OptionDef *param = m_options[name];
m_flags[flag] = param;
param->flag = flag;
}
// help methods
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
{
QStringList help;
help << compileUsage(progName, useFlags) << "\r\n";
QStringList help;
help << compileUsage(progName, useFlags) << "\r\n";
// positionals
if (!m_positionals.isEmpty())
{
help << "\r\n";
help << "Positional arguments:\r\n";
QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *param = it2.next();
help << " " << param->metavar;
help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
help << param->doc << "\r\n";
}
}
// positionals
if (!m_positionals.isEmpty())
{
help << "\r\n";
help << "Positional arguments:\r\n";
QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *param = it2.next();
help << " " << param->metavar;
help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
help << param->doc << "\r\n";
}
}
// Options
if (!m_optionList.isEmpty())
{
help << "\r\n";
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
// Options
if (!m_optionList.isEmpty())
{
help << "\r\n";
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
help << "Options & Switches:\r\n";
QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
help << " ";
int nameLength = optPrefix.length() + option->name.length();
if (!option->flag.isNull())
{
nameLength += 3 + flagPrefix.length();
help << flagPrefix << option->flag << ", ";
}
help << optPrefix << option->name;
if (option->type == otOption)
{
QString arg = QString("%1%2").arg(
((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
nameLength += arg.length();
help << arg;
}
help << " " << QString(helpIndent - nameLength - 1, ' ');
help << option->doc << "\r\n";
}
}
help << "Options & Switches:\r\n";
QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
help << " ";
int nameLength = optPrefix.length() + option->name.length();
if (!option->flag.isNull())
{
nameLength += 3 + flagPrefix.length();
help << flagPrefix << option->flag << ", ";
}
help << optPrefix << option->name;
if (option->type == otOption)
{
QString arg = QString("%1%2").arg(
((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
nameLength += arg.length();
help << arg;
}
help << " " << QString(helpIndent - nameLength - 1, ' ');
help << option->doc << "\r\n";
}
}
return help.join("");
return help.join("");
}
QString Parser::compileUsage(QString progName, bool useFlags)
{
QStringList usage;
usage << "Usage: " << progName;
QStringList usage;
usage << "Usage: " << progName;
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
// options
QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
usage << " [";
if (!option->flag.isNull() && useFlags)
usage << flagPrefix << option->flag;
else
usage << optPrefix << option->name;
if (option->type == otOption)
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
usage << "]";
}
// options
QListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
usage << " [";
if (!option->flag.isNull() && useFlags)
usage << flagPrefix << option->flag;
else
usage << optPrefix << option->name;
if (option->type == otOption)
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
usage << "]";
}
// arguments
QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *param = it2.next();
usage << " " << (param->required ? "<" : "[");
usage << param->metavar;
usage << (param->required ? ">" : "]");
}
// arguments
QListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *param = it2.next();
usage << " " << (param->required ? "<" : "[");
usage << param->metavar;
usage << (param->required ? ">" : "]");
}
return usage.join("");
return usage.join("");
}
// parsing
QHash<QString, QVariant> Parser::parse(QStringList argv)
{
QHash<QString, QVariant> map;
QHash<QString, QVariant> map;
QStringListIterator it(argv);
QString programName = it.next();
QStringListIterator it(argv);
QString programName = it.next();
QString optionPrefix;
QString flagPrefix;
QListIterator<PositionalDef *> positionals(m_positionals);
QStringList expecting;
QString optionPrefix;
QString flagPrefix;
QListIterator<PositionalDef *> positionals(m_positionals);
QStringList expecting;
getPrefix(optionPrefix, flagPrefix);
getPrefix(optionPrefix, flagPrefix);
while (it.hasNext())
{
QString arg = it.next();
while (it.hasNext())
{
QString arg = it.next();
if (!expecting.isEmpty())
// we were expecting an argument
{
QString name = expecting.first();
if (!expecting.isEmpty())
// we were expecting an argument
{
QString name = expecting.first();
/*
if (map.contains(name))
throw ParsingError(
QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
if (map.contains(name))
throw ParsingError(
QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
*/
map[name] = QVariant(arg);
map[name] = QVariant(arg);
expecting.removeFirst();
continue;
}
expecting.removeFirst();
continue;
}
if (arg.startsWith(optionPrefix))
// we have an option
{
// qDebug("Found option %s", qPrintable(arg));
if (arg.startsWith(optionPrefix))
// we have an option
{
// qDebug("Found option %s", qPrintable(arg));
QString name = arg.mid(optionPrefix.length());
QString equals;
QString name = arg.mid(optionPrefix.length());
QString equals;
if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
name.contains("="))
{
int i = name.indexOf("=");
equals = name.mid(i + 1);
name = name.left(i);
}
if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
name.contains("="))
{
int i = name.indexOf("=");
equals = name.mid(i + 1);
name = name.left(i);
}
if (m_options.contains(name))
{
/*
if (map.contains(name))
throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(name, optionPrefix));
if (m_options.contains(name))
{
/*
if (map.contains(name))
throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(name, optionPrefix));
*/
OptionDef *option = m_options[name];
if (option->type == otSwitch)
map[name] = true;
else // if (option->type == otOption)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(name);
else if (!equals.isNull())
map[name] = equals;
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(name);
else
throw ParsingError(QString("Option %2%1 reqires an argument.")
.arg(name, optionPrefix));
}
OptionDef *option = m_options[name];
if (option->type == otSwitch)
map[name] = true;
else // if (option->type == otOption)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(name);
else if (!equals.isNull())
map[name] = equals;
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(name);
else
throw ParsingError(QString("Option %2%1 reqires an argument.")
.arg(name, optionPrefix));
}
continue;
}
continue;
}
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
}
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
}
if (arg.startsWith(flagPrefix))
// we have (a) flag(s)
{
// qDebug("Found flags %s", qPrintable(arg));
if (arg.startsWith(flagPrefix))
// we have (a) flag(s)
{
// qDebug("Found flags %s", qPrintable(arg));
QString flags = arg.mid(flagPrefix.length());
QString equals;
QString flags = arg.mid(flagPrefix.length());
QString equals;
if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
flags.contains("="))
{
int i = flags.indexOf("=");
equals = flags.mid(i + 1);
flags = flags.left(i);
}
if ((m_argStyle == ArgumentStyle::Equals ||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
flags.contains("="))
{
int i = flags.indexOf("=");
equals = flags.mid(i + 1);
flags = flags.left(i);
}
for (int i = 0; i < flags.length(); i++)
{
QChar flag = flags.at(i);
for (int i = 0; i < flags.length(); i++)
{
QChar flag = flags.at(i);
if (!m_flags.contains(flag))
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
if (!m_flags.contains(flag))
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
OptionDef *option = m_flags[flag];
OptionDef *option = m_flags[flag];
/*
if (map.contains(option->name))
throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(option->name, optionPrefix));
if (map.contains(option->name))
throw ParsingError(QString("Option %2%1 was given multiple times")
.arg(option->name, optionPrefix));
*/
if (option->type == otSwitch)
map[option->name] = true;
else // if (option->type == otOption)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(option->name);
else if (!equals.isNull())
if (i == flags.length() - 1)
map[option->name] = equals;
else
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
"%1 not last flag in %4%3")
.arg(option->name, flag, flags, flagPrefix));
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(option->name);
else
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
.arg(option->name, flag, flagPrefix));
}
}
if (option->type == otSwitch)
map[option->name] = true;
else // if (option->type == otOption)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(option->name);
else if (!equals.isNull())
if (i == flags.length() - 1)
map[option->name] = equals;
else
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
"%1 not last flag in %4%3")
.arg(option->name, flag, flags, flagPrefix));
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(option->name);
else
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
.arg(option->name, flag, flagPrefix));
}
}
continue;
}
continue;
}
// must be a positional argument
if (!positionals.hasNext())
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
// must be a positional argument
if (!positionals.hasNext())
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
PositionalDef *param = positionals.next();
PositionalDef *param = positionals.next();
map[param->name] = arg;
}
map[param->name] = arg;
}
// check if we're missing something
if (!expecting.isEmpty())
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
expecting.join(QString(", ") + optionPrefix), optionPrefix));
// check if we're missing something
if (!expecting.isEmpty())
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
expecting.join(QString(", ") + optionPrefix), optionPrefix));
while (positionals.hasNext())
{
PositionalDef *param = positionals.next();
if (param->required)
throw ParsingError(
QString("Missing required positional argument '%1'").arg(param->name));
else
map[param->name] = param->def;
}
while (positionals.hasNext())
{
PositionalDef *param = positionals.next();
if (param->required)
throw ParsingError(
QString("Missing required positional argument '%1'").arg(param->name));
else
map[param->name] = param->def;
}
// fill out gaps
QListIterator<OptionDef *> iter(m_optionList);
while (iter.hasNext())
{
OptionDef *option = iter.next();
if (!map.contains(option->name))
map[option->name] = option->def;
}
// fill out gaps
QListIterator<OptionDef *> iter(m_optionList);
while (iter.hasNext())
{
OptionDef *option = iter.next();
if (!map.contains(option->name))
map[option->name] = option->def;
}
return map;
return map;
}
// clear defs
void Parser::clear()
{
m_flags.clear();
m_params.clear();
m_options.clear();
m_flags.clear();
m_params.clear();
m_options.clear();
QMutableListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
it.remove();
delete option;
}
QMutableListIterator<OptionDef *> it(m_optionList);
while (it.hasNext())
{
OptionDef *option = it.next();
it.remove();
delete option;
}
QMutableListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *arg = it2.next();
it2.remove();
delete arg;
}
QMutableListIterator<PositionalDef *> it2(m_positionals);
while (it2.hasNext())
{
PositionalDef *arg = it2.next();
it2.remove();
delete arg;
}
}
// Destructor
Parser::~Parser()
{
clear();
clear();
}
// getPrefix
void Parser::getPrefix(QString &opt, QString &flag)
{
if (m_flagStyle == FlagStyle::Windows)
opt = flag = "/";
else if (m_flagStyle == FlagStyle::Unix)
opt = flag = "-";
// else if (m_flagStyle == FlagStyle::GNU)
else
{
opt = "--";
flag = "-";
}
if (m_flagStyle == FlagStyle::Windows)
opt = flag = "/";
else if (m_flagStyle == FlagStyle::Unix)
opt = flag = "-";
// else if (m_flagStyle == FlagStyle::GNU)
else
{
opt = "--";
flag = "-";
}
}
// ParsingError

View File

@ -51,13 +51,13 @@ namespace FlagStyle
{
enum Enum
{
GNU, /**< --option and -o (GNU Style) */
Unix, /**< -option and -o (Unix Style) */
Windows, /**< /option and /o (Windows Style) */
GNU, /**< --option and -o (GNU Style) */
Unix, /**< -option and -o (Unix Style) */
Windows, /**< /option and /o (Windows Style) */
#ifdef Q_OS_WIN32
Default = Windows
Default = Windows
#else
Default = GNU
Default = GNU
#endif
};
}
@ -69,13 +69,13 @@ namespace ArgumentStyle
{
enum Enum
{
Space, /**< --option=value */
Equals, /**< --option value */
SpaceAndEquals, /**< --option[= ]value */
Space, /**< --option=value */
Equals, /**< --option value */
SpaceAndEquals, /**< --option[= ]value */
#ifdef Q_OS_WIN32
Default = Equals
Default = Equals
#else
Default = SpaceAndEquals
Default = SpaceAndEquals
#endif
};
}
@ -86,7 +86,7 @@ enum Enum
class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error
{
public:
ParsingError(const QString &what);
ParsingError(const QString &what);
};
/**
@ -95,158 +95,158 @@ public:
class MULTIMC_LOGIC_EXPORT Parser
{
public:
/**
* @brief Parser constructor
* @param flagStyle the FlagStyle to use in this Parser
* @param argStyle the ArgumentStyle to use in this Parser
*/
Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
/**
* @brief Parser constructor
* @param flagStyle the FlagStyle to use in this Parser
* @param argStyle the ArgumentStyle to use in this Parser
*/
Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
/**
* @brief set the flag style
* @param style
*/
void setFlagStyle(FlagStyle::Enum style);
/**
* @brief set the flag style
* @param style
*/
void setFlagStyle(FlagStyle::Enum style);
/**
* @brief get the flag style
* @return
*/
FlagStyle::Enum flagStyle();
/**
* @brief get the flag style
* @return
*/
FlagStyle::Enum flagStyle();
/**
* @brief set the argument style
* @param style
*/
void setArgumentStyle(ArgumentStyle::Enum style);
/**
* @brief set the argument style
* @param style
*/
void setArgumentStyle(ArgumentStyle::Enum style);
/**
* @brief get the argument style
* @return
*/
ArgumentStyle::Enum argumentStyle();
/**
* @brief get the argument style
* @return
*/
ArgumentStyle::Enum argumentStyle();
/**
* @brief define a boolean switch
* @param name the parameter name
* @param def the default value
*/
void addSwitch(QString name, bool def = false);
/**
* @brief define a boolean switch
* @param name the parameter name
* @param def the default value
*/
void addSwitch(QString name, bool def = false);
/**
* @brief define an option that takes an additional argument
* @param name the parameter name
* @param def the default value
*/
void addOption(QString name, QVariant def = QVariant());
/**
* @brief define an option that takes an additional argument
* @param name the parameter name
* @param def the default value
*/
void addOption(QString name, QVariant def = QVariant());
/**
* @brief define a positional argument
* @param name the parameter name
* @param required wether this argument is required
* @param def the default value
*/
void addArgument(QString name, bool required = true, QVariant def = QVariant());
/**
* @brief define a positional argument
* @param name the parameter name
* @param required wether this argument is required
* @param def the default value
*/
void addArgument(QString name, bool required = true, QVariant def = QVariant());
/**
* @brief adds a flag to an existing parameter
* @param name the (existing) parameter name
* @param flag the flag character
* @see addSwitch addArgument addOption
* Note: any one parameter can only have one flag
*/
void addShortOpt(QString name, QChar flag);
/**
* @brief adds a flag to an existing parameter
* @param name the (existing) parameter name
* @param flag the flag character
* @see addSwitch addArgument addOption
* Note: any one parameter can only have one flag
*/
void addShortOpt(QString name, QChar flag);
/**
* @brief adds documentation to a Parameter
* @param name the parameter name
* @param metavar a string to be displayed as placeholder for the value
* @param doc a QString containing the documentation
* Note: on positional arguments, metavar replaces the name as displayed.
* on options , metavar replaces the value placeholder
*/
void addDocumentation(QString name, QString doc, QString metavar = QString());
/**
* @brief adds documentation to a Parameter
* @param name the parameter name
* @param metavar a string to be displayed as placeholder for the value
* @param doc a QString containing the documentation
* Note: on positional arguments, metavar replaces the name as displayed.
* on options , metavar replaces the value placeholder
*/
void addDocumentation(QString name, QString doc, QString metavar = QString());
/**
* @brief generate a help message
* @param progName the program name to use in the help message
* @param helpIndent how much the parameter documentation should be indented
* @param flagsInUsage whether we should use flags instead of options in the usage
* @return a help message
*/
QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
/**
* @brief generate a help message
* @param progName the program name to use in the help message
* @param helpIndent how much the parameter documentation should be indented
* @param flagsInUsage whether we should use flags instead of options in the usage
* @return a help message
*/
QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
/**
* @brief generate a short usage message
* @param progName the program name to use in the usage message
* @param useFlags whether we should use flags instead of options
* @return a usage message
*/
QString compileUsage(QString progName, bool useFlags = true);
/**
* @brief generate a short usage message
* @param progName the program name to use in the usage message
* @param useFlags whether we should use flags instead of options
* @return a usage message
*/
QString compileUsage(QString progName, bool useFlags = true);
/**
* @brief parse
* @param argv a QStringList containing the program ARGV
* @return a QHash mapping argument names to their values
*/
QHash<QString, QVariant> parse(QStringList argv);
/**
* @brief parse
* @param argv a QStringList containing the program ARGV
* @return a QHash mapping argument names to their values
*/
QHash<QString, QVariant> parse(QStringList argv);
/**
* @brief clear all definitions
*/
void clear();
/**
* @brief clear all definitions
*/
void clear();
~Parser();
~Parser();
private:
FlagStyle::Enum m_flagStyle;
ArgumentStyle::Enum m_argStyle;
FlagStyle::Enum m_flagStyle;
ArgumentStyle::Enum m_argStyle;
enum OptionType
{
otSwitch,
otOption
};
enum OptionType
{
otSwitch,
otOption
};
// Important: the common part MUST BE COMMON ON ALL THREE structs
struct CommonDef
{
QString name;
QString doc;
QString metavar;
QVariant def;
};
// Important: the common part MUST BE COMMON ON ALL THREE structs
struct CommonDef
{
QString name;
QString doc;
QString metavar;
QVariant def;
};
struct OptionDef
{
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// option
OptionType type;
QChar flag;
};
struct OptionDef
{
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// option
OptionType type;
QChar flag;
};
struct PositionalDef
{
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// positional
bool required;
};
struct PositionalDef
{
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// positional
bool required;
};
QHash<QString, OptionDef *> m_options;
QHash<QChar, OptionDef *> m_flags;
QHash<QString, CommonDef *> m_params;
QList<PositionalDef *> m_positionals;
QList<OptionDef *> m_optionList;
QHash<QString, OptionDef *> m_options;
QHash<QChar, OptionDef *> m_flags;
QHash<QString, CommonDef *> m_params;
QList<PositionalDef *> m_positionals;
QList<OptionDef *> m_optionList;
void getPrefix(QString &opt, QString &flag);
void getPrefix(QString &opt, QString &flag);
};
}

View File

@ -4,32 +4,32 @@ template <typename T>
class DefaultVariable
{
public:
DefaultVariable(const T & value)
{
defaultValue = value;
}
DefaultVariable<T> & operator =(const T & value)
{
currentValue = value;
is_default = currentValue == defaultValue;
is_explicit = true;
return *this;
}
operator const T &() const
{
return is_default ? defaultValue : currentValue;
}
bool isDefault() const
{
return is_default;
}
bool isExplicit() const
{
return is_explicit;
}
DefaultVariable(const T & value)
{
defaultValue = value;
}
DefaultVariable<T> & operator =(const T & value)
{
currentValue = value;
is_default = currentValue == defaultValue;
is_explicit = true;
return *this;
}
operator const T &() const
{
return is_default ? defaultValue : currentValue;
}
bool isDefault() const
{
return is_default;
}
bool isExplicit() const
{
return is_explicit;
}
private:
T currentValue;
T defaultValue;
bool is_default = true;
bool is_explicit = false;
T currentValue;
T defaultValue;
bool is_default = true;
bool is_explicit = false;
};

View File

@ -15,11 +15,11 @@
struct Env::Private
{
QNetworkAccessManager m_qnam;
shared_qobject_ptr<HttpMetaCache> m_metacache;
std::shared_ptr<IIconList> m_iconlist;
shared_qobject_ptr<Meta::Index> m_metadataIndex;
QString m_jarsPath;
QNetworkAccessManager m_qnam;
shared_qobject_ptr<HttpMetaCache> m_metacache;
std::shared_ptr<IIconList> m_iconlist;
shared_qobject_ptr<Meta::Index> m_metadataIndex;
QString m_jarsPath;
};
static Env * instance;
@ -30,152 +30,152 @@ static Env * instance;
Env::Env()
{
d = new Private();
d = new Private();
}
Env::~Env()
{
delete d;
delete d;
}
Env& Env::Env::getInstance()
{
if(!instance)
{
instance = new Env();
}
return *instance;
if(!instance)
{
instance = new Env();
}
return *instance;
}
void Env::dispose()
{
delete instance;
instance = nullptr;
delete instance;
instance = nullptr;
}
shared_qobject_ptr< HttpMetaCache > Env::metacache()
{
return d->m_metacache;
return d->m_metacache;
}
QNetworkAccessManager& Env::qnam() const
{
return d->m_qnam;
return d->m_qnam;
}
std::shared_ptr<IIconList> Env::icons()
{
return d->m_iconlist;
return d->m_iconlist;
}
void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
{
d->m_iconlist = iconlist;
d->m_iconlist = iconlist;
}
shared_qobject_ptr<Meta::Index> Env::metadataIndex()
{
if (!d->m_metadataIndex)
{
d->m_metadataIndex.reset(new Meta::Index());
}
return d->m_metadataIndex;
if (!d->m_metadataIndex)
{
d->m_metadataIndex.reset(new Meta::Index());
}
return d->m_metadataIndex;
}
void Env::initHttpMetaCache()
{
auto &m_metacache = d->m_metacache;
m_metacache.reset(new HttpMetaCache("metacache"));
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("general", QDir("cache").absolutePath());
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->Load();
auto &m_metacache = d->m_metacache;
m_metacache.reset(new HttpMetaCache("metacache"));
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("general", QDir("cache").absolutePath());
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->Load();
}
void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
{
// Set the application proxy settings.
if (proxyTypeStr == "SOCKS5")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
}
else if (proxyTypeStr == "HTTP")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
}
else if (proxyTypeStr == "None")
{
// If we have no proxy set, set no proxy and return.
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
}
else
{
// If we have "Default" selected, set Qt to use the system proxy settings.
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
// Set the application proxy settings.
if (proxyTypeStr == "SOCKS5")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
}
else if (proxyTypeStr == "HTTP")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
}
else if (proxyTypeStr == "None")
{
// If we have no proxy set, set no proxy and return.
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
}
else
{
// If we have "Default" selected, set Qt to use the system proxy settings.
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
qDebug() << "Detecting proxy settings...";
QNetworkProxy proxy = QNetworkProxy::applicationProxy();
d->m_qnam.setProxy(proxy);
QString proxyDesc;
if (proxy.type() == QNetworkProxy::NoProxy)
{
qDebug() << "Using no proxy is an option!";
return;
}
switch (proxy.type())
{
case QNetworkProxy::DefaultProxy:
proxyDesc = "Default proxy: ";
break;
case QNetworkProxy::Socks5Proxy:
proxyDesc = "Socks5 proxy: ";
break;
case QNetworkProxy::HttpProxy:
proxyDesc = "HTTP proxy: ";
break;
case QNetworkProxy::HttpCachingProxy:
proxyDesc = "HTTP caching: ";
break;
case QNetworkProxy::FtpCachingProxy:
proxyDesc = "FTP caching: ";
break;
default:
proxyDesc = "DERP proxy: ";
break;
}
proxyDesc += QString("%3@%1:%2 pass %4")
.arg(proxy.hostName())
.arg(proxy.port())
.arg(proxy.user())
.arg(proxy.password());
qDebug() << proxyDesc;
qDebug() << "Detecting proxy settings...";
QNetworkProxy proxy = QNetworkProxy::applicationProxy();
d->m_qnam.setProxy(proxy);
QString proxyDesc;
if (proxy.type() == QNetworkProxy::NoProxy)
{
qDebug() << "Using no proxy is an option!";
return;
}
switch (proxy.type())
{
case QNetworkProxy::DefaultProxy:
proxyDesc = "Default proxy: ";
break;
case QNetworkProxy::Socks5Proxy:
proxyDesc = "Socks5 proxy: ";
break;
case QNetworkProxy::HttpProxy:
proxyDesc = "HTTP proxy: ";
break;
case QNetworkProxy::HttpCachingProxy:
proxyDesc = "HTTP caching: ";
break;
case QNetworkProxy::FtpCachingProxy:
proxyDesc = "FTP caching: ";
break;
default:
proxyDesc = "DERP proxy: ";
break;
}
proxyDesc += QString("%3@%1:%2 pass %4")
.arg(proxy.hostName())
.arg(proxy.port())
.arg(proxy.user())
.arg(proxy.password());
qDebug() << proxyDesc;
}
QString Env::getJarsPath()
{
if(d->m_jarsPath.isEmpty())
{
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
}
return d->m_jarsPath;
if(d->m_jarsPath.isEmpty())
{
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
}
return d->m_jarsPath;
}
void Env::setJarsPath(const QString& path)
{
d->m_jarsPath = path;
d->m_jarsPath = path;
}

View File

@ -20,40 +20,40 @@ class Index;
}
#if defined(ENV)
#undef ENV
#undef ENV
#endif
#define ENV (Env::getInstance())
class MULTIMC_LOGIC_EXPORT Env
{
friend class MultiMC;
friend class MultiMC;
private:
struct Private;
Env();
~Env();
static void dispose();
struct Private;
Env();
~Env();
static void dispose();
public:
static Env& getInstance();
static Env& getInstance();
QNetworkAccessManager &qnam() const;
QNetworkAccessManager &qnam() const;
shared_qobject_ptr<HttpMetaCache> metacache();
shared_qobject_ptr<HttpMetaCache> metacache();
std::shared_ptr<IIconList> icons();
std::shared_ptr<IIconList> icons();
/// init the cache. FIXME: possible future hook point
void initHttpMetaCache();
/// init the cache. FIXME: possible future hook point
void initHttpMetaCache();
/// Updates the application proxy settings from the settings object.
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
/// Updates the application proxy settings from the settings object.
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
void registerIconList(std::shared_ptr<IIconList> iconlist);
void registerIconList(std::shared_ptr<IIconList> iconlist);
shared_qobject_ptr<Meta::Index> metadataIndex();
shared_qobject_ptr<Meta::Index> metadataIndex();
QString getJarsPath();
void setJarsPath(const QString & path);
QString getJarsPath();
void setJarsPath(const QString & path);
protected:
Private * d;
Private * d;
};

View File

@ -11,24 +11,24 @@
class MULTIMC_LOGIC_EXPORT Exception : public std::exception
{
public:
Exception(const QString &message) : std::exception(), m_message(message)
{
qCritical() << "Exception:" << message;
}
Exception(const Exception &other)
: std::exception(), m_message(other.cause())
{
}
virtual ~Exception() noexcept {}
const char *what() const noexcept
{
return m_message.toLatin1().constData();
}
QString cause() const
{
return m_message;
}
Exception(const QString &message) : std::exception(), m_message(message)
{
qCritical() << "Exception:" << message;
}
Exception(const Exception &other)
: std::exception(), m_message(other.cause())
{
}
virtual ~Exception() noexcept {}
const char *what() const noexcept
{
return m_message.toLatin1().constData();
}
QString cause() const
{
return m_message;
}
private:
QString m_message;
QString m_message;
};

View File

@ -12,262 +12,262 @@
#include <QTextStream>
#if defined Q_OS_WIN32
#include <windows.h>
#include <string>
#include <sys/utime.h>
#include <winnls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
#include <shlobj.h>
#include <windows.h>
#include <string>
#include <sys/utime.h>
#include <winnls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
#include <shlobj.h>
#else
#include <utime.h>
#include <utime.h>
#endif
namespace FS {
void ensureExists(const QDir &dir)
{
if (!QDir().mkpath(dir.absolutePath()))
{
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
dir.absolutePath() + ")");
}
if (!QDir().mkpath(dir.absolutePath()))
{
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
dir.absolutePath() + ")");
}
}
void write(const QString &filename, const QByteArray &data)
{
ensureExists(QFileInfo(filename).dir());
QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly))
{
throw FileSystemException("Couldn't open " + filename + " for writing: " +
file.errorString());
}
if (data.size() != file.write(data))
{
throw FileSystemException("Error writing data to " + filename + ": " +
file.errorString());
}
if (!file.commit())
{
throw FileSystemException("Error while committing data to " + filename + ": " +
file.errorString());
}
ensureExists(QFileInfo(filename).dir());
QSaveFile file(filename);
if (!file.open(QSaveFile::WriteOnly))
{
throw FileSystemException("Couldn't open " + filename + " for writing: " +
file.errorString());
}
if (data.size() != file.write(data))
{
throw FileSystemException("Error writing data to " + filename + ": " +
file.errorString());
}
if (!file.commit())
{
throw FileSystemException("Error while committing data to " + filename + ": " +
file.errorString());
}
}
QByteArray read(const QString &filename)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly))
{
throw FileSystemException("Unable to open " + filename + " for reading: " +
file.errorString());
}
const qint64 size = file.size();
QByteArray data(int(size), 0);
const qint64 ret = file.read(data.data(), size);
if (ret == -1 || ret != size)
{
throw FileSystemException("Error reading data from " + filename + ": " +
file.errorString());
}
return data;
QFile file(filename);
if (!file.open(QFile::ReadOnly))
{
throw FileSystemException("Unable to open " + filename + " for reading: " +
file.errorString());
}
const qint64 size = file.size();
QByteArray data(int(size), 0);
const qint64 ret = file.read(data.data(), size);
if (ret == -1 || ret != size)
{
throw FileSystemException("Error reading data from " + filename + ": " +
file.errorString());
}
return data;
}
bool updateTimestamp(const QString& filename)
{
#ifdef Q_OS_WIN32
std::wstring filename_utf_16 = filename.toStdWString();
return (_wutime64(filename_utf_16.c_str(), nullptr) == 0);
std::wstring filename_utf_16 = filename.toStdWString();
return (_wutime64(filename_utf_16.c_str(), nullptr) == 0);
#else
QByteArray filenameBA = QFile::encodeName(filename);
return (utime(filenameBA.data(), nullptr) == 0);
QByteArray filenameBA = QFile::encodeName(filename);
return (utime(filenameBA.data(), nullptr) == 0);
#endif
}
bool ensureFilePathExists(QString filenamepath)
{
QFileInfo a(filenamepath);
QDir dir;
QString ensuredPath = a.path();
bool success = dir.mkpath(ensuredPath);
return success;
QFileInfo a(filenamepath);
QDir dir;
QString ensuredPath = a.path();
bool success = dir.mkpath(ensuredPath);
return success;
}
bool ensureFolderPathExists(QString foldernamepath)
{
QFileInfo a(foldernamepath);
QDir dir;
QString ensuredPath = a.filePath();
bool success = dir.mkpath(ensuredPath);
return success;
QFileInfo a(foldernamepath);
QDir dir;
QString ensuredPath = a.filePath();
bool success = dir.mkpath(ensuredPath);
return success;
}
bool copy::operator()(const QString &offset)
{
//NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32
m_followSymlinks = true;
#endif
//NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32
m_followSymlinks = true;
#endif
auto src = PathCombine(m_src.absolutePath(), offset);
auto dst = PathCombine(m_dst.absolutePath(), offset);
auto src = PathCombine(m_src.absolutePath(), offset);
auto dst = PathCombine(m_dst.absolutePath(), offset);
QFileInfo currentSrc(src);
if (!currentSrc.exists())
return false;
QFileInfo currentSrc(src);
if (!currentSrc.exists())
return false;
if(!m_followSymlinks && currentSrc.isSymLink())
{
qDebug() << "creating symlink" << src << " - " << dst;
if (!ensureFilePathExists(dst))
{
qWarning() << "Cannot create path!";
return false;
}
return QFile::link(currentSrc.symLinkTarget(), dst);
}
else if(currentSrc.isFile())
{
qDebug() << "copying file" << src << " - " << dst;
if (!ensureFilePathExists(dst))
{
qWarning() << "Cannot create path!";
return false;
}
return QFile::copy(src, dst);
}
else if(currentSrc.isDir())
{
qDebug() << "recursing" << offset;
if (!ensureFolderPathExists(dst))
{
qWarning() << "Cannot create path!";
return false;
}
QDir currentDir(src);
for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
{
auto inner_offset = PathCombine(offset, f);
// ignore and skip stuff that matches the blacklist.
if(m_blacklist && m_blacklist->matches(inner_offset))
{
continue;
}
if(!operator()(inner_offset))
{
qWarning() << "Failed to copy" << inner_offset;
return false;
}
}
}
else
{
qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
return false;
}
return true;
if(!m_followSymlinks && currentSrc.isSymLink())
{
qDebug() << "creating symlink" << src << " - " << dst;
if (!ensureFilePathExists(dst))
{
qWarning() << "Cannot create path!";
return false;
}
return QFile::link(currentSrc.symLinkTarget(), dst);
}
else if(currentSrc.isFile())
{
qDebug() << "copying file" << src << " - " << dst;
if (!ensureFilePathExists(dst))
{
qWarning() << "Cannot create path!";
return false;
}
return QFile::copy(src, dst);
}
else if(currentSrc.isDir())
{
qDebug() << "recursing" << offset;
if (!ensureFolderPathExists(dst))
{
qWarning() << "Cannot create path!";
return false;
}
QDir currentDir(src);
for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
{
auto inner_offset = PathCombine(offset, f);
// ignore and skip stuff that matches the blacklist.
if(m_blacklist && m_blacklist->matches(inner_offset))
{
continue;
}
if(!operator()(inner_offset))
{
qWarning() << "Failed to copy" << inner_offset;
return false;
}
}
}
else
{
qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
return false;
}
return true;
}
bool deletePath(QString path)
{
bool OK = true;
QDir dir(path);
bool OK = true;
QDir dir(path);
if (!dir.exists())
{
return OK;
}
auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
QDir::AllDirs | QDir::Files,
QDir::DirsFirst);
if (!dir.exists())
{
return OK;
}
auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
QDir::AllDirs | QDir::Files,
QDir::DirsFirst);
for(auto & info: allEntries)
{
for(auto & info: allEntries)
{
#if defined Q_OS_WIN32
QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
auto wString = nativePath.toStdWString();
DWORD dwAttrs = GetFileAttributesW(wString.c_str());
// Windows: check for junctions, reparse points and other nasty things of that sort
if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
{
if (info.isFile())
{
OK &= QFile::remove(info.absoluteFilePath());
}
else if (info.isDir())
{
OK &= dir.rmdir(info.absoluteFilePath());
}
}
QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
auto wString = nativePath.toStdWString();
DWORD dwAttrs = GetFileAttributesW(wString.c_str());
// Windows: check for junctions, reparse points and other nasty things of that sort
if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
{
if (info.isFile())
{
OK &= QFile::remove(info.absoluteFilePath());
}
else if (info.isDir())
{
OK &= dir.rmdir(info.absoluteFilePath());
}
}
#else
// We do not trust Qt with reparse points, but do trust it with unix symlinks.
if(info.isSymLink())
{
OK &= QFile::remove(info.absoluteFilePath());
}
// We do not trust Qt with reparse points, but do trust it with unix symlinks.
if(info.isSymLink())
{
OK &= QFile::remove(info.absoluteFilePath());
}
#endif
else if (info.isDir())
{
OK &= deletePath(info.absoluteFilePath());
}
else if (info.isFile())
{
OK &= QFile::remove(info.absoluteFilePath());
}
else
{
OK = false;
qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
}
}
OK &= dir.rmdir(dir.absolutePath());
return OK;
else if (info.isDir())
{
OK &= deletePath(info.absoluteFilePath());
}
else if (info.isFile())
{
OK &= QFile::remove(info.absoluteFilePath());
}
else
{
OK = false;
qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
}
}
OK &= dir.rmdir(dir.absolutePath());
return OK;
}
QString PathCombine(const QString & path1, const QString & path2)
{
if(!path1.size())
return path2;
if(!path2.size())
return path1;
if(!path1.size())
return path2;
if(!path2.size())
return path1;
return QDir::cleanPath(path1 + QDir::separator() + path2);
}
QString PathCombine(const QString & path1, const QString & path2, const QString & path3)
{
return PathCombine(PathCombine(path1, path2), path3);
return PathCombine(PathCombine(path1, path2), path3);
}
QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
{
return PathCombine(PathCombine(path1, path2, path3), path4);
return PathCombine(PathCombine(path1, path2, path3), path4);
}
QString AbsolutePath(QString path)
{
return QFileInfo(path).absolutePath();
return QFileInfo(path).absolutePath();
}
QString ResolveExecutable(QString path)
{
if (path.isEmpty())
{
return QString();
}
if(!path.contains('/'))
{
path = QStandardPaths::findExecutable(path);
}
QFileInfo pathInfo(path);
if(!pathInfo.exists() || !pathInfo.isExecutable())
{
return QString();
}
return pathInfo.absoluteFilePath();
if (path.isEmpty())
{
return QString();
}
if(!path.contains('/'))
{
path = QStandardPaths::findExecutable(path);
}
QFileInfo pathInfo(path);
if(!pathInfo.exists() || !pathInfo.isExecutable())
{
return QString();
}
return pathInfo.absoluteFilePath();
}
/**
@ -278,66 +278,66 @@ QString ResolveExecutable(QString path)
*/
QString NormalizePath(QString path)
{
QDir a = QDir::currentPath();
QString currentAbsolute = a.absolutePath();
QDir a = QDir::currentPath();
QString currentAbsolute = a.absolutePath();
QDir b(path);
QString newAbsolute = b.absolutePath();
QDir b(path);
QString newAbsolute = b.absolutePath();
if (newAbsolute.startsWith(currentAbsolute))
{
return a.relativeFilePath(newAbsolute);
}
else
{
return newAbsolute;
}
if (newAbsolute.startsWith(currentAbsolute))
{
return a.relativeFilePath(newAbsolute);
}
else
{
return newAbsolute;
}
}
QString badFilenameChars = "\"\\/?<>:*|!";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{
for (int i = 0; i < string.length(); i++)
{
if (badFilenameChars.contains(string[i]))
{
string[i] = replaceWith;
}
}
return string;
for (int i = 0; i < string.length(); i++)
{
if (badFilenameChars.contains(string[i]))
{
string[i] = replaceWith;
}
}
return string;
}
QString DirNameFromString(QString string, QString inDir)
{
int num = 0;
QString baseName = RemoveInvalidFilenameChars(string, '-');
QString dirName;
do
{
if(num == 0)
{
dirName = baseName;
}
else
{
dirName = baseName + QString::number(num);;
}
int num = 0;
QString baseName = RemoveInvalidFilenameChars(string, '-');
QString dirName;
do
{
if(num == 0)
{
dirName = baseName;
}
else
{
dirName = baseName + QString::number(num);;
}
// If it's over 9000
if (num > 9000)
return "";
num++;
} while (QFileInfo(PathCombine(inDir, dirName)).exists());
return dirName;
// If it's over 9000
if (num > 9000)
return "";
num++;
} while (QFileInfo(PathCombine(inDir, dirName)).exists());
return dirName;
}
// Does the folder path contain any '!'? If yes, return true, otherwise false.
// (This is a problem for Java)
bool checkProblemticPathJava(QDir folder)
{
QString pathfoldername = folder.absolutePath();
return pathfoldername.contains("!", Qt::CaseInsensitive);
QString pathfoldername = folder.absolutePath();
return pathfoldername.contains("!", Qt::CaseInsensitive);
}
// Win32 crap
@ -347,106 +347,106 @@ bool called_coinit = false;
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
{
HRESULT hres;
HRESULT hres;
if (!called_coinit)
{
hres = CoInitialize(NULL);
called_coinit = true;
if (!called_coinit)
{
hres = CoInitialize(NULL);
called_coinit = true;
if (!SUCCEEDED(hres))
{
qWarning("Failed to initialize COM. Error 0x%08lX", hres);
return hres;
}
}
if (!SUCCEEDED(hres))
{
qWarning("Failed to initialize COM. Error 0x%08lX", hres);
return hres;
}
}
IShellLink *link;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
(LPVOID *)&link);
IShellLink *link;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
(LPVOID *)&link);
if (SUCCEEDED(hres))
{
IPersistFile *persistFile;
if (SUCCEEDED(hres))
{
IPersistFile *persistFile;
link->SetPath(targetPath);
link->SetArguments(args);
link->SetPath(targetPath);
link->SetArguments(args);
hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
if (SUCCEEDED(hres))
{
WCHAR wstr[MAX_PATH];
hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
if (SUCCEEDED(hres))
{
WCHAR wstr[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
hres = persistFile->Save(wstr, TRUE);
persistFile->Release();
}
link->Release();
}
return hres;
hres = persistFile->Save(wstr, TRUE);
persistFile->Release();
}
link->Release();
}
return hres;
}
#endif
QString getDesktopDir()
{
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
}
// Cross-platform Shortcut creation
bool createShortCut(QString location, QString dest, QStringList args, QString name,
QString icon)
QString icon)
{
#if defined Q_OS_LINUX
location = PathCombine(location, name + ".desktop");
location = PathCombine(location, name + ".desktop");
QFile f(location);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
QFile f(location);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
QString argstring;
if (!args.empty())
argstring = " '" + args.join("' '") + "'";
QString argstring;
if (!args.empty())
argstring = " '" + args.join("' '") + "'";
stream << "[Desktop Entry]"
<< "\n";
stream << "Type=Application"
<< "\n";
stream << "TryExec=" << dest.toLocal8Bit() << "\n";
stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n";
stream << "Icon=" << icon.toLocal8Bit() << "\n";
stream << "[Desktop Entry]"
<< "\n";
stream << "Type=Application"
<< "\n";
stream << "TryExec=" << dest.toLocal8Bit() << "\n";
stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n";
stream << "Icon=" << icon.toLocal8Bit() << "\n";
stream.flush();
f.close();
stream.flush();
f.close();
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
QFileDevice::ExeOther);
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
QFileDevice::ExeOther);
return true;
return true;
#elif defined Q_OS_WIN
// TODO: Fix
// QFile file(PathCombine(location, name + ".lnk"));
// WCHAR *file_w;
// WCHAR *dest_w;
// WCHAR *args_w;
// file.fileName().toWCharArray(file_w);
// dest.toWCharArray(dest_w);
// TODO: Fix
// QFile file(PathCombine(location, name + ".lnk"));
// WCHAR *file_w;
// WCHAR *dest_w;
// WCHAR *args_w;
// file.fileName().toWCharArray(file_w);
// dest.toWCharArray(dest_w);
// QString argStr;
// for (int i = 0; i < args.count(); i++)
// {
// argStr.append(args[i]);
// argStr.append(" ");
// }
// argStr.toWCharArray(args_w);
// QString argStr;
// for (int i = 0; i < args.count(); i++)
// {
// argStr.append(args[i]);
// argStr.append(" ");
// }
// argStr.toWCharArray(args_w);
// return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
return false;
// return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
return false;
#else
qWarning("Desktop Shortcuts not supported on your platform!");
return false;
qWarning("Desktop Shortcuts not supported on your platform!");
return false;
#endif
}
}

View File

@ -15,7 +15,7 @@ namespace FS
class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception
{
public:
FileSystemException(const QString &message) : Exception(message) {}
FileSystemException(const QString &message) : Exception(message) {}
};
/**
@ -48,34 +48,34 @@ MULTIMC_LOGIC_EXPORT bool ensureFolderPathExists(QString filenamepath);
class MULTIMC_LOGIC_EXPORT copy
{
public:
copy(const QString & src, const QString & dst)
{
m_src = src;
m_dst = dst;
}
copy & followSymlinks(const bool follow)
{
m_followSymlinks = follow;
return *this;
}
copy & blacklist(const IPathMatcher * filter)
{
m_blacklist = filter;
return *this;
}
bool operator()()
{
return operator()(QString());
}
copy(const QString & src, const QString & dst)
{
m_src = src;
m_dst = dst;
}
copy & followSymlinks(const bool follow)
{
m_followSymlinks = follow;
return *this;
}
copy & blacklist(const IPathMatcher * filter)
{
m_blacklist = filter;
return *this;
}
bool operator()()
{
return operator()(QString());
}
private:
bool operator()(const QString &offset);
bool operator()(const QString &offset);
private:
bool m_followSymlinks = true;
const IPathMatcher * m_blacklist = nullptr;
QDir m_src;
QDir m_dst;
bool m_followSymlinks = true;
const IPathMatcher * m_blacklist = nullptr;
QDir m_src;
QDir m_dst;
};
/**

View File

@ -7,155 +7,155 @@
class FileSystemTest : public QObject
{
Q_OBJECT
Q_OBJECT
const QString bothSlash = "/foo/";
const QString trailingSlash = "foo/";
const QString leadingSlash = "/foo";
const QString bothSlash = "/foo/";
const QString trailingSlash = "foo/";
const QString leadingSlash = "/foo";
private
slots:
void test_pathCombine()
{
QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
void test_pathCombine()
{
QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
}
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
}
void test_PathCombine1_data()
{
QTest::addColumn<QString>("result");
QTest::addColumn<QString>("path1");
QTest::addColumn<QString>("path2");
void test_PathCombine1_data()
{
QTest::addColumn<QString>("result");
QTest::addColumn<QString>("path1");
QTest::addColumn<QString>("path2");
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
#if defined(Q_OS_WIN)
QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
#endif
}
}
void test_PathCombine1()
{
QFETCH(QString, result);
QFETCH(QString, path1);
QFETCH(QString, path2);
void test_PathCombine1()
{
QFETCH(QString, result);
QFETCH(QString, path1);
QFETCH(QString, path2);
QCOMPARE(FS::PathCombine(path1, path2), result);
}
QCOMPARE(FS::PathCombine(path1, path2), result);
}
void test_PathCombine2_data()
{
QTest::addColumn<QString>("result");
QTest::addColumn<QString>("path1");
QTest::addColumn<QString>("path2");
QTest::addColumn<QString>("path3");
void test_PathCombine2_data()
{
QTest::addColumn<QString>("result");
QTest::addColumn<QString>("path1");
QTest::addColumn<QString>("path2");
QTest::addColumn<QString>("path3");
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
#if defined(Q_OS_WIN)
QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
#endif
}
}
void test_PathCombine2()
{
QFETCH(QString, result);
QFETCH(QString, path1);
QFETCH(QString, path2);
QFETCH(QString, path3);
void test_PathCombine2()
{
QFETCH(QString, result);
QFETCH(QString, path1);
QFETCH(QString, path2);
QFETCH(QString, path3);
QCOMPARE(FS::PathCombine(path1, path2, path3), result);
}
QCOMPARE(FS::PathCombine(path1, path2, path3), result);
}
void test_copy()
{
QString folder = QFINDTESTDATA("data/test_folder");
auto f = [&folder]()
{
QTemporaryDir tempDir;
tempDir.setAutoRemove(true);
qDebug() << "From:" << folder << "To:" << tempDir.path();
void test_copy()
{
QString folder = QFINDTESTDATA("data/test_folder");
auto f = [&folder]()
{
QTemporaryDir tempDir;
tempDir.setAutoRemove(true);
qDebug() << "From:" << folder << "To:" << tempDir.path();
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
qDebug() << tempDir.path();
qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path());
c();
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
qDebug() << tempDir.path();
qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path());
c();
for(auto entry: target_dir.entryList())
{
qDebug() << entry;
}
QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
QVERIFY(target_dir.entryList().contains("assets"));
};
for(auto entry: target_dir.entryList())
{
qDebug() << entry;
}
QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
QVERIFY(target_dir.entryList().contains("assets"));
};
// first try variant without trailing /
QVERIFY(!folder.endsWith('/'));
f();
// first try variant without trailing /
QVERIFY(!folder.endsWith('/'));
f();
// then variant with trailing /
folder.append('/');
QVERIFY(folder.endsWith('/'));
f();
}
// then variant with trailing /
folder.append('/');
QVERIFY(folder.endsWith('/'));
f();
}
void test_getDesktop()
{
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
}
void test_getDesktop()
{
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
}
// this is only valid on linux
// FIXME: implement on windows, OSX, then test.
#if defined(Q_OS_LINUX)
void test_createShortcut_data()
{
QTest::addColumn<QString>("location");
QTest::addColumn<QString>("dest");
QTest::addColumn<QStringList>("args");
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("iconLocation");
QTest::addColumn<QByteArray>("result");
void test_createShortcut_data()
{
QTest::addColumn<QString>("location");
QTest::addColumn<QString>("dest");
QTest::addColumn<QStringList>("args");
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("iconLocation");
QTest::addColumn<QByteArray>("result");
QTest::newRow("unix") << QDir::currentPath()
<< "asdfDest"
<< (QStringList() << "arg1" << "arg2")
<< "asdf"
<< QString()
#if defined(Q_OS_LINUX)
<< MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
#elif defined(Q_OS_WIN)
<< QByteArray()
#endif
;
}
QTest::newRow("unix") << QDir::currentPath()
<< "asdfDest"
<< (QStringList() << "arg1" << "arg2")
<< "asdf"
<< QString()
#if defined(Q_OS_LINUX)
<< MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
#elif defined(Q_OS_WIN)
<< QByteArray()
#endif
;
}
void test_createShortcut()
{
QFETCH(QString, location);
QFETCH(QString, dest);
QFETCH(QStringList, args);
QFETCH(QString, name);
QFETCH(QString, iconLocation);
QFETCH(QByteArray, result);
void test_createShortcut()
{
QFETCH(QString, location);
QFETCH(QString, dest);
QFETCH(QStringList, args);
QFETCH(QString, name);
QFETCH(QString, iconLocation);
QFETCH(QByteArray, result);
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
//QDir().remove(location);
}
//QDir().remove(location);
}
#endif
};

View File

@ -6,26 +6,26 @@ ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
ContainsFilter::~ContainsFilter(){}
bool ContainsFilter::accepts(const QString& value)
{
return value.contains(pattern);
return value.contains(pattern);
}
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
ExactFilter::~ExactFilter(){}
bool ExactFilter::accepts(const QString& value)
{
return value.contains(pattern);
return value.contains(pattern);
}
RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
:invert(invert)
:invert(invert)
{
pattern.setPattern(regexp);
pattern.optimize();
pattern.setPattern(regexp);
pattern.optimize();
}
RegexpFilter::~RegexpFilter(){}
bool RegexpFilter::accepts(const QString& value)
{
auto match = pattern.match(value);
bool matched = match.hasMatch();
return invert ? (!matched) : (matched);
auto match = pattern.match(value);
bool matched = match.hasMatch();
return invert ? (!matched) : (matched);
}

View File

@ -8,37 +8,37 @@
class MULTIMC_LOGIC_EXPORT Filter
{
public:
virtual ~Filter();
virtual bool accepts(const QString & value) = 0;
virtual ~Filter();
virtual bool accepts(const QString & value) = 0;
};
class MULTIMC_LOGIC_EXPORT ContainsFilter: public Filter
{
public:
ContainsFilter(const QString &pattern);
virtual ~ContainsFilter();
bool accepts(const QString & value) override;
ContainsFilter(const QString &pattern);
virtual ~ContainsFilter();
bool accepts(const QString & value) override;
private:
QString pattern;
QString pattern;
};
class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter
{
public:
ExactFilter(const QString &pattern);
virtual ~ExactFilter();
bool accepts(const QString & value) override;
ExactFilter(const QString &pattern);
virtual ~ExactFilter();
bool accepts(const QString & value) override;
private:
QString pattern;
QString pattern;
};
class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter
{
public:
RegexpFilter(const QString &regexp, bool invert);
virtual ~RegexpFilter();
bool accepts(const QString & value) override;
RegexpFilter(const QString &regexp, bool invert);
virtual ~RegexpFilter();
bool accepts(const QString & value) override;
private:
QRegularExpression pattern;
bool invert = false;
QRegularExpression pattern;
bool invert = false;
};

View File

@ -18,322 +18,322 @@ const static int GROUP_FILE_FORMAT_VERSION = 1;
struct WatchLock
{
WatchLock(QFileSystemWatcher * watcher, const QString& instDir)
: m_watcher(watcher), m_instDir(instDir)
{
m_watcher->removePath(m_instDir);
}
~WatchLock()
{
m_watcher->addPath(m_instDir);
}
QFileSystemWatcher * m_watcher;
QString m_instDir;
WatchLock(QFileSystemWatcher * watcher, const QString& instDir)
: m_watcher(watcher), m_instDir(instDir)
{
m_watcher->removePath(m_instDir);
}
~WatchLock()
{
m_watcher->addPath(m_instDir);
}
QFileSystemWatcher * m_watcher;
QString m_instDir;
};
FolderInstanceProvider::FolderInstanceProvider(SettingsObjectPtr settings, const QString& instDir)
: BaseInstanceProvider(settings)
: BaseInstanceProvider(settings)
{
// Create aand normalize path
if (!QDir::current().exists(instDir))
{
QDir::current().mkpath(instDir);
}
// NOTE: canonicalPath requires the path to exist. Do not move this above the creation block!
m_instDir = QDir(instDir).canonicalPath();
m_watcher = new QFileSystemWatcher(this);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &FolderInstanceProvider::instanceDirContentsChanged);
m_watcher->addPath(m_instDir);
// Create aand normalize path
if (!QDir::current().exists(instDir))
{
QDir::current().mkpath(instDir);
}
// NOTE: canonicalPath requires the path to exist. Do not move this above the creation block!
m_instDir = QDir(instDir).canonicalPath();
m_watcher = new QFileSystemWatcher(this);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &FolderInstanceProvider::instanceDirContentsChanged);
m_watcher->addPath(m_instDir);
}
QList< InstanceId > FolderInstanceProvider::discoverInstances()
{
QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
while (iter.hasNext())
{
QString subDir = iter.next();
QFileInfo dirInfo(subDir);
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
continue;
// if it is a symlink, ignore it if it goes to the instance folder
if(dirInfo.isSymLink())
{
QFileInfo targetInfo(dirInfo.symLinkTarget());
QFileInfo instDirInfo(m_instDir);
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
{
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
continue;
}
}
auto id = dirInfo.fileName();
out.append(id);
qDebug() << "Found instance ID" << id;
}
return out;
QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
while (iter.hasNext())
{
QString subDir = iter.next();
QFileInfo dirInfo(subDir);
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
continue;
// if it is a symlink, ignore it if it goes to the instance folder
if(dirInfo.isSymLink())
{
QFileInfo targetInfo(dirInfo.symLinkTarget());
QFileInfo instDirInfo(m_instDir);
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
{
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
continue;
}
}
auto id = dirInfo.fileName();
out.append(id);
qDebug() << "Found instance ID" << id;
}
return out;
}
InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id)
{
if(!m_groupsLoaded)
{
loadGroupList();
}
if(!m_groupsLoaded)
{
loadGroupList();
}
auto instanceRoot = FS::PathCombine(m_instDir, id);
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
InstancePtr inst;
auto instanceRoot = FS::PathCombine(m_instDir, id);
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
InstancePtr inst;
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->registerSetting("InstanceType", "Legacy");
QString inst_type = instanceSettings->get("InstanceType").toString();
QString inst_type = instanceSettings->get("InstanceType").toString();
if (inst_type == "OneSix" || inst_type == "Nostalgia")
{
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else if (inst_type == "Legacy")
{
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else
{
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
}
inst->init();
inst->setProvider(this);
auto iter = groupMap.find(id);
if (iter != groupMap.end())
{
inst->setGroupInitial((*iter));
}
connect(inst.get(), &BaseInstance::groupChanged, this, &FolderInstanceProvider::groupChanged);
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
return inst;
if (inst_type == "OneSix" || inst_type == "Nostalgia")
{
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else if (inst_type == "Legacy")
{
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
}
else
{
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
}
inst->init();
inst->setProvider(this);
auto iter = groupMap.find(id);
if (iter != groupMap.end())
{
inst->setGroupInitial((*iter));
}
connect(inst.get(), &BaseInstance::groupChanged, this, &FolderInstanceProvider::groupChanged);
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
return inst;
}
void FolderInstanceProvider::saveGroupList()
{
WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++)
{
QString id = iter.key();
QString group = iter.value();
if (group.isEmpty())
continue;
WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++)
{
QString id = iter.key();
QString group = iter.value();
if (group.isEmpty())
continue;
if (!reverseGroupMap.count(group))
{
QSet<QString> set;
set.insert(id);
reverseGroupMap[group] = set;
}
else
{
QSet<QString> &set = reverseGroupMap[group];
set.insert(id);
}
}
QJsonObject toplevel;
toplevel.insert("formatVersion", QJsonValue(QString("1")));
QJsonObject groupsArr;
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
{
auto list = iter.value();
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(QString("false")));
for (auto item : list)
{
instanceArr.append(QJsonValue(item));
}
groupObj.insert("instances", instanceArr);
groupsArr.insert(name, groupObj);
}
toplevel.insert("groups", groupsArr);
QJsonDocument doc(toplevel);
try
{
FS::write(groupFileName, doc.toJson());
}
catch (const FS::FileSystemException &e)
{
qCritical() << "Failed to write instance group file :" << e.cause();
}
if (!reverseGroupMap.count(group))
{
QSet<QString> set;
set.insert(id);
reverseGroupMap[group] = set;
}
else
{
QSet<QString> &set = reverseGroupMap[group];
set.insert(id);
}
}
QJsonObject toplevel;
toplevel.insert("formatVersion", QJsonValue(QString("1")));
QJsonObject groupsArr;
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
{
auto list = iter.value();
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(QString("false")));
for (auto item : list)
{
instanceArr.append(QJsonValue(item));
}
groupObj.insert("instances", instanceArr);
groupsArr.insert(name, groupObj);
}
toplevel.insert("groups", groupsArr);
QJsonDocument doc(toplevel);
try
{
FS::write(groupFileName, doc.toJson());
}
catch (const FS::FileSystemException &e)
{
qCritical() << "Failed to write instance group file :" << e.cause();
}
}
void FolderInstanceProvider::loadGroupList()
{
QSet<QString> groupSet;
QSet<QString> groupSet;
QString groupFileName = m_instDir + "/instgroups.json";
QString groupFileName = m_instDir + "/instgroups.json";
// if there's no group file, fail
if (!QFileInfo(groupFileName).exists())
return;
// if there's no group file, fail
if (!QFileInfo(groupFileName).exists())
return;
QByteArray jsonData;
try
{
jsonData = FS::read(groupFileName);
}
catch (const FS::FileSystemException &e)
{
qCritical() << "Failed to read instance group file :" << e.cause();
return;
}
QByteArray jsonData;
try
{
jsonData = FS::read(groupFileName);
}
catch (const FS::FileSystemException &e)
{
qCritical() << "Failed to read instance group file :" << e.cause();
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
// if the json was bad, fail
if (error.error != QJsonParseError::NoError)
{
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
.arg(error.errorString(), QString::number(error.offset))
.toUtf8();
return;
}
// if the json was bad, fail
if (error.error != QJsonParseError::NoError)
{
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
.arg(error.errorString(), QString::number(error.offset))
.toUtf8();
return;
}
// if the root of the json wasn't an object, fail
if (!jsonDoc.isObject())
{
qWarning() << "Invalid group file. Root entry should be an object.";
return;
}
// if the root of the json wasn't an object, fail
if (!jsonDoc.isObject())
{
qWarning() << "Invalid group file. Root entry should be an object.";
return;
}
QJsonObject rootObj = jsonDoc.object();
QJsonObject rootObj = jsonDoc.object();
// Make sure the format version matches, otherwise fail.
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
return;
// Make sure the format version matches, otherwise fail.
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
return;
// Get the groups. if it's not an object, fail
if (!rootObj.value("groups").isObject())
{
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
return;
}
// Get the groups. if it's not an object, fail
if (!rootObj.value("groups").isObject())
{
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
return;
}
groupMap.clear();
groupMap.clear();
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
{
QString groupName = iter.key();
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
{
QString groupName = iter.key();
// If not an object, complain and skip to the next one.
if (!iter.value().isObject())
{
qWarning() << QString("Group '%1' in the group list should "
"be an object.")
.arg(groupName)
.toUtf8();
continue;
}
// If not an object, complain and skip to the next one.
if (!iter.value().isObject())
{
qWarning() << QString("Group '%1' in the group list should "
"be an object.")
.arg(groupName)
.toUtf8();
continue;
}
QJsonObject groupObj = iter.value().toObject();
if (!groupObj.value("instances").isArray())
{
qWarning() << QString("Group '%1' in the group list is invalid. "
"It should contain an array "
"called 'instances'.")
.arg(groupName)
.toUtf8();
continue;
}
QJsonObject groupObj = iter.value().toObject();
if (!groupObj.value("instances").isArray())
{
qWarning() << QString("Group '%1' in the group list is invalid. "
"It should contain an array "
"called 'instances'.")
.arg(groupName)
.toUtf8();
continue;
}
// keep a list/set of groups for choosing
groupSet.insert(groupName);
// keep a list/set of groups for choosing
groupSet.insert(groupName);
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end();
iter2++)
{
groupMap[(*iter2).toString()] = groupName;
}
}
m_groupsLoaded = true;
emit groupsChanged(groupSet);
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end();
iter2++)
{
groupMap[(*iter2).toString()] = groupName;
}
}
m_groupsLoaded = true;
emit groupsChanged(groupSet);
}
void FolderInstanceProvider::groupChanged()
{
// save the groups. save all of them.
auto instance = (BaseInstance *) QObject::sender();
auto id = instance->id();
groupMap[id] = instance->group();
emit groupsChanged({instance->group()});
saveGroupList();
// save the groups. save all of them.
auto instance = (BaseInstance *) QObject::sender();
auto id = instance->id();
groupMap[id] = instance->group();
emit groupsChanged({instance->group()});
saveGroupList();
}
void FolderInstanceProvider::instanceDirContentsChanged(const QString& path)
{
Q_UNUSED(path);
emit instancesChanged();
Q_UNUSED(path);
emit instancesChanged();
}
void FolderInstanceProvider::on_InstFolderChanged(const Setting &setting, QVariant value)
{
QString newInstDir = QDir(value.toString()).canonicalPath();
if(newInstDir != m_instDir)
{
if(m_groupsLoaded)
{
saveGroupList();
}
m_instDir = newInstDir;
m_groupsLoaded = false;
emit instancesChanged();
}
QString newInstDir = QDir(value.toString()).canonicalPath();
if(newInstDir != m_instDir)
{
if(m_groupsLoaded)
{
saveGroupList();
}
m_instDir = newInstDir;
m_groupsLoaded = false;
emit instancesChanged();
}
}
template <typename T>
static void clamp(T& current, T min, T max)
{
if (current < min)
{
current = min;
}
else if(current > max)
{
current = max;
}
if (current < min)
{
current = min;
}
else if(current > max)
{
current = max;
}
}
// List of numbers from min to max. Next is exponent times bigger than previous.
class ExponentialSeries
{
public:
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
{
m_current = m_min = min;
m_max = max;
m_exponent = exponent;
}
void reset()
{
m_current = m_min;
}
unsigned operator()()
{
unsigned retval = m_current;
m_current *= m_exponent;
clamp(m_current, m_min, m_max);
return retval;
}
unsigned m_current;
unsigned m_min;
unsigned m_max;
unsigned m_exponent;
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
{
m_current = m_min = min;
m_max = max;
m_exponent = exponent;
}
void reset()
{
m_current = m_min;
}
unsigned operator()()
{
unsigned retval = m_current;
m_current *= m_exponent;
clamp(m_current, m_min, m_max);
return retval;
}
unsigned m_current;
unsigned m_min;
unsigned m_max;
unsigned m_exponent;
};
/*
@ -344,121 +344,121 @@ public:
class FolderInstanceStaging : public Task
{
Q_OBJECT
const unsigned minBackoff = 1;
const unsigned maxBackoff = 16;
const unsigned minBackoff = 1;
const unsigned maxBackoff = 16;
public:
FolderInstanceStaging (
FolderInstanceProvider * parent,
Task * child,
const QString & stagingPath,
const QString& instanceName,
const QString& groupName )
: backoff(minBackoff, maxBackoff)
{
m_parent = parent;
m_child.reset(child);
connect(child, &Task::succeeded, this, &FolderInstanceStaging::childSucceded);
connect(child, &Task::failed, this, &FolderInstanceStaging::childFailed);
connect(child, &Task::status, this, &FolderInstanceStaging::setStatus);
connect(child, &Task::progress, this, &FolderInstanceStaging::setProgress);
m_instanceName = instanceName;
m_groupName = groupName;
m_stagingPath = stagingPath;
m_backoffTimer.setSingleShot(true);
connect(&m_backoffTimer, &QTimer::timeout, this, &FolderInstanceStaging::childSucceded);
}
FolderInstanceStaging (
FolderInstanceProvider * parent,
Task * child,
const QString & stagingPath,
const QString& instanceName,
const QString& groupName )
: backoff(minBackoff, maxBackoff)
{
m_parent = parent;
m_child.reset(child);
connect(child, &Task::succeeded, this, &FolderInstanceStaging::childSucceded);
connect(child, &Task::failed, this, &FolderInstanceStaging::childFailed);
connect(child, &Task::status, this, &FolderInstanceStaging::setStatus);
connect(child, &Task::progress, this, &FolderInstanceStaging::setProgress);
m_instanceName = instanceName;
m_groupName = groupName;
m_stagingPath = stagingPath;
m_backoffTimer.setSingleShot(true);
connect(&m_backoffTimer, &QTimer::timeout, this, &FolderInstanceStaging::childSucceded);
}
virtual ~FolderInstanceStaging() {};
virtual ~FolderInstanceStaging() {};
protected:
virtual void executeTask() override
{
m_child->start();
}
QStringList warnings() const override
{
return m_child->warnings();
}
virtual void executeTask() override
{
m_child->start();
}
QStringList warnings() const override
{
return m_child->warnings();
}
private slots:
void childSucceded()
{
unsigned sleepTime = backoff();
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
{
emitSucceeded();
return;
}
// we actually failed, retry?
if(sleepTime == maxBackoff)
{
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return;
}
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
m_backoffTimer.start(sleepTime * 500);
}
void childFailed(const QString & reason)
{
m_parent->destroyStagingPath(m_stagingPath);
emitFailed(reason);
}
void childSucceded()
{
unsigned sleepTime = backoff();
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
{
emitSucceeded();
return;
}
// we actually failed, retry?
if(sleepTime == maxBackoff)
{
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return;
}
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
m_backoffTimer.start(sleepTime * 500);
}
void childFailed(const QString & reason)
{
m_parent->destroyStagingPath(m_stagingPath);
emitFailed(reason);
}
private:
ExponentialSeries backoff;
QString m_stagingPath;
FolderInstanceProvider * m_parent;
unique_qobject_ptr<Task> m_child;
QString m_instanceName;
QString m_groupName;
QTimer m_backoffTimer;
ExponentialSeries backoff;
QString m_stagingPath;
FolderInstanceProvider * m_parent;
unique_qobject_ptr<Task> m_child;
QString m_instanceName;
QString m_groupName;
QTimer m_backoffTimer;
};
#include "InstanceTask.h"
Task * FolderInstanceProvider::wrapInstanceTask(InstanceTask * task)
{
auto stagingPath = getStagedInstancePath();
task->setStagingPath(stagingPath);
task->setParentSettings(m_globalSettings);
return new FolderInstanceStaging(this, task, stagingPath, task->name(), task->group());
auto stagingPath = getStagedInstancePath();
task->setStagingPath(stagingPath);
task->setParentSettings(m_globalSettings);
return new FolderInstanceStaging(this, task, stagingPath, task->name(), task->group());
}
QString FolderInstanceProvider::getStagedInstancePath()
{
QString key = QUuid::createUuid().toString();
QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
QDir rootPath(m_instDir);
auto path = FS::PathCombine(m_instDir, relPath);
if(!rootPath.mkpath(relPath))
{
return QString();
}
return path;
QString key = QUuid::createUuid().toString();
QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
QDir rootPath(m_instDir);
auto path = FS::PathCombine(m_instDir, relPath);
if(!rootPath.mkpath(relPath))
{
return QString();
}
return path;
}
bool FolderInstanceProvider::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName)
{
QDir dir;
QString instID = FS::DirNameFromString(instanceName, m_instDir);
{
WatchLock lock(m_watcher, m_instDir);
QString destination = FS::PathCombine(m_instDir, instID);
if(!dir.rename(path, destination))
{
qWarning() << "Failed to move" << path << "to" << destination;
return false;
}
groupMap[instID] = groupName;
emit groupsChanged({groupName});
emit instancesChanged();
}
saveGroupList();
return true;
QDir dir;
QString instID = FS::DirNameFromString(instanceName, m_instDir);
{
WatchLock lock(m_watcher, m_instDir);
QString destination = FS::PathCombine(m_instDir, instID);
if(!dir.rename(path, destination))
{
qWarning() << "Failed to move" << path << "to" << destination;
return false;
}
groupMap[instID] = groupName;
emit groupsChanged({groupName});
emit instancesChanged();
}
saveGroupList();
return true;
}
bool FolderInstanceProvider::destroyStagingPath(const QString& keyPath)
{
return FS::deletePath(keyPath);
return FS::deletePath(keyPath);
}
#include "FolderInstanceProvider.moc"

View File

@ -8,68 +8,68 @@ class InstanceTask;
class MULTIMC_LOGIC_EXPORT FolderInstanceProvider : public BaseInstanceProvider
{
Q_OBJECT
Q_OBJECT
public:
FolderInstanceProvider(SettingsObjectPtr settings, const QString & instDir);
FolderInstanceProvider(SettingsObjectPtr settings, const QString & instDir);
public:
/// used by InstanceList to @return a list of plausible IDs to probe for
QList<InstanceId> discoverInstances() override;
/// used by InstanceList to @return a list of plausible IDs to probe for
QList<InstanceId> discoverInstances() override;
/// used by InstanceList to (re)load an instance with the given @id.
InstancePtr loadInstance(const InstanceId& id) override;
/// used by InstanceList to (re)load an instance with the given @id.
InstancePtr loadInstance(const InstanceId& id) override;
/*
// create instance in this provider
Task * creationTask(BaseVersionPtr version, const QString &instName, const QString &instGroup, const QString &instIcon);
/*
// create instance in this provider
Task * creationTask(BaseVersionPtr version, const QString &instName, const QString &instGroup, const QString &instIcon);
// copy instance to this provider
Task * copyTask(const InstancePtr &oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves);
// copy instance to this provider
Task * copyTask(const InstancePtr &oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves);
// import zipped instance into this provider
Task * zipImportTask(const QUrl sourceUrl, const QString &instName, const QString &instGroup, const QString &instIcon);
// import zipped instance into this provider
Task * zipImportTask(const QUrl sourceUrl, const QString &instName, const QString &instGroup, const QString &instIcon);
//create FtbInstance
Task * ftbCreationTask(FtbPackDownloader *downloader, const QString &instName, const QString &instGroup, const QString &instIcon);
//create FtbInstance
Task * ftbCreationTask(FtbPackDownloader *downloader, const QString &instName, const QString &instGroup, const QString &instIcon);
// migrate an instance to the current format
Task * legacyUpgradeTask(const InstancePtr& oldInstance);
// migrate an instance to the current format
Task * legacyUpgradeTask(const InstancePtr& oldInstance);
*/
// Wrap an instance creation task in some more task machinery and make it ready to be used
Task * wrapInstanceTask(InstanceTask * task);
// Wrap an instance creation task in some more task machinery and make it ready to be used
Task * wrapInstanceTask(InstanceTask * task);
/**
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
* Used by instance manipulation tasks.
*/
QString getStagedInstancePath() override;
/**
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
* Used by instance manipulation tasks.
*/
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName) override;
/**
* Destroy a previously created staging area given by @keyPath - used when creation fails.
* Used by instance manipulation tasks.
*/
bool destroyStagingPath(const QString & keyPath) override;
/**
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
* Used by instance manipulation tasks.
*/
QString getStagedInstancePath() override;
/**
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
* Used by instance manipulation tasks.
*/
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName) override;
/**
* Destroy a previously created staging area given by @keyPath - used when creation fails.
* Used by instance manipulation tasks.
*/
bool destroyStagingPath(const QString & keyPath) override;
public slots:
void on_InstFolderChanged(const Setting &setting, QVariant value);
void on_InstFolderChanged(const Setting &setting, QVariant value);
private slots:
void instanceDirContentsChanged(const QString &path);
void groupChanged();
void instanceDirContentsChanged(const QString &path);
void groupChanged();
private: /* methods */
void loadGroupList() override;
void saveGroupList() override;
void loadGroupList() override;
void saveGroupList() override;
private: /* data */
QString m_instDir;
QFileSystemWatcher * m_watcher;
QMap<QString, QString> groupMap;
bool m_groupsLoaded = false;
QString m_instDir;
QFileSystemWatcher * m_watcher;
QMap<QString, QString> groupMap;
bool m_groupsLoaded = false;
};

View File

@ -4,112 +4,112 @@
bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
{
if (compressedBytes.size() == 0)
{
uncompressedBytes = compressedBytes;
return true;
}
if (compressedBytes.size() == 0)
{
uncompressedBytes = compressedBytes;
return true;
}
unsigned uncompLength = compressedBytes.size();
uncompressedBytes.clear();
uncompressedBytes.resize(uncompLength);
unsigned uncompLength = compressedBytes.size();
uncompressedBytes.clear();
uncompressedBytes.resize(uncompLength);
z_stream strm;
memset(&strm, 0, sizeof(strm));
strm.next_in = (Bytef *)compressedBytes.data();
strm.avail_in = compressedBytes.size();
z_stream strm;
memset(&strm, 0, sizeof(strm));
strm.next_in = (Bytef *)compressedBytes.data();
strm.avail_in = compressedBytes.size();
bool done = false;
bool done = false;
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
{
return false;
}
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
{
return false;
}
int err = Z_OK;
int err = Z_OK;
while (!done)
{
// If our output buffer is too small
if (strm.total_out >= uncompLength)
{
uncompressedBytes.resize(uncompLength * 2);
uncompLength *= 2;
}
while (!done)
{
// If our output buffer is too small
if (strm.total_out >= uncompLength)
{
uncompressedBytes.resize(uncompLength * 2);
uncompLength *= 2;
}
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
strm.avail_out = uncompLength - strm.total_out;
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
strm.avail_out = uncompLength - strm.total_out;
// Inflate another chunk.
err = inflate(&strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END)
done = true;
else if (err != Z_OK)
{
break;
}
}
// Inflate another chunk.
err = inflate(&strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END)
done = true;
else if (err != Z_OK)
{
break;
}
}
if (inflateEnd(&strm) != Z_OK || !done)
{
return false;
}
if (inflateEnd(&strm) != Z_OK || !done)
{
return false;
}
uncompressedBytes.resize(strm.total_out);
return true;
uncompressedBytes.resize(strm.total_out);
return true;
}
bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
{
if (uncompressedBytes.size() == 0)
{
compressedBytes = uncompressedBytes;
return true;
}
if (uncompressedBytes.size() == 0)
{
compressedBytes = uncompressedBytes;
return true;
}
unsigned compLength = std::min(uncompressedBytes.size(), 16);
compressedBytes.clear();
compressedBytes.resize(compLength);
unsigned compLength = std::min(uncompressedBytes.size(), 16);
compressedBytes.clear();
compressedBytes.resize(compLength);
z_stream zs;
memset(&zs, 0, sizeof(zs));
z_stream zs;
memset(&zs, 0, sizeof(zs));
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
{
return false;
}
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
{
return false;
}
zs.next_in = (Bytef*)uncompressedBytes.data();
zs.avail_in = uncompressedBytes.size();
zs.next_in = (Bytef*)uncompressedBytes.data();
zs.avail_in = uncompressedBytes.size();
int ret;
compressedBytes.resize(uncompressedBytes.size());
int ret;
compressedBytes.resize(uncompressedBytes.size());
unsigned offset = 0;
unsigned temp = 0;
do
{
auto remaining = compressedBytes.size() - offset;
if(remaining < 1)
{
compressedBytes.resize(compressedBytes.size() * 2);
}
zs.next_out = (Bytef *) (compressedBytes.data() + offset);
temp = zs.avail_out = compressedBytes.size() - offset;
ret = deflate(&zs, Z_FINISH);
offset += temp - zs.avail_out;
} while (ret == Z_OK);
unsigned offset = 0;
unsigned temp = 0;
do
{
auto remaining = compressedBytes.size() - offset;
if(remaining < 1)
{
compressedBytes.resize(compressedBytes.size() * 2);
}
zs.next_out = (Bytef *) (compressedBytes.data() + offset);
temp = zs.avail_out = compressedBytes.size() - offset;
ret = deflate(&zs, Z_FINISH);
offset += temp - zs.avail_out;
} while (ret == Z_OK);
compressedBytes.resize(offset);
compressedBytes.resize(offset);
if (deflateEnd(&zs) != Z_OK)
{
return false;
}
if (deflateEnd(&zs) != Z_OK)
{
return false;
}
if (ret != Z_STREAM_END)
{
return false;
}
return true;
if (ret != Z_STREAM_END)
{
return false;
}
return true;
}

View File

@ -6,7 +6,7 @@
class MULTIMC_LOGIC_EXPORT GZip
{
public:
static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
};

View File

@ -6,50 +6,50 @@
void fib(int &prev, int &cur)
{
auto ret = prev + cur;
prev = cur;
cur = ret;
auto ret = prev + cur;
prev = cur;
cur = ret;
}
class GZipTest : public QObject
{
Q_OBJECT
Q_OBJECT
private
slots:
void test_Through()
{
// test up to 10 MB
static const int size = 10 * 1024 * 1024;
QByteArray random;
QByteArray compressed;
QByteArray decompressed;
std::default_random_engine eng((std::random_device())());
std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max());
void test_Through()
{
// test up to 10 MB
static const int size = 10 * 1024 * 1024;
QByteArray random;
QByteArray compressed;
QByteArray decompressed;
std::default_random_engine eng((std::random_device())());
std::uniform_int_distribution<uint8_t> idis(0, std::numeric_limits<uint8_t>::max());
// initialize random buffer
for(int i = 0; i < size; i++)
{
random.append((char)idis(eng));
}
// initialize random buffer
for(int i = 0; i < size; i++)
{
random.append((char)idis(eng));
}
// initialize fibonacci
int prev = 1;
int cur = 1;
// initialize fibonacci
int prev = 1;
int cur = 1;
// test if fibonacci long random buffers pass through GZip
do
{
QByteArray copy = random;
copy.resize(cur);
compressed.clear();
decompressed.clear();
QVERIFY(GZip::zip(copy, compressed));
QVERIFY(GZip::unzip(compressed, decompressed));
QCOMPARE(decompressed, copy);
fib(prev, cur);
} while (cur < size);
}
// test if fibonacci long random buffers pass through GZip
do
{
QByteArray copy = random;
copy.resize(cur);
compressed.clear();
decompressed.clear();
QVERIFY(GZip::zip(copy, compressed));
QVERIFY(GZip::unzip(compressed, decompressed));
QCOMPARE(decompressed, copy);
fib(prev, cur);
} while (cur < size);
}
};
QTEST_GUILESS_MAIN(GZipTest)

View File

@ -8,50 +8,50 @@
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
{
m_origInstance = origInstance;
m_origInstance = origInstance;
if(!copySaves)
{
// FIXME: get this from the original instance type...
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
matcherReal->caseSensitive(false);
m_matcher.reset(matcherReal);
}
if(!copySaves)
{
// FIXME: get this from the original instance type...
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
matcherReal->caseSensitive(false);
m_matcher.reset(matcherReal);
}
}
void InstanceCopyTask::executeTask()
{
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
m_copyFutureWatcher.setFuture(m_copyFuture);
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
m_copyFutureWatcher.setFuture(m_copyFuture);
}
void InstanceCopyTask::copyFinished()
{
auto successful = m_copyFuture.result();
if(!successful)
{
emitFailed(tr("Instance folder copy failed."));
return;
}
// FIXME: shouldn't this be able to report errors?
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
instanceSettings->registerSetting("InstanceType", "Legacy");
auto successful = m_copyFuture.result();
if(!successful)
{
emitFailed(tr("Instance folder copy failed."));
return;
}
// FIXME: shouldn't this be able to report errors?
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
instanceSettings->registerSetting("InstanceType", "Legacy");
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(m_instName);
inst->setIconKey(m_instIcon);
emitSucceeded();
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(m_instName);
inst->setIconKey(m_instIcon);
emitSucceeded();
}
void InstanceCopyTask::copyAborted()
{
emitFailed(tr("Instance folder copy has been aborted."));
return;
emitFailed(tr("Instance folder copy has been aborted."));
return;
}

View File

@ -15,19 +15,19 @@ class BaseInstanceProvider;
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
{
Q_OBJECT
Q_OBJECT
public:
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
protected:
//! Entry point for tasks.
virtual void executeTask() override;
void copyFinished();
void copyAborted();
//! Entry point for tasks.
virtual void executeTask() override;
void copyFinished();
void copyAborted();
private: /* data */
InstancePtr m_origInstance;
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
std::unique_ptr<IPathMatcher> m_matcher;
InstancePtr m_origInstance;
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
std::unique_ptr<IPathMatcher> m_matcher;
};

View File

@ -9,25 +9,25 @@
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
{
m_version = version;
m_version = version;
}
void InstanceCreationTask::executeTask()
{
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
{
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
instanceSettings->suspendSave();
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
auto components = inst.getComponentList();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
inst.setName(m_instName);
inst.setIconKey(m_instIcon);
inst.init();
instanceSettings->resumeSave();
}
emitSucceeded();
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
{
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
instanceSettings->suspendSave();
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
auto components = inst.getComponentList();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
inst.setName(m_instName);
inst.setIconKey(m_instIcon);
inst.init();
instanceSettings->resumeSave();
}
emitSucceeded();
}

View File

@ -10,14 +10,14 @@
class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask
{
Q_OBJECT
Q_OBJECT
public:
explicit InstanceCreationTask(BaseVersionPtr version);
explicit InstanceCreationTask(BaseVersionPtr version);
protected:
//! Entry point for tasks.
virtual void executeTask() override;
//! Entry point for tasks.
virtual void executeTask() override;
private: /* data */
BaseVersionPtr m_version;
BaseVersionPtr m_version;
};

View File

@ -18,394 +18,394 @@
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
{
m_sourceUrl = sourceUrl;
m_sourceUrl = sourceUrl;
}
void InstanceImportTask::executeTask()
{
InstancePtr newInstance;
InstancePtr newInstance;
if (m_sourceUrl.isLocalFile())
{
m_archivePath = m_sourceUrl.toLocalFile();
processZipPack();
}
else
{
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true;
if (m_sourceUrl.isLocalFile())
{
m_archivePath = m_sourceUrl.toLocalFile();
processZipPack();
}
else
{
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true;
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
auto entry = ENV.metacache()->resolveEntry("general", path);
entry->setStale(true);
m_filesNetJob.reset(new NetJob(tr("Modpack download")));
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath();
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
m_filesNetJob->start();
}
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
auto entry = ENV.metacache()->resolveEntry("general", path);
entry->setStale(true);
m_filesNetJob.reset(new NetJob(tr("Modpack download")));
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath();
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
m_filesNetJob->start();
}
}
void InstanceImportTask::downloadSucceeded()
{
processZipPack();
m_filesNetJob.reset();
processZipPack();
m_filesNetJob.reset();
}
void InstanceImportTask::downloadFailed(QString reason)
{
emitFailed(reason);
m_filesNetJob.reset();
emitFailed(reason);
m_filesNetJob.reset();
}
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{
setProgress(current / 2, total);
setProgress(current / 2, total);
}
void InstanceImportTask::processZipPack()
{
setStatus(tr("Extracting modpack"));
QDir extractDir(m_stagingPath);
qDebug() << "Attempting to create instance from" << m_archivePath;
setStatus(tr("Extracting modpack"));
QDir extractDir(m_stagingPath);
qDebug() << "Attempting to create instance from" << m_archivePath;
// open the zip and find relevant files in it
m_packZip.reset(new QuaZip(m_archivePath));
if (!m_packZip->open(QuaZip::mdUnzip))
{
emitFailed(tr("Unable to open supplied modpack zip file."));
return;
}
// open the zip and find relevant files in it
m_packZip.reset(new QuaZip(m_archivePath));
if (!m_packZip->open(QuaZip::mdUnzip))
{
emitFailed(tr("Unable to open supplied modpack zip file."));
return;
}
QStringList blacklist = {"instance.cfg", "manifest.json"};
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
QString root;
if(!mmcFound.isNull())
{
// process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcFound;
root = mmcFound;
m_modpackType = ModpackType::MultiMC;
}
else if(!flameFound.isNull())
{
// process as Flame pack
qDebug() << "Flame:" << flameFound;
root = flameFound;
m_modpackType = ModpackType::Flame;
}
QStringList blacklist = {"instance.cfg", "manifest.json"};
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
QString root;
if(!mmcFound.isNull())
{
// process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcFound;
root = mmcFound;
m_modpackType = ModpackType::MultiMC;
}
else if(!flameFound.isNull())
{
// process as Flame pack
qDebug() << "Flame:" << flameFound;
root = flameFound;
m_modpackType = ModpackType::Flame;
}
if(m_modpackType == ModpackType::Unknown)
{
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
}
if(m_modpackType == ModpackType::Unknown)
{
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
}
// make sure we extract just the pack
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
m_extractFutureWatcher.setFuture(m_extractFuture);
// make sure we extract just the pack
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
m_extractFutureWatcher.setFuture(m_extractFuture);
}
void InstanceImportTask::extractFinished()
{
m_packZip.reset();
if (m_extractFuture.result().isEmpty())
{
emitFailed(tr("Failed to extract modpack"));
return;
}
QDir extractDir(m_stagingPath);
m_packZip.reset();
if (m_extractFuture.result().isEmpty())
{
emitFailed(tr("Failed to extract modpack"));
return;
}
QDir extractDir(m_stagingPath);
qDebug() << "Fixing permissions for extracted pack files...";
QDirIterator it(extractDir, QDirIterator::Subdirectories);
while (it.hasNext())
{
auto filepath = it.next();
QFileInfo file(filepath);
auto permissions = QFile::permissions(filepath);
auto origPermissions = permissions;
if(file.isDir())
{
// Folder +rwx for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
}
else
{
// File +rw for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
}
if(origPermissions != permissions)
{
if(!QFile::setPermissions(filepath, permissions))
{
logWarning(tr("Could not fix permissions for %1").arg(filepath));
}
else
{
qDebug() << "Fixed" << filepath;
}
}
}
qDebug() << "Fixing permissions for extracted pack files...";
QDirIterator it(extractDir, QDirIterator::Subdirectories);
while (it.hasNext())
{
auto filepath = it.next();
QFileInfo file(filepath);
auto permissions = QFile::permissions(filepath);
auto origPermissions = permissions;
if(file.isDir())
{
// Folder +rwx for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
}
else
{
// File +rw for current user
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
}
if(origPermissions != permissions)
{
if(!QFile::setPermissions(filepath, permissions))
{
logWarning(tr("Could not fix permissions for %1").arg(filepath));
}
else
{
qDebug() << "Fixed" << filepath;
}
}
}
switch(m_modpackType)
{
case ModpackType::Flame:
processFlame();
return;
case ModpackType::MultiMC:
processMultiMC();
return;
case ModpackType::Unknown:
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
}
switch(m_modpackType)
{
case ModpackType::Flame:
processFlame();
return;
case ModpackType::MultiMC:
processMultiMC();
return;
case ModpackType::Unknown:
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
}
}
void InstanceImportTask::extractAborted()
{
emitFailed(tr("Instance import has been aborted."));
return;
emitFailed(tr("Instance import has been aborted."));
return;
}
void InstanceImportTask::processFlame()
{
const static QMap<QString,QString> forgemap = {
{"1.2.5", "3.4.9.171"},
{"1.4.2", "6.0.1.355"},
{"1.4.7", "6.6.2.534"},
{"1.5.2", "7.8.1.737"}
};
Flame::Manifest pack;
try
{
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
Flame::loadManifest(pack, configPath);
QFile::remove(configPath);
}
catch (const JSONValidationError &e)
{
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
return;
}
if(!pack.overrides.isEmpty())
{
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
if (QFile::exists(overridePath))
{
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
if (!QFile::rename(overridePath, mcPath))
{
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
return;
}
}
else
{
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
}
}
const static QMap<QString,QString> forgemap = {
{"1.2.5", "3.4.9.171"},
{"1.4.2", "6.0.1.355"},
{"1.4.7", "6.6.2.534"},
{"1.5.2", "7.8.1.737"}
};
Flame::Manifest pack;
try
{
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
Flame::loadManifest(pack, configPath);
QFile::remove(configPath);
}
catch (const JSONValidationError &e)
{
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
return;
}
if(!pack.overrides.isEmpty())
{
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
if (QFile::exists(overridePath))
{
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
if (!QFile::rename(overridePath, mcPath))
{
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
return;
}
}
else
{
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
}
}
QString forgeVersion;
for(auto &loader: pack.minecraft.modLoaders)
{
auto id = loader.id;
if(id.startsWith("forge-"))
{
id.remove("forge-");
forgeVersion = id;
continue;
}
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
}
QString forgeVersion;
for(auto &loader: pack.minecraft.modLoaders)
{
auto id = loader.id;
if(id.startsWith("forge-"))
{
id.remove("forge-");
forgeVersion = id;
continue;
}
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
}
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto mcVersion = pack.minecraft.version;
// Hack to correct some 'special sauce'...
if(mcVersion.endsWith('.'))
{
mcVersion.remove(QRegExp("[.]+$"));
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
}
auto components = instance.getComponentList();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", mcVersion, true);
if(!forgeVersion.isEmpty())
{
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
if(forgeVersion == "recommended")
{
if(forgemap.contains(mcVersion))
{
forgeVersion = forgemap[mcVersion];
}
else
{
logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
}
}
components->setComponentVersion("net.minecraftforge", forgeVersion);
}
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
if(pack.name.contains("Direwolf20"))
{
instance.setIconKey("steve");
}
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
{
instance.setIconKey("ftb_logo");
}
else
{
// default to something other than the MultiMC default to distinguish these
instance.setIconKey("flame");
}
}
instance.init();
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
QFileInfo jarmodsInfo(jarmodsPath);
if(jarmodsInfo.isDir())
{
// install all the jar mods
qDebug() << "Found jarmods:";
QDir jarmodsDir(jarmodsPath);
QStringList jarMods;
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
{
qDebug() << info.fileName();
jarMods.push_back(info.absoluteFilePath());
}
auto profile = instance.getComponentList();
profile->installJarMods(jarMods);
// nuke the original files
FS::deletePath(jarmodsPath);
}
instance.setName(m_instName);
m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
{
auto results = m_modIdResolver->getResults();
m_filesNetJob.reset(new NetJob(tr("Mod download")));
for(auto result: results.files)
{
QString filename = result.fileName;
if(!result.required)
{
filename += ".disabled";
}
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto mcVersion = pack.minecraft.version;
// Hack to correct some 'special sauce'...
if(mcVersion.endsWith('.'))
{
mcVersion.remove(QRegExp("[.]+$"));
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
}
auto components = instance.getComponentList();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", mcVersion, true);
if(!forgeVersion.isEmpty())
{
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
if(forgeVersion == "recommended")
{
if(forgemap.contains(mcVersion))
{
forgeVersion = forgemap[mcVersion];
}
else
{
logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
}
}
components->setComponentVersion("net.minecraftforge", forgeVersion);
}
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
if(pack.name.contains("Direwolf20"))
{
instance.setIconKey("steve");
}
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
{
instance.setIconKey("ftb_logo");
}
else
{
// default to something other than the MultiMC default to distinguish these
instance.setIconKey("flame");
}
}
instance.init();
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
QFileInfo jarmodsInfo(jarmodsPath);
if(jarmodsInfo.isDir())
{
// install all the jar mods
qDebug() << "Found jarmods:";
QDir jarmodsDir(jarmodsPath);
QStringList jarMods;
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
{
qDebug() << info.fileName();
jarMods.push_back(info.absoluteFilePath());
}
auto profile = instance.getComponentList();
profile->installJarMods(jarMods);
// nuke the original files
FS::deletePath(jarmodsPath);
}
instance.setName(m_instName);
m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
{
auto results = m_modIdResolver->getResults();
m_filesNetJob.reset(new NetJob(tr("Mod download")));
for(auto result: results.files)
{
QString filename = result.fileName;
if(!result.required)
{
filename += ".disabled";
}
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath , relpath);
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
auto path = FS::PathCombine(m_stagingPath , relpath);
switch(result.type)
{
case Flame::File::Type::Folder:
{
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever.
}
case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod:
{
qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl);
break;
}
case Flame::File::Type::Modpack:
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
break;
case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc:
case Flame::File::Type::Unknown:
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
break;
}
}
m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
{
m_filesNetJob.reset();
emitSucceeded();
}
);
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
{
m_filesNetJob.reset();
emitFailed(reason);
});
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
{
setProgress(current, total);
});
setStatus(tr("Downloading mods..."));
m_filesNetJob->start();
}
);
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
{
m_modIdResolver.reset();
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
});
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
{
setProgress(current, total);
});
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
{
setStatus(status);
});
m_modIdResolver->start();
switch(result.type)
{
case Flame::File::Type::Folder:
{
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
// fall-through intentional, we treat these as plain old mods and dump them wherever.
}
case Flame::File::Type::SingleFile:
case Flame::File::Type::Mod:
{
qDebug() << "Will download" << result.url << "to" << path;
auto dl = Net::Download::makeFile(result.url, path);
m_filesNetJob->addNetAction(dl);
break;
}
case Flame::File::Type::Modpack:
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
break;
case Flame::File::Type::Cmod2:
case Flame::File::Type::Ctoc:
case Flame::File::Type::Unknown:
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
break;
}
}
m_modIdResolver.reset();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
{
m_filesNetJob.reset();
emitSucceeded();
}
);
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
{
m_filesNetJob.reset();
emitFailed(reason);
});
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
{
setProgress(current, total);
});
setStatus(tr("Downloading mods..."));
m_filesNetJob->start();
}
);
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
{
m_modIdResolver.reset();
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
});
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
{
setProgress(current, total);
});
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
{
setStatus(status);
});
m_modIdResolver->start();
}
void InstanceImportTask::processMultiMC()
{
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy");
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy");
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
// reset time played on import... because packs.
instance.resetTimePlayed();
// reset time played on import... because packs.
instance.resetTimePlayed();
// set a new nice name
instance.setName(m_instName);
// set a new nice name
instance.setName(m_instName);
// if the icon was specified by user, use that. otherwise pull icon from the pack
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
m_instIcon = instance.iconKey();
auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png");
if (QFile::exists(importIconPath))
{
// import icon
auto iconList = ENV.icons();
if (iconList->iconFileExists(m_instIcon))
{
iconList->deleteIcon(m_instIcon);
}
iconList->installIcons({importIconPath});
}
}
emitSucceeded();
// if the icon was specified by user, use that. otherwise pull icon from the pack
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
m_instIcon = instance.iconKey();
auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png");
if (QFile::exists(importIconPath))
{
// import icon
auto iconList = ENV.icons();
if (iconList->iconFileExists(m_instIcon))
{
iconList->deleteIcon(m_instIcon);
}
iconList->installIcons({importIconPath});
}
}
emitSucceeded();
}

View File

@ -13,43 +13,43 @@ class QuaZip;
class BaseInstanceProvider;
namespace Flame
{
class FileResolvingTask;
class FileResolvingTask;
}
class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask
{
Q_OBJECT
Q_OBJECT
public:
explicit InstanceImportTask(const QUrl sourceUrl);
explicit InstanceImportTask(const QUrl sourceUrl);
protected:
//! Entry point for tasks.
virtual void executeTask() override;
//! Entry point for tasks.
virtual void executeTask() override;
private:
void processZipPack();
void processMultiMC();
void processFlame();
void processZipPack();
void processMultiMC();
void processFlame();
private slots:
void downloadSucceeded();
void downloadFailed(QString reason);
void downloadProgressChanged(qint64 current, qint64 total);
void extractFinished();
void extractAborted();
void downloadSucceeded();
void downloadFailed(QString reason);
void downloadProgressChanged(qint64 current, qint64 total);
void extractFinished();
void extractAborted();
private: /* data */
NetJobPtr m_filesNetJob;
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
QUrl m_sourceUrl;
QString m_archivePath;
bool m_downloadRequired = false;
std::unique_ptr<QuaZip> m_packZip;
QFuture<QStringList> m_extractFuture;
QFutureWatcher<QStringList> m_extractFutureWatcher;
enum class ModpackType{
Unknown,
MultiMC,
Flame
} m_modpackType = ModpackType::Unknown;
NetJobPtr m_filesNetJob;
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
QUrl m_sourceUrl;
QString m_archivePath;
bool m_downloadRequired = false;
std::unique_ptr<QuaZip> m_packZip;
QFuture<QStringList> m_extractFuture;
QFutureWatcher<QStringList> m_extractFutureWatcher;
enum class ModpackType{
Unknown,
MultiMC,
Flame
} m_modpackType = ModpackType::Unknown;
};

View File

@ -27,9 +27,9 @@
#include "FolderInstanceProvider.h"
InstanceList::InstanceList(QObject *parent)
: QAbstractListModel(parent)
: QAbstractListModel(parent)
{
resumeWatch();
resumeWatch();
}
InstanceList::~InstanceList()
@ -38,310 +38,310 @@ InstanceList::~InstanceList()
int InstanceList::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_instances.count();
Q_UNUSED(parent);
return m_instances.count();
}
QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
if (row < 0 || row >= m_instances.size())
return QModelIndex();
return createIndex(row, column, (void *)m_instances.at(row).get());
Q_UNUSED(parent);
if (row < 0 || row >= m_instances.size())
return QModelIndex();
return createIndex(row, column, (void *)m_instances.at(row).get());
}
QVariant InstanceList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
switch (role)
{
case InstancePointerRole:
{
QVariant v = qVariantFromValue((void *)pdata);
return v;
}
case InstanceIDRole:
if (!index.isValid())
{
return QVariant();
}
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
switch (role)
{
case InstancePointerRole:
{
QVariant v = qVariantFromValue((void *)pdata);
return v;
}
case InstanceIDRole:
{
return pdata->id();
}
case Qt::DisplayRole:
{
return pdata->name();
}
case Qt::ToolTipRole:
{
return pdata->instanceRoot();
}
case Qt::DecorationRole:
{
return pdata->iconKey();
}
// HACK: see GroupView.h in gui!
case GroupRole:
{
return pdata->group();
}
default:
break;
}
return QVariant();
case Qt::DisplayRole:
{
return pdata->name();
}
case Qt::ToolTipRole:
{
return pdata->instanceRoot();
}
case Qt::DecorationRole:
{
return pdata->iconKey();
}
// HACK: see GroupView.h in gui!
case GroupRole:
{
return pdata->group();
}
default:
break;
}
return QVariant();
}
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
{
Qt::ItemFlags f;
if (index.isValid())
{
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
}
return f;
Qt::ItemFlags f;
if (index.isValid())
{
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
}
return f;
}
QStringList InstanceList::getGroups()
{
return m_groups.toList();
return m_groups.toList();
}
void InstanceList::deleteGroup(const QString& name)
{
for(auto & instance: m_instances)
{
auto instGroupName = instance->group();
if(instGroupName == name)
{
instance->setGroupPost(QString());
}
}
for(auto & instance: m_instances)
{
auto instGroupName = instance->group();
if(instGroupName == name)
{
instance->setGroupPost(QString());
}
}
}
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
{
QMap<InstanceId, InstanceLocator> out;
int i = 0;
for(auto & item: list)
{
auto id = item->id();
if(out.contains(id))
{
qWarning() << "Duplicate ID" << id << "in instance list";
}
out[id] = std::make_pair(item, i);
i++;
}
return out;
QMap<InstanceId, InstanceLocator> out;
int i = 0;
for(auto & item: list)
{
auto id = item->id();
if(out.contains(id))
{
qWarning() << "Duplicate ID" << id << "in instance list";
}
out[id] = std::make_pair(item, i);
i++;
}
return out;
}
InstanceList::InstListError InstanceList::loadList(bool complete)
{
auto existingIds = getIdMapping(m_instances);
auto existingIds = getIdMapping(m_instances);
QList<InstancePtr> newList;
QList<InstancePtr> newList;
auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids)
{
for(auto & id: ids)
{
if(existingIds.contains(id))
{
auto instPair = existingIds[id];
/*
auto & instPtr = instPair.first;
auto & instIdx = instPair.second;
*/
existingIds.remove(id);
qDebug() << "Should keep and soft-reload" << id;
}
else
{
InstancePtr instPtr = provider->loadInstance(id);
if(instPtr)
{
newList.append(instPtr);
}
}
}
};
if(complete)
{
for(auto & item: m_providers)
{
processIds(item.get(), item->discoverInstances());
}
}
else
{
for (auto & item: m_updatedProviders)
{
processIds(item, item->discoverInstances());
}
}
auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids)
{
for(auto & id: ids)
{
if(existingIds.contains(id))
{
auto instPair = existingIds[id];
/*
auto & instPtr = instPair.first;
auto & instIdx = instPair.second;
*/
existingIds.remove(id);
qDebug() << "Should keep and soft-reload" << id;
}
else
{
InstancePtr instPtr = provider->loadInstance(id);
if(instPtr)
{
newList.append(instPtr);
}
}
}
};
if(complete)
{
for(auto & item: m_providers)
{
processIds(item.get(), item->discoverInstances());
}
}
else
{
for (auto & item: m_updatedProviders)
{
processIds(item, item->discoverInstances());
}
}
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
if(!existingIds.isEmpty())
{
// get the list of removed instances and sort it by their original index, from last to first
auto deadList = existingIds.values();
auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
{
return a.second > b.second;
};
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
// remove the contiguous ranges of rows
int front_bookmark = -1;
int back_bookmark = -1;
int currentItem = -1;
auto removeNow = [&]()
{
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
endRemoveRows();
front_bookmark = -1;
back_bookmark = currentItem;
};
for(auto & removedItem: deadList)
{
auto instPtr = removedItem.first;
if(!complete && !m_updatedProviders.contains(instPtr->provider()))
{
continue;
}
instPtr->invalidate();
currentItem = removedItem.second;
if(back_bookmark == -1)
{
// no bookmark yet
back_bookmark = currentItem;
}
else if(currentItem == front_bookmark - 1)
{
// part of contiguous sequence, continue
}
else
{
// seam between previous and current item
removeNow();
}
front_bookmark = currentItem;
}
if(back_bookmark != -1)
{
removeNow();
}
}
if(newList.size())
{
add(newList);
}
m_updatedProviders.clear();
return NoError;
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
if(!existingIds.isEmpty())
{
// get the list of removed instances and sort it by their original index, from last to first
auto deadList = existingIds.values();
auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
{
return a.second > b.second;
};
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
// remove the contiguous ranges of rows
int front_bookmark = -1;
int back_bookmark = -1;
int currentItem = -1;
auto removeNow = [&]()
{
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
endRemoveRows();
front_bookmark = -1;
back_bookmark = currentItem;
};
for(auto & removedItem: deadList)
{
auto instPtr = removedItem.first;
if(!complete && !m_updatedProviders.contains(instPtr->provider()))
{
continue;
}
instPtr->invalidate();
currentItem = removedItem.second;
if(back_bookmark == -1)
{
// no bookmark yet
back_bookmark = currentItem;
}
else if(currentItem == front_bookmark - 1)
{
// part of contiguous sequence, continue
}
else
{
// seam between previous and current item
removeNow();
}
front_bookmark = currentItem;
}
if(back_bookmark != -1)
{
removeNow();
}
}
if(newList.size())
{
add(newList);
}
m_updatedProviders.clear();
return NoError;
}
void InstanceList::saveNow()
{
for(auto & item: m_instances)
{
item->saveNow();
}
for(auto & item: m_instances)
{
item->saveNow();
}
}
void InstanceList::add(const QList<InstancePtr> &t)
{
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
m_instances.append(t);
for(auto & ptr : t)
{
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
}
endInsertRows();
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
m_instances.append(t);
for(auto & ptr : t)
{
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
}
endInsertRows();
}
void InstanceList::resumeWatch()
{
if(m_watchLevel > 0)
{
qWarning() << "Bad suspend level resume in instance list";
return;
}
m_watchLevel++;
if(m_watchLevel > 0 && !m_updatedProviders.isEmpty())
{
loadList();
}
if(m_watchLevel > 0)
{
qWarning() << "Bad suspend level resume in instance list";
return;
}
m_watchLevel++;
if(m_watchLevel > 0 && !m_updatedProviders.isEmpty())
{
loadList();
}
}
void InstanceList::suspendWatch()
{
m_watchLevel --;
m_watchLevel --;
}
void InstanceList::providerUpdated()
{
auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender());
if(!provider)
{
qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
return;
}
m_updatedProviders.insert(provider);
if(m_watchLevel == 1)
{
loadList();
}
auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender());
if(!provider)
{
qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
return;
}
m_updatedProviders.insert(provider);
if(m_watchLevel == 1)
{
loadList();
}
}
void InstanceList::groupsPublished(QSet<QString> newGroups)
{
m_groups.unite(newGroups);
m_groups.unite(newGroups);
}
void InstanceList::addInstanceProvider(BaseInstanceProvider* provider)
{
connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
m_providers.append(provider);
connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
m_providers.append(provider);
}
InstancePtr InstanceList::getInstanceById(QString instId) const
{
if(instId.isEmpty())
return InstancePtr();
for(auto & inst: m_instances)
{
if (inst->id() == instId)
{
return inst;
}
}
return InstancePtr();
if(instId.isEmpty())
return InstancePtr();
for(auto & inst: m_instances)
{
if (inst->id() == instId)
{
return inst;
}
}
return InstancePtr();
}
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
{
return index(getInstIndex(getInstanceById(id).get()));
return index(getInstIndex(getInstanceById(id).get()));
}
int InstanceList::getInstIndex(BaseInstance *inst) const
{
int count = m_instances.count();
for (int i = 0; i < count; i++)
{
if (inst == m_instances[i].get())
{
return i;
}
}
return -1;
int count = m_instances.count();
for (int i = 0; i < count; i++)
{
if (inst == m_instances[i].get())
{
return i;
}
}
return -1;
}
void InstanceList::propertiesChanged(BaseInstance *inst)
{
int i = getInstIndex(inst);
if (i != -1)
{
emit dataChanged(index(i), index(i));
}
int i = getInstIndex(inst);
if (i != -1)
{
emit dataChanged(index(i), index(i));
}
}

View File

@ -31,75 +31,75 @@ class BaseInstance;
class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel
{
Q_OBJECT
Q_OBJECT
public:
explicit InstanceList(QObject *parent = 0);
virtual ~InstanceList();
explicit InstanceList(QObject *parent = 0);
virtual ~InstanceList();
public:
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
enum AdditionalRoles
{
GroupRole = Qt::UserRole,
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
};
/*!
* \brief Error codes returned by functions in the InstanceList class.
* NoError Indicates that no error occurred.
* UnknownError indicates that an unspecified error occurred.
*/
enum InstListError
{
NoError = 0,
UnknownError
};
enum AdditionalRoles
{
GroupRole = Qt::UserRole,
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
};
/*!
* \brief Error codes returned by functions in the InstanceList class.
* NoError Indicates that no error occurred.
* UnknownError indicates that an unspecified error occurred.
*/
enum InstListError
{
NoError = 0,
UnknownError
};
InstancePtr at(int i) const
{
return m_instances.at(i);
}
InstancePtr at(int i) const
{
return m_instances.at(i);
}
int count() const
{
return m_instances.count();
}
int count() const
{
return m_instances.count();
}
InstListError loadList(bool complete = false);
void saveNow();
InstListError loadList(bool complete = false);
void saveNow();
/// Add an instance provider. Takes ownership of it. Should only be done before the first load.
void addInstanceProvider(BaseInstanceProvider * provider);
/// Add an instance provider. Takes ownership of it. Should only be done before the first load.
void addInstanceProvider(BaseInstanceProvider * provider);
InstancePtr getInstanceById(QString id) const;
QModelIndex getInstanceIndexById(const QString &id) const;
QStringList getGroups();
InstancePtr getInstanceById(QString id) const;
QModelIndex getInstanceIndexById(const QString &id) const;
QStringList getGroups();
void deleteGroup(const QString & name);
void deleteGroup(const QString & name);
signals:
void dataIsInvalid();
void dataIsInvalid();
private slots:
void propertiesChanged(BaseInstance *inst);
void groupsPublished(QSet<QString>);
void providerUpdated();
void propertiesChanged(BaseInstance *inst);
void groupsPublished(QSet<QString>);
void providerUpdated();
private:
int getInstIndex(BaseInstance *inst) const;
void suspendWatch();
void resumeWatch();
void add(const QList<InstancePtr> &list);
int getInstIndex(BaseInstance *inst) const;
void suspendWatch();
void resumeWatch();
void add(const QList<InstancePtr> &list);
protected:
int m_watchLevel = 0;
QSet<BaseInstanceProvider *> m_updatedProviders;
QList<InstancePtr> m_instances;
QSet<QString> m_groups;
QVector<shared_qobject_ptr<BaseInstanceProvider>> m_providers;
int m_watchLevel = 0;
QSet<BaseInstanceProvider *> m_updatedProviders;
QList<InstancePtr> m_instances;
QSet<QString> m_groups;
QVector<shared_qobject_ptr<BaseInstanceProvider>> m_providers;
};

View File

@ -8,48 +8,48 @@ class BaseInstanceProvider;
class MULTIMC_LOGIC_EXPORT InstanceTask : public Task
{
Q_OBJECT
Q_OBJECT
public:
explicit InstanceTask();
virtual ~InstanceTask();
explicit InstanceTask();
virtual ~InstanceTask();
void setParentSettings(SettingsObjectPtr settings)
{
m_globalSettings = settings;
}
void setParentSettings(SettingsObjectPtr settings)
{
m_globalSettings = settings;
}
void setStagingPath(const QString &stagingPath)
{
m_stagingPath = stagingPath;
}
void setStagingPath(const QString &stagingPath)
{
m_stagingPath = stagingPath;
}
void setName(const QString &name)
{
m_instName = name;
}
QString name() const
{
return m_instName;
}
void setName(const QString &name)
{
m_instName = name;
}
QString name() const
{
return m_instName;
}
void setIcon(const QString &icon)
{
m_instIcon = icon;
}
void setIcon(const QString &icon)
{
m_instIcon = icon;
}
void setGroup(const QString &group)
{
m_instGroup = group;
}
QString group() const
{
return m_instGroup;
}
void setGroup(const QString &group)
{
m_instGroup = group;
}
QString group() const
{
return m_instGroup;
}
protected: /* data */
SettingsObjectPtr m_globalSettings;
QString m_instName;
QString m_instIcon;
QString m_instGroup;
QString m_stagingPath;
SettingsObjectPtr m_globalSettings;
QString m_instName;
QString m_instIcon;
QString m_instGroup;
QString m_stagingPath;
};

View File

@ -11,262 +11,262 @@ namespace Json
{
void write(const QJsonDocument &doc, const QString &filename)
{
FS::write(filename, doc.toJson());
FS::write(filename, doc.toJson());
}
void write(const QJsonObject &object, const QString &filename)
{
write(QJsonDocument(object), filename);
write(QJsonDocument(object), filename);
}
void write(const QJsonArray &array, const QString &filename)
{
write(QJsonDocument(array), filename);
write(QJsonDocument(array), filename);
}
QByteArray toBinary(const QJsonObject &obj)
{
return QJsonDocument(obj).toBinaryData();
return QJsonDocument(obj).toBinaryData();
}
QByteArray toBinary(const QJsonArray &array)
{
return QJsonDocument(array).toBinaryData();
return QJsonDocument(array).toBinaryData();
}
QByteArray toText(const QJsonObject &obj)
{
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
}
QByteArray toText(const QJsonArray &array)
{
return QJsonDocument(array).toJson(QJsonDocument::Compact);
return QJsonDocument(array).toJson(QJsonDocument::Compact);
}
static bool isBinaryJson(const QByteArray &data)
{
decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
}
QJsonDocument requireDocument(const QByteArray &data, const QString &what)
{
if (isBinaryJson(data))
{
QJsonDocument doc = QJsonDocument::fromBinaryData(data);
if (doc.isNull())
{
throw JsonException(what + ": Invalid JSON (binary JSON detected)");
}
return doc;
}
else
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError)
{
throw JsonException(what + ": Error parsing JSON: " + error.errorString());
}
return doc;
}
if (isBinaryJson(data))
{
QJsonDocument doc = QJsonDocument::fromBinaryData(data);
if (doc.isNull())
{
throw JsonException(what + ": Invalid JSON (binary JSON detected)");
}
return doc;
}
else
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError)
{
throw JsonException(what + ": Error parsing JSON: " + error.errorString());
}
return doc;
}
}
QJsonDocument requireDocument(const QString &filename, const QString &what)
{
return requireDocument(FS::read(filename), what);
return requireDocument(FS::read(filename), what);
}
QJsonObject requireObject(const QJsonDocument &doc, const QString &what)
{
if (!doc.isObject())
{
throw JsonException(what + " is not an object");
}
return doc.object();
if (!doc.isObject())
{
throw JsonException(what + " is not an object");
}
return doc.object();
}
QJsonArray requireArray(const QJsonDocument &doc, const QString &what)
{
if (!doc.isArray())
{
throw JsonException(what + " is not an array");
}
return doc.array();
if (!doc.isArray())
{
throw JsonException(what + " is not an array");
}
return doc.array();
}
void writeString(QJsonObject &to, const QString &key, const QString &value)
{
if (!value.isEmpty())
{
to.insert(key, value);
}
if (!value.isEmpty())
{
to.insert(key, value);
}
}
void writeStringList(QJsonObject &to, const QString &key, const QStringList &values)
{
if (!values.isEmpty())
{
QJsonArray array;
for(auto value: values)
{
array.append(value);
}
to.insert(key, array);
}
if (!values.isEmpty())
{
QJsonArray array;
for(auto value: values)
{
array.append(value);
}
to.insert(key, array);
}
}
template<>
QJsonValue toJson<QUrl>(const QUrl &url)
{
return QJsonValue(url.toString(QUrl::FullyEncoded));
return QJsonValue(url.toString(QUrl::FullyEncoded));
}
template<>
QJsonValue toJson<QByteArray>(const QByteArray &data)
{
return QJsonValue(QString::fromLatin1(data.toHex()));
return QJsonValue(QString::fromLatin1(data.toHex()));
}
template<>
QJsonValue toJson<QDateTime>(const QDateTime &datetime)
{
return QJsonValue(datetime.toString(Qt::ISODate));
return QJsonValue(datetime.toString(Qt::ISODate));
}
template<>
QJsonValue toJson<QDir>(const QDir &dir)
{
return QDir::current().relativeFilePath(dir.absolutePath());
return QDir::current().relativeFilePath(dir.absolutePath());
}
template<>
QJsonValue toJson<QUuid>(const QUuid &uuid)
{
return uuid.toString();
return uuid.toString();
}
template<>
QJsonValue toJson<QVariant>(const QVariant &variant)
{
return QJsonValue::fromVariant(variant);
return QJsonValue::fromVariant(variant);
}
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what)
{
const QString string = ensureIsType<QString>(value, what);
// ensure that the string can be safely cast to Latin1
if (string != QString::fromLatin1(string.toLatin1()))
{
throw JsonException(what + " is not encodable as Latin1");
}
return QByteArray::fromHex(string.toLatin1());
const QString string = ensureIsType<QString>(value, what);
// ensure that the string can be safely cast to Latin1
if (string != QString::fromLatin1(string.toLatin1()))
{
throw JsonException(what + " is not encodable as Latin1");
}
return QByteArray::fromHex(string.toLatin1());
}
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what)
{
if (!value.isArray())
{
throw JsonException(what + " is not an array");
}
return value.toArray();
if (!value.isArray())
{
throw JsonException(what + " is not an array");
}
return value.toArray();
}
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what)
{
if (!value.isString())
{
throw JsonException(what + " is not a string");
}
return value.toString();
if (!value.isString())
{
throw JsonException(what + " is not a string");
}
return value.toString();
}
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what)
{
if (!value.isBool())
{
throw JsonException(what + " is not a bool");
}
return value.toBool();
if (!value.isBool())
{
throw JsonException(what + " is not a bool");
}
return value.toBool();
}
template<> double requireIsType<double>(const QJsonValue &value, const QString &what)
{
if (!value.isDouble())
{
throw JsonException(what + " is not a double");
}
return value.toDouble();
if (!value.isDouble())
{
throw JsonException(what + " is not a double");
}
return value.toDouble();
}
template<> int requireIsType<int>(const QJsonValue &value, const QString &what)
{
const double doubl = requireIsType<double>(value, what);
if (fmod(doubl, 1) != 0)
{
throw JsonException(what + " is not an integer");
}
return int(doubl);
const double doubl = requireIsType<double>(value, what);
if (fmod(doubl, 1) != 0)
{
throw JsonException(what + " is not an integer");
}
return int(doubl);
}
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what)
{
const QString string = requireIsType<QString>(value, what);
const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
if (!datetime.isValid())
{
throw JsonException(what + " is not a ISO formatted date/time value");
}
return datetime;
const QString string = requireIsType<QString>(value, what);
const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
if (!datetime.isValid())
{
throw JsonException(what + " is not a ISO formatted date/time value");
}
return datetime;
}
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what)
{
const QString string = ensureIsType<QString>(value, what);
if (string.isEmpty())
{
return QUrl();
}
const QUrl url = QUrl(string, QUrl::StrictMode);
if (!url.isValid())
{
throw JsonException(what + " is not a correctly formatted URL");
}
return url;
const QString string = ensureIsType<QString>(value, what);
if (string.isEmpty())
{
return QUrl();
}
const QUrl url = QUrl(string, QUrl::StrictMode);
if (!url.isValid())
{
throw JsonException(what + " is not a correctly formatted URL");
}
return url;
}
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what)
{
const QString string = requireIsType<QString>(value, what);
// FIXME: does not handle invalid characters!
return QDir::current().absoluteFilePath(string);
const QString string = requireIsType<QString>(value, what);
// FIXME: does not handle invalid characters!
return QDir::current().absoluteFilePath(string);
}
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what)
{
const QString string = requireIsType<QString>(value, what);
const QUuid uuid = QUuid(string);
if (uuid.toString() != string) // converts back => valid
{
throw JsonException(what + " is not a valid UUID");
}
return uuid;
const QString string = requireIsType<QString>(value, what);
const QUuid uuid = QUuid(string);
if (uuid.toString() != string) // converts back => valid
{
throw JsonException(what + " is not a valid UUID");
}
return uuid;
}
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what)
{
if (!value.isObject())
{
throw JsonException(what + " is not an object");
}
return value.toObject();
if (!value.isObject())
{
throw JsonException(what + " is not an object");
}
return value.toObject();
}
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what)
{
if (value.isNull() || value.isUndefined())
{
throw JsonException(what + " is null or undefined");
}
return value.toVariant();
if (value.isNull() || value.isUndefined())
{
throw JsonException(what + " is null or undefined");
}
return value.toVariant();
}
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what)
{
if (value.isNull() || value.isUndefined())
{
throw JsonException(what + " is null or undefined");
}
return value;
if (value.isNull() || value.isUndefined())
{
throw JsonException(what + " is null or undefined");
}
return value;
}
}

View File

@ -19,7 +19,7 @@ namespace Json
class MULTIMC_LOGIC_EXPORT JsonException : public ::Exception
{
public:
JsonException(const QString &message) : Exception(message) {}
JsonException(const QString &message) : Exception(message) {}
};
/// @throw FileSystemException
@ -51,7 +51,7 @@ void writeStringList(QJsonObject & to, const QString &key, const QStringList &va
template<typename T>
QJsonValue toJson(const T &t)
{
return QJsonValue(t);
return QJsonValue(t);
}
template<>
QJsonValue toJson<QUrl>(const QUrl &url);
@ -69,12 +69,12 @@ QJsonValue toJson<QVariant>(const QVariant &variant);
template<typename T>
QJsonArray toJsonArray(const QList<T> &container)
{
QJsonArray array;
for (const T item : container)
{
array.append(toJson<T>(item));
}
return array;
QJsonArray array;
for (const T item : container)
{
array.append(toJson<T>(item));
}
return array;
}
////////////////// READING ////////////////////
@ -115,119 +115,119 @@ template<> MULTIMC_LOGIC_EXPORT QUrl requireIsType<QUrl>(const QJsonValue &value
template <typename T>
T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value")
{
if (value.isUndefined() || value.isNull())
{
return default_;
}
try
{
return requireIsType<T>(value, what);
}
catch (const JsonException &)
{
return default_;
}
if (value.isUndefined() || value.isNull())
{
return default_;
}
try
{
return requireIsType<T>(value, what);
}
catch (const JsonException &)
{
return default_;
}
}
/// @throw JsonException
template <typename T>
T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
{
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
throw JsonException(localWhat + "s parent does not contain " + localWhat);
}
return requireIsType<T>(parent.value(key), localWhat);
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
throw JsonException(localWhat + "s parent does not contain " + localWhat);
}
return requireIsType<T>(parent.value(key), localWhat);
}
template <typename T>
T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__")
{
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
return default_;
}
return ensureIsType<T>(parent.value(key), default_, localWhat);
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
return default_;
}
return ensureIsType<T>(parent.value(key), default_, localWhat);
}
template <typename T>
QVector<T> requireIsArrayOf(const QJsonDocument &doc)
{
const QJsonArray array = requireArray(doc);
QVector<T> out;
for (const QJsonValue val : array)
{
out.append(requireIsType<T>(val, "Document"));
}
return out;
const QJsonArray array = requireArray(doc);
QVector<T> out;
for (const QJsonValue val : array)
{
out.append(requireIsType<T>(val, "Document"));
}
return out;
}
template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value")
{
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
QVector<T> out;
for (const QJsonValue val : array)
{
out.append(requireIsType<T>(val, what));
}
return out;
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
QVector<T> out;
for (const QJsonValue val : array)
{
out.append(requireIsType<T>(val, what));
}
return out;
}
template <typename T>
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value")
{
if (value.isUndefined())
{
return default_;
}
return ensureIsArrayOf<T>(value, what);
if (value.isUndefined())
{
return default_;
}
return ensureIsArrayOf<T>(value, what);
}
/// @throw JsonException
template <typename T>
QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
{
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
throw JsonException(localWhat + "s parent does not contain " + localWhat);
}
return ensureIsArrayOf<T>(parent.value(key), localWhat);
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
throw JsonException(localWhat + "s parent does not contain " + localWhat);
}
return ensureIsArrayOf<T>(parent.value(key), localWhat);
}
template <typename T>
QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key,
const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__")
const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__")
{
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
return default_;
}
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
if (!parent.contains(key))
{
return default_;
}
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
}
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \
inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
{ \
return requireIsType<TYPE>(value, what); \
} \
inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
{ \
return ensureIsType<TYPE>(value, default_, what); \
} \
inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \
{ \
return requireIsType<TYPE>(parent, key, what); \
} \
inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \
{ \
return ensureIsType<TYPE>(parent, key, default_, what); \
}
inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
{ \
return requireIsType<TYPE>(value, what); \
} \
inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
{ \
return ensureIsType<TYPE>(value, default_, what); \
} \
inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \
{ \
return requireIsType<TYPE>(parent, key, what); \
} \
inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \
{ \
return ensureIsType<TYPE>(parent, key, default_, what); \
}
JSON_HELPERFUNCTIONS(Array, QJsonArray)
JSON_HELPERFUNCTIONS(Object, QJsonObject)

View File

@ -4,157 +4,157 @@
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
{
// QProcess has a strange interface... let's map a lot of those into a few.
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
// QProcess has a strange interface... let's map a lot of those into a few.
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
}
LoggedProcess::~LoggedProcess()
{
if(m_is_detachable)
{
setProcessState(QProcess::NotRunning);
}
if(m_is_detachable)
{
setProcessState(QProcess::NotRunning);
}
}
QStringList reprocess(const QByteArray & data, QString & leftover)
{
QString str = leftover + QString::fromLocal8Bit(data);
QString str = leftover + QString::fromLocal8Bit(data);
str.remove('\r');
QStringList lines = str.split("\n");
leftover = lines.takeLast();
return lines;
str.remove('\r');
QStringList lines = str.split("\n");
leftover = lines.takeLast();
return lines;
}
void LoggedProcess::on_stdErr()
{
auto lines = reprocess(readAllStandardError(), m_err_leftover);
emit log(lines, MessageLevel::StdErr);
auto lines = reprocess(readAllStandardError(), m_err_leftover);
emit log(lines, MessageLevel::StdErr);
}
void LoggedProcess::on_stdOut()
{
auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
emit log(lines, MessageLevel::StdOut);
auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
emit log(lines, MessageLevel::StdOut);
}
void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
{
// save the exit code
m_exit_code = exit_code;
// save the exit code
m_exit_code = exit_code;
// Flush console window
if (!m_err_leftover.isEmpty())
{
emit log({m_err_leftover}, MessageLevel::StdErr);
m_err_leftover.clear();
}
if (!m_out_leftover.isEmpty())
{
emit log({m_err_leftover}, MessageLevel::StdOut);
m_out_leftover.clear();
}
// Flush console window
if (!m_err_leftover.isEmpty())
{
emit log({m_err_leftover}, MessageLevel::StdErr);
m_err_leftover.clear();
}
if (!m_out_leftover.isEmpty())
{
emit log({m_err_leftover}, MessageLevel::StdOut);
m_out_leftover.clear();
}
// based on state, send signals
if (!m_is_aborting)
{
if (status == QProcess::NormalExit)
{
//: Message displayed on instance exit
emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Finished);
}
else
{
//: Message displayed on instance crashed
if(exit_code == -1)
emit log({tr("Process crashed.")}, MessageLevel::MultiMC);
else
emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Crashed);
}
}
else
{
//: Message displayed after the instance exits due to kill request
emit log({tr("Process was killed by user.")}, MessageLevel::Error);
changeState(LoggedProcess::Aborted);
}
// based on state, send signals
if (!m_is_aborting)
{
if (status == QProcess::NormalExit)
{
//: Message displayed on instance exit
emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Finished);
}
else
{
//: Message displayed on instance crashed
if(exit_code == -1)
emit log({tr("Process crashed.")}, MessageLevel::MultiMC);
else
emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Crashed);
}
}
else
{
//: Message displayed after the instance exits due to kill request
emit log({tr("Process was killed by user.")}, MessageLevel::Error);
changeState(LoggedProcess::Aborted);
}
}
void LoggedProcess::on_error(QProcess::ProcessError error)
{
switch(error)
{
case QProcess::FailedToStart:
{
emit log({tr("The process failed to start.")}, MessageLevel::Fatal);
changeState(LoggedProcess::FailedToStart);
break;
}
// we'll just ignore those... never needed them
case QProcess::Crashed:
case QProcess::ReadError:
case QProcess::Timedout:
case QProcess::UnknownError:
case QProcess::WriteError:
break;
}
switch(error)
{
case QProcess::FailedToStart:
{
emit log({tr("The process failed to start.")}, MessageLevel::Fatal);
changeState(LoggedProcess::FailedToStart);
break;
}
// we'll just ignore those... never needed them
case QProcess::Crashed:
case QProcess::ReadError:
case QProcess::Timedout:
case QProcess::UnknownError:
case QProcess::WriteError:
break;
}
}
void LoggedProcess::kill()
{
m_is_aborting = true;
QProcess::kill();
m_is_aborting = true;
QProcess::kill();
}
int LoggedProcess::exitCode() const
{
return m_exit_code;
return m_exit_code;
}
void LoggedProcess::changeState(LoggedProcess::State state)
{
if(state == m_state)
return;
m_state = state;
emit stateChanged(m_state);
if(state == m_state)
return;
m_state = state;
emit stateChanged(m_state);
}
LoggedProcess::State LoggedProcess::state() const
{
return m_state;
return m_state;
}
void LoggedProcess::on_stateChange(QProcess::ProcessState state)
{
switch(state)
{
case QProcess::NotRunning:
break; // let's not - there are too many that handle this already.
case QProcess::Starting:
{
if(m_state != LoggedProcess::NotRunning)
{
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting;
}
changeState(LoggedProcess::Starting);
return;
}
case QProcess::Running:
{
if(m_state != LoggedProcess::Starting)
{
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running;
}
changeState(LoggedProcess::Running);
return;
}
}
switch(state)
{
case QProcess::NotRunning:
break; // let's not - there are too many that handle this already.
case QProcess::Starting:
{
if(m_state != LoggedProcess::NotRunning)
{
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting;
}
changeState(LoggedProcess::Starting);
return;
}
case QProcess::Running:
{
if(m_state != LoggedProcess::Starting)
{
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running;
}
changeState(LoggedProcess::Running);
return;
}
}
}
#if defined Q_OS_WIN32
@ -172,5 +172,5 @@ qint64 LoggedProcess::processId() const
void LoggedProcess::setDetachable(bool detachable)
{
m_is_detachable = detachable;
m_is_detachable = detachable;
}

View File

@ -27,54 +27,54 @@ class MULTIMC_LOGIC_EXPORT LoggedProcess : public QProcess
{
Q_OBJECT
public:
enum State
{
NotRunning,
Starting,
FailedToStart,
Running,
Finished,
Crashed,
Aborted
};
enum State
{
NotRunning,
Starting,
FailedToStart,
Running,
Finished,
Crashed,
Aborted
};
public:
explicit LoggedProcess(QObject* parent = 0);
virtual ~LoggedProcess();
explicit LoggedProcess(QObject* parent = 0);
virtual ~LoggedProcess();
State state() const;
int exitCode() const;
qint64 processId() const;
State state() const;
int exitCode() const;
qint64 processId() const;
void setDetachable(bool detachable);
void setDetachable(bool detachable);
signals:
void log(QStringList lines, MessageLevel::Enum level);
void stateChanged(LoggedProcess::State state);
void log(QStringList lines, MessageLevel::Enum level);
void stateChanged(LoggedProcess::State state);
public slots:
/**
* @brief kill the process - equivalent to kill -9
*/
void kill();
/**
* @brief kill the process - equivalent to kill -9
*/
void kill();
private slots:
void on_stdErr();
void on_stdOut();
void on_exit(int exit_code, QProcess::ExitStatus status);
void on_error(QProcess::ProcessError error);
void on_stateChange(QProcess::ProcessState);
void on_stdErr();
void on_stdOut();
void on_exit(int exit_code, QProcess::ExitStatus status);
void on_error(QProcess::ProcessError error);
void on_stateChange(QProcess::ProcessState);
private:
void changeState(LoggedProcess::State state);
void changeState(LoggedProcess::State state);
private:
QString m_err_leftover;
QString m_out_leftover;
bool m_killed = false;
State m_state = NotRunning;
int m_exit_code = 0;
bool m_is_aborting = false;
bool m_is_detachable = false;
QString m_err_leftover;
QString m_out_leftover;
bool m_killed = false;
State m_state = NotRunning;
int m_exit_code = 0;
bool m_is_aborting = false;
bool m_is_detachable = false;
};

View File

@ -3,74 +3,74 @@
/// TAKEN FROM Qt, because it doesn't expose it intelligently
static inline QChar getNextChar(const QString &s, int location)
{
return (location < s.length()) ? s.at(location) : QChar();
return (location < s.length()) ? s.at(location) : QChar();
}
/// TAKEN FROM Qt, because it doesn't expose it intelligently
int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
{
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
{
// skip spaces, tabs and 0's
QChar c1 = getNextChar(s1, l1);
while (c1.isSpace())
c1 = getNextChar(s1, ++l1);
QChar c2 = getNextChar(s2, l2);
while (c2.isSpace())
c2 = getNextChar(s2, ++l2);
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
{
// skip spaces, tabs and 0's
QChar c1 = getNextChar(s1, l1);
while (c1.isSpace())
c1 = getNextChar(s1, ++l1);
QChar c2 = getNextChar(s2, l2);
while (c2.isSpace())
c2 = getNextChar(s2, ++l2);
if (c1.isDigit() && c2.isDigit())
{
while (c1.digitValue() == 0)
c1 = getNextChar(s1, ++l1);
while (c2.digitValue() == 0)
c2 = getNextChar(s2, ++l2);
if (c1.isDigit() && c2.isDigit())
{
while (c1.digitValue() == 0)
c1 = getNextChar(s1, ++l1);
while (c2.digitValue() == 0)
c2 = getNextChar(s2, ++l2);
int lookAheadLocation1 = l1;
int lookAheadLocation2 = l2;
int currentReturnValue = 0;
// find the last digit, setting currentReturnValue as we go if it isn't equal
for (QChar lookAhead1 = c1, lookAhead2 = c2;
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
{
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
if (!is1ADigit && !is2ADigit)
break;
if (!is1ADigit)
return -1;
if (!is2ADigit)
return 1;
if (currentReturnValue == 0)
{
if (lookAhead1 < lookAhead2)
{
currentReturnValue = -1;
}
else if (lookAhead1 > lookAhead2)
{
currentReturnValue = 1;
}
}
}
if (currentReturnValue != 0)
return currentReturnValue;
}
if (cs == Qt::CaseInsensitive)
{
if (!c1.isLower())
c1 = c1.toLower();
if (!c2.isLower())
c2 = c2.toLower();
}
int r = QString::localeAwareCompare(c1, c2);
if (r < 0)
return -1;
if (r > 0)
return 1;
}
// The two strings are the same (02 == 2) so fall back to the normal sort
return QString::compare(s1, s2, cs);
int lookAheadLocation1 = l1;
int lookAheadLocation2 = l2;
int currentReturnValue = 0;
// find the last digit, setting currentReturnValue as we go if it isn't equal
for (QChar lookAhead1 = c1, lookAhead2 = c2;
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
{
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
if (!is1ADigit && !is2ADigit)
break;
if (!is1ADigit)
return -1;
if (!is2ADigit)
return 1;
if (currentReturnValue == 0)
{
if (lookAhead1 < lookAhead2)
{
currentReturnValue = -1;
}
else if (lookAhead1 > lookAhead2)
{
currentReturnValue = 1;
}
}
}
if (currentReturnValue != 0)
return currentReturnValue;
}
if (cs == Qt::CaseInsensitive)
{
if (!c1.isLower())
c1 = c1.toLower();
if (!c2.isLower())
c2 = c2.toLower();
}
int r = QString::localeAwareCompare(c1, c2);
if (r < 0)
return -1;
if (r > 0)
return 1;
}
// The two strings are the same (02 == 2) so fall back to the normal sort
return QString::compare(s1, s2, cs);
}

View File

@ -6,5 +6,5 @@
namespace Strings
{
int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
}

View File

@ -25,231 +25,231 @@
// ours
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const JlCompress::FilterFunction filter)
{
QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip);
QuaZip modZip(from.filePath());
modZip.open(QuaZip::mdUnzip);
QuaZipFile fileInsideMod(&modZip);
QuaZipFile zipOutFile(into);
for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
{
QString filename = modZip.getCurrentFileName();
if (filter && !filter(filename))
{
qDebug() << "Skipping file " << filename << " from "
<< from.fileName() << " - filtered";
continue;
}
if (contained.contains(filename))
{
qDebug() << "Skipping already contained file " << filename << " from "
<< from.fileName();
continue;
}
contained.insert(filename);
QuaZipFile fileInsideMod(&modZip);
QuaZipFile zipOutFile(into);
for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
{
QString filename = modZip.getCurrentFileName();
if (filter && !filter(filename))
{
qDebug() << "Skipping file " << filename << " from "
<< from.fileName() << " - filtered";
continue;
}
if (contained.contains(filename))
{
qDebug() << "Skipping already contained file " << filename << " from "
<< from.fileName();
continue;
}
contained.insert(filename);
if (!fileInsideMod.open(QIODevice::ReadOnly))
{
qCritical() << "Failed to open " << filename << " from " << from.fileName();
return false;
}
if (!fileInsideMod.open(QIODevice::ReadOnly))
{
qCritical() << "Failed to open " << filename << " from " << from.fileName();
return false;
}
QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
{
qCritical() << "Failed to open " << filename << " in the jar";
fileInsideMod.close();
return false;
}
if (!JlCompress::copyData(fileInsideMod, zipOutFile))
{
zipOutFile.close();
fileInsideMod.close();
qCritical() << "Failed to copy data of " << filename << " into the jar";
return false;
}
zipOutFile.close();
fileInsideMod.close();
}
return true;
if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
{
qCritical() << "Failed to open " << filename << " in the jar";
fileInsideMod.close();
return false;
}
if (!JlCompress::copyData(fileInsideMod, zipOutFile))
{
zipOutFile.close();
fileInsideMod.close();
qCritical() << "Failed to copy data of " << filename << " into the jar";
return false;
}
zipOutFile.close();
fileInsideMod.close();
}
return true;
}
// ours
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
{
QuaZip zipOut(targetJarPath);
if (!zipOut.open(QuaZip::mdCreate))
{
QFile::remove(targetJarPath);
qCritical() << "Failed to open the minecraft.jar for modding";
return false;
}
// Files already added to the jar.
// These files will be skipped.
QSet<QString> addedFiles;
QuaZip zipOut(targetJarPath);
if (!zipOut.open(QuaZip::mdCreate))
{
QFile::remove(targetJarPath);
qCritical() << "Failed to open the minecraft.jar for modding";
return false;
}
// Files already added to the jar.
// These files will be skipped.
QSet<QString> addedFiles;
// Modify the jar
QListIterator<Mod> i(mods);
// Modify the jar
QListIterator<Mod> i(mods);
i.toBack();
while (i.hasPrevious())
{
const Mod &mod = i.previous();
// do not merge disabled mods.
if (!mod.enabled())
continue;
if (mod.type() == Mod::MOD_ZIPFILE)
{
if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false;
}
}
else if (mod.type() == Mod::MOD_SINGLEFILE)
{
// FIXME: buggy - does not work with addedFiles
auto filename = mod.filename();
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false;
}
addedFiles.insert(filename.fileName());
}
else if (mod.type() == Mod::MOD_FOLDER)
{
// FIXME: buggy - does not work with addedFiles
auto filename = mod.filename();
QString what_to_zip = filename.absoluteFilePath();
QDir dir(what_to_zip);
dir.cdUp();
QString parent_dir = dir.absolutePath();
if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false;
}
qDebug() << "Adding folder " << filename.fileName() << " from "
<< filename.absoluteFilePath();
}
else
{
// Make sure we do not continue launching when something is missing or undefined...
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
return false;
}
}
{
const Mod &mod = i.previous();
// do not merge disabled mods.
if (!mod.enabled())
continue;
if (mod.type() == Mod::MOD_ZIPFILE)
{
if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false;
}
}
else if (mod.type() == Mod::MOD_SINGLEFILE)
{
// FIXME: buggy - does not work with addedFiles
auto filename = mod.filename();
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false;
}
addedFiles.insert(filename.fileName());
}
else if (mod.type() == Mod::MOD_FOLDER)
{
// FIXME: buggy - does not work with addedFiles
auto filename = mod.filename();
QString what_to_zip = filename.absoluteFilePath();
QDir dir(what_to_zip);
dir.cdUp();
QString parent_dir = dir.absolutePath();
if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
return false;
}
qDebug() << "Adding folder " << filename.fileName() << " from "
<< filename.absoluteFilePath();
}
else
{
// Make sure we do not continue launching when something is missing or undefined...
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
return false;
}
}
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");}))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to insert minecraft.jar contents.";
return false;
}
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");}))
{
zipOut.close();
QFile::remove(targetJarPath);
qCritical() << "Failed to insert minecraft.jar contents.";
return false;
}
// Recompress the jar
zipOut.close();
if (zipOut.getZipError() != 0)
{
QFile::remove(targetJarPath);
qCritical() << "Failed to finalize minecraft.jar!";
return false;
}
return true;
// Recompress the jar
zipOut.close();
if (zipOut.getZipError() != 0)
{
QFile::remove(targetJarPath);
qCritical() << "Failed to finalize minecraft.jar!";
return false;
}
return true;
}
// ours
QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root)
{
QuaZipDir rootDir(zip, root);
for(auto fileName: rootDir.entryList(QDir::Files))
{
if(fileName == what)
return root;
}
for(auto fileName: rootDir.entryList(QDir::Dirs))
{
QString result = findFolderOfFileInZip(zip, what, root + fileName);
if(!result.isEmpty())
{
return result;
}
}
return QString();
QuaZipDir rootDir(zip, root);
for(auto fileName: rootDir.entryList(QDir::Files))
{
if(fileName == what)
return root;
}
for(auto fileName: rootDir.entryList(QDir::Dirs))
{
QString result = findFolderOfFileInZip(zip, what, root + fileName);
if(!result.isEmpty())
{
return result;
}
}
return QString();
}
// ours
bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root)
{
QuaZipDir rootDir(zip, root);
for(auto fileName: rootDir.entryList(QDir::Files))
{
if(fileName == what)
{
result.append(root);
return true;
}
}
for(auto fileName: rootDir.entryList(QDir::Dirs))
{
findFilesInZip(zip, what, result, root + fileName);
}
return !result.isEmpty();
QuaZipDir rootDir(zip, root);
for(auto fileName: rootDir.entryList(QDir::Files))
{
if(fileName == what)
{
result.append(root);
return true;
}
}
for(auto fileName: rootDir.entryList(QDir::Dirs))
{
findFilesInZip(zip, what, result, root + fileName);
}
return !result.isEmpty();
}
// ours
QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
{
QDir directory(target);
QStringList extracted;
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
if (!zip->goToFirstFile())
{
qWarning() << "Failed to seek to first file in zip";
return QStringList();
}
do
{
QString name = zip->getCurrentFileName();
if(!name.startsWith(subdir))
{
continue;
}
name.remove(0, subdir.size());
QString absFilePath = directory.absoluteFilePath(name);
if(name.isEmpty())
{
absFilePath += "/";
}
if (!JlCompress::extractFile(zip, "", absFilePath))
{
qWarning() << "Failed to extract file" << name << "to" << absFilePath;
JlCompress::removeFile(extracted);
return QStringList();
}
extracted.append(absFilePath);
qDebug() << "Extracted file" << name;
} while (zip->goToNextFile());
return extracted;
QDir directory(target);
QStringList extracted;
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
if (!zip->goToFirstFile())
{
qWarning() << "Failed to seek to first file in zip";
return QStringList();
}
do
{
QString name = zip->getCurrentFileName();
if(!name.startsWith(subdir))
{
continue;
}
name.remove(0, subdir.size());
QString absFilePath = directory.absoluteFilePath(name);
if(name.isEmpty())
{
absFilePath += "/";
}
if (!JlCompress::extractFile(zip, "", absFilePath))
{
qWarning() << "Failed to extract file" << name << "to" << absFilePath;
JlCompress::removeFile(extracted);
return QStringList();
}
extracted.append(absFilePath);
qDebug() << "Extracted file" << name;
} while (zip->goToNextFile());
return extracted;
}
// ours
QStringList MMCZip::extractDir(QString fileCompressed, QString dir)
{
QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip))
{
return {};
}
return MMCZip::extractSubDir(&zip, "", dir);
QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip))
{
return {};
}
return MMCZip::extractSubDir(&zip, "", dir);
}

View File

@ -28,44 +28,44 @@
namespace MMCZip
{
/**
* Merge two zip files, using a filter function
*/
bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
const JlCompress::FilterFunction filter = nullptr);
/**
* Merge two zip files, using a filter function
*/
bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
const JlCompress::FilterFunction filter = nullptr);
/**
* take a source jar, add mods to it, resulting in target jar
*/
bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
/**
* take a source jar, add mods to it, resulting in target jar
*/
bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
/**
* Find a single file in archive by file name (not path)
*
* \return the path prefix where the file is
*/
QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
/**
* Find a single file in archive by file name (not path)
*
* \return the path prefix where the file is
*/
QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
/**
* Find a multiple files of the same name in archive by file name
* If a file is found in a path, no deeper paths are searched
*
* \return true if anything was found
*/
bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
/**
* Find a multiple files of the same name in archive by file name
* If a file is found in a path, no deeper paths are searched
*
* \return true if anything was found
*/
bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
/**
* Extract a subdirectory from an archive
*/
QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
/**
* Extract a subdirectory from an archive
*/
QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
/**
* Extract a whole archive.
*
* \param fileCompressed The name of the archive.
* \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure.
*/
QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
/**
* Extract a whole archive.
*
* \param fileCompressed The name of the archive.
* \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure.
*/
QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
}

View File

@ -2,35 +2,35 @@
MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
{
if (levelName == "MultiMC")
return MessageLevel::MultiMC;
else if (levelName == "Debug")
return MessageLevel::Debug;
else if (levelName == "Info")
return MessageLevel::Info;
else if (levelName == "Message")
return MessageLevel::Message;
else if (levelName == "Warning")
return MessageLevel::Warning;
else if (levelName == "Error")
return MessageLevel::Error;
else if (levelName == "Fatal")
return MessageLevel::Fatal;
// Skip PrePost, it's not exposed to !![]!
// Also skip StdErr and StdOut
else
return MessageLevel::Unknown;
if (levelName == "MultiMC")
return MessageLevel::MultiMC;
else if (levelName == "Debug")
return MessageLevel::Debug;
else if (levelName == "Info")
return MessageLevel::Info;
else if (levelName == "Message")
return MessageLevel::Message;
else if (levelName == "Warning")
return MessageLevel::Warning;
else if (levelName == "Error")
return MessageLevel::Error;
else if (levelName == "Fatal")
return MessageLevel::Fatal;
// Skip PrePost, it's not exposed to !![]!
// Also skip StdErr and StdOut
else
return MessageLevel::Unknown;
}
MessageLevel::Enum MessageLevel::fromLine(QString &line)
{
// Level prefix
int endmark = line.indexOf("]!");
if (line.startsWith("!![") && endmark != -1)
{
auto level = MessageLevel::getLevel(line.left(endmark).mid(3));
line = line.mid(endmark + 2);
return level;
}
return MessageLevel::Unknown;
// Level prefix
int endmark = line.indexOf("]!");
if (line.startsWith("!![") && endmark != -1)
{
auto level = MessageLevel::getLevel(line.left(endmark).mid(3));
line = line.mid(endmark + 2);
return level;
}
return MessageLevel::Unknown;
}

View File

@ -10,16 +10,16 @@ namespace MessageLevel
{
enum Enum
{
Unknown, /**< No idea what this is or where it came from */
StdOut, /**< Undetermined stderr messages */
StdErr, /**< Undetermined stdout messages */
MultiMC, /**< MultiMC Messages */
Debug, /**< Debug Messages */
Info, /**< Info Messages */
Message, /**< Standard Messages */
Warning, /**< Warnings */
Error, /**< Errors */
Fatal, /**< Fatal Errors */
Unknown, /**< No idea what this is or where it came from */
StdOut, /**< Undetermined stderr messages */
StdErr, /**< Undetermined stdout messages */
MultiMC, /**< MultiMC Messages */
Debug, /**< Debug Messages */
Info, /**< Info Messages */
Message, /**< Standard Messages */
Warning, /**< Warnings */
Error, /**< Errors */
Fatal, /**< Fatal Errors */
};
MessageLevel::Enum getLevel(const QString &levelName);

View File

@ -4,74 +4,74 @@
class NullInstance: public BaseInstance
{
public:
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
:BaseInstance(globalSettings, settings, rootDir)
{
setVersionBroken(true);
}
virtual ~NullInstance() {};
virtual void init() override
{
}
virtual void saveNow() override
{
}
virtual QString getStatusbarDescription() override
{
return tr("Unknown instance type");
};
virtual QSet< QString > traits() const override
{
return {};
};
virtual QString instanceConfigFolder() const override
{
return instanceRoot();
};
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
{
return nullptr;
}
virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
{
return nullptr;
}
virtual QProcessEnvironment createEnvironment() override
{
return QProcessEnvironment();
}
virtual QMap<QString, QString> getVariables() const override
{
return QMap<QString, QString>();
}
virtual IPathMatcher::Ptr getLogFileMatcher() override
{
return nullptr;
}
virtual QString getLogFileRoot() override
{
return instanceRoot();
}
virtual QString typeName() const override
{
return "Null";
}
bool canExport() const override
{
return false;
}
bool canEdit() const override
{
return false;
}
bool canLaunch() const override
{
return false;
}
QStringList verboseDescription(AuthSessionPtr session) override
{
QStringList out;
out << "Null instance - placeholder.";
return out;
}
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
:BaseInstance(globalSettings, settings, rootDir)
{
setVersionBroken(true);
}
virtual ~NullInstance() {};
virtual void init() override
{
}
virtual void saveNow() override
{
}
virtual QString getStatusbarDescription() override
{
return tr("Unknown instance type");
};
virtual QSet< QString > traits() const override
{
return {};
};
virtual QString instanceConfigFolder() const override
{
return instanceRoot();
};
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
{
return nullptr;
}
virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
{
return nullptr;
}
virtual QProcessEnvironment createEnvironment() override
{
return QProcessEnvironment();
}
virtual QMap<QString, QString> getVariables() const override
{
return QMap<QString, QString>();
}
virtual IPathMatcher::Ptr getLogFileMatcher() override
{
return nullptr;
}
virtual QString getLogFileRoot() override
{
return instanceRoot();
}
virtual QString typeName() const override
{
return "Null";
}
bool canExport() const override
{
return false;
}
bool canEdit() const override
{
return false;
}
bool canLaunch() const override
{
return false;
}
QStringList verboseDescription(AuthSessionPtr session) override
{
QStringList out;
out << "Null instance - placeholder.";
return out;
}
};

View File

@ -4,46 +4,46 @@
enum class ProblemSeverity
{
None,
Warning,
Error
None,
Warning,
Error
};
struct PatchProblem
{
ProblemSeverity m_severity;
QString m_description;
ProblemSeverity m_severity;
QString m_description;
};
class MULTIMC_LOGIC_EXPORT ProblemProvider
{
public:
virtual ~ProblemProvider() {};
virtual const QList<PatchProblem> getProblems() const = 0;
virtual ProblemSeverity getProblemSeverity() const = 0;
virtual ~ProblemProvider() {};
virtual const QList<PatchProblem> getProblems() const = 0;
virtual ProblemSeverity getProblemSeverity() const = 0;
};
class MULTIMC_LOGIC_EXPORT ProblemContainer : public ProblemProvider
{
public:
const QList<PatchProblem> getProblems() const override
{
return m_problems;
}
ProblemSeverity getProblemSeverity() const override
{
return m_problemSeverity;
}
virtual void addProblem(ProblemSeverity severity, const QString &description)
{
if(severity > m_problemSeverity)
{
m_problemSeverity = severity;
}
m_problems.append({severity, description});
}
const QList<PatchProblem> getProblems() const override
{
return m_problems;
}
ProblemSeverity getProblemSeverity() const override
{
return m_problemSeverity;
}
virtual void addProblem(ProblemSeverity severity, const QString &description)
{
if(severity > m_problemSeverity)
{
m_problemSeverity = severity;
}
m_problems.append({severity, description});
}
private:
QList<PatchProblem> m_problems;
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
QList<PatchProblem> m_problems;
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
};

View File

@ -8,10 +8,10 @@ namespace details
{
struct DeleteQObjectLater
{
void operator()(QObject *obj) const
{
obj->deleteLater();
}
void operator()(QObject *obj) const
{
obj->deleteLater();
}
};
}
/**
@ -28,56 +28,56 @@ template <typename T>
class shared_qobject_ptr
{
public:
shared_qobject_ptr(){}
shared_qobject_ptr(T * wrap)
{
reset(wrap);
}
shared_qobject_ptr(const shared_qobject_ptr<T>& other)
{
m_ptr = other.m_ptr;
}
template<typename Derived>
shared_qobject_ptr(const shared_qobject_ptr<Derived> &other)
{
m_ptr = other.unwrap();
}
shared_qobject_ptr(){}
shared_qobject_ptr(T * wrap)
{
reset(wrap);
}
shared_qobject_ptr(const shared_qobject_ptr<T>& other)
{
m_ptr = other.m_ptr;
}
template<typename Derived>
shared_qobject_ptr(const shared_qobject_ptr<Derived> &other)
{
m_ptr = other.unwrap();
}
public:
void reset(T * wrap)
{
using namespace std::placeholders;
m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1));
}
void reset(const shared_qobject_ptr<T> &other)
{
m_ptr = other.m_ptr;
}
void reset()
{
m_ptr.reset();
}
T * get() const
{
return m_ptr.get();
}
T * operator->() const
{
return m_ptr.get();
}
T & operator*() const
{
return *m_ptr.get();
}
operator bool() const
{
return m_ptr.get() != nullptr;
}
const std::shared_ptr <T> unwrap() const
{
return m_ptr;
}
void reset(T * wrap)
{
using namespace std::placeholders;
m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1));
}
void reset(const shared_qobject_ptr<T> &other)
{
m_ptr = other.m_ptr;
}
void reset()
{
m_ptr.reset();
}
T * get() const
{
return m_ptr.get();
}
T * operator->() const
{
return m_ptr.get();
}
T & operator*() const
{
return *m_ptr.get();
}
operator bool() const
{
return m_ptr.get() != nullptr;
}
const std::shared_ptr <T> unwrap() const
{
return m_ptr;
}
private:
std::shared_ptr <T> m_ptr;
std::shared_ptr <T> m_ptr;
};

View File

@ -5,59 +5,59 @@ template <typename K, typename V>
class RWStorage
{
public:
void add(K key, V value)
{
QWriteLocker l(&lock);
cache[key] = value;
stale_entries.remove(key);
}
V get(K key)
{
QReadLocker l(&lock);
if(cache.contains(key))
{
return cache[key];
}
else return V();
}
bool get(K key, V& value)
{
QReadLocker l(&lock);
if(cache.contains(key))
{
value = cache[key];
return true;
}
else return false;
}
bool has(K key)
{
QReadLocker l(&lock);
return cache.contains(key);
}
bool stale(K key)
{
QReadLocker l(&lock);
if(!cache.contains(key))
return true;
return stale_entries.contains(key);
}
void setStale(K key)
{
QWriteLocker l(&lock);
if(cache.contains(key))
{
stale_entries.insert(key);
}
}
void clear()
{
QWriteLocker l(&lock);
cache.clear();
stale_entries.clear();
}
void add(K key, V value)
{
QWriteLocker l(&lock);
cache[key] = value;
stale_entries.remove(key);
}
V get(K key)
{
QReadLocker l(&lock);
if(cache.contains(key))
{
return cache[key];
}
else return V();
}
bool get(K key, V& value)
{
QReadLocker l(&lock);
if(cache.contains(key))
{
value = cache[key];
return true;
}
else return false;
}
bool has(K key)
{
QReadLocker l(&lock);
return cache.contains(key);
}
bool stale(K key)
{
QReadLocker l(&lock);
if(!cache.contains(key))
return true;
return stale_entries.contains(key);
}
void setStale(K key)
{
QWriteLocker l(&lock);
if(cache.contains(key))
{
stale_entries.insert(key);
}
}
void clear()
{
QWriteLocker l(&lock);
cache.clear();
stale_entries.clear();
}
private:
QReadWriteLock lock;
QMap<K, V> cache;
QSet<K> stale_entries;
QReadWriteLock lock;
QMap<K, V> cache;
QSet<K> stale_entries;
};

View File

@ -4,108 +4,108 @@
#include <QDebug>
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent)
: QObject(parent), m_watcher(new QFileSystemWatcher(this))
: QObject(parent), m_watcher(new QFileSystemWatcher(this))
{
connect(m_watcher, &QFileSystemWatcher::fileChanged, this,
&RecursiveFileSystemWatcher::fileChange);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this,
&RecursiveFileSystemWatcher::directoryChange);
connect(m_watcher, &QFileSystemWatcher::fileChanged, this,
&RecursiveFileSystemWatcher::fileChange);
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this,
&RecursiveFileSystemWatcher::directoryChange);
}
void RecursiveFileSystemWatcher::setRootDir(const QDir &root)
{
bool wasEnabled = m_isEnabled;
disable();
m_root = root;
setFiles(scanRecursive(m_root));
if (wasEnabled)
{
enable();
}
bool wasEnabled = m_isEnabled;
disable();
m_root = root;
setFiles(scanRecursive(m_root));
if (wasEnabled)
{
enable();
}
}
void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles)
{
bool wasEnabled = m_isEnabled;
disable();
m_watchFiles = watchFiles;
if (wasEnabled)
{
enable();
}
bool wasEnabled = m_isEnabled;
disable();
m_watchFiles = watchFiles;
if (wasEnabled)
{
enable();
}
}
void RecursiveFileSystemWatcher::enable()
{
if (m_isEnabled)
{
return;
}
Q_ASSERT(m_root != QDir::root());
addFilesToWatcherRecursive(m_root);
m_isEnabled = true;
if (m_isEnabled)
{
return;
}
Q_ASSERT(m_root != QDir::root());
addFilesToWatcherRecursive(m_root);
m_isEnabled = true;
}
void RecursiveFileSystemWatcher::disable()
{
if (!m_isEnabled)
{
return;
}
m_isEnabled = false;
m_watcher->removePaths(m_watcher->files());
m_watcher->removePaths(m_watcher->directories());
if (!m_isEnabled)
{
return;
}
m_isEnabled = false;
m_watcher->removePaths(m_watcher->files());
m_watcher->removePaths(m_watcher->directories());
}
void RecursiveFileSystemWatcher::setFiles(const QStringList &files)
{
if (files != m_files)
{
m_files = files;
emit filesChanged();
}
if (files != m_files)
{
m_files = files;
emit filesChanged();
}
}
void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir)
{
m_watcher->addPath(dir.absolutePath());
for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
addFilesToWatcherRecursive(dir.absoluteFilePath(directory));
}
if (m_watchFiles)
{
for (const QFileInfo &info : dir.entryInfoList(QDir::Files))
{
m_watcher->addPath(info.absoluteFilePath());
}
}
m_watcher->addPath(dir.absolutePath());
for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
addFilesToWatcherRecursive(dir.absoluteFilePath(directory));
}
if (m_watchFiles)
{
for (const QFileInfo &info : dir.entryInfoList(QDir::Files))
{
m_watcher->addPath(info.absoluteFilePath());
}
}
}
QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory)
{
QStringList ret;
if(!m_matcher)
{
return {};
}
for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden))
{
ret.append(scanRecursive(directory.absoluteFilePath(dir)));
}
for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden))
{
auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
if (m_matcher->matches(relPath))
{
ret.append(relPath);
}
}
return ret;
QStringList ret;
if(!m_matcher)
{
return {};
}
for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden))
{
ret.append(scanRecursive(directory.absoluteFilePath(dir)));
}
for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden))
{
auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
if (m_matcher->matches(relPath))
{
ret.append(relPath);
}
}
return ret;
}
void RecursiveFileSystemWatcher::fileChange(const QString &path)
{
emit fileChanged(path);
emit fileChanged(path);
}
void RecursiveFileSystemWatcher::directoryChange(const QString &path)
{
setFiles(scanRecursive(m_root));
setFiles(scanRecursive(m_root));
}

View File

@ -8,56 +8,56 @@
class MULTIMC_LOGIC_EXPORT RecursiveFileSystemWatcher : public QObject
{
Q_OBJECT
Q_OBJECT
public:
RecursiveFileSystemWatcher(QObject *parent);
RecursiveFileSystemWatcher(QObject *parent);
void setRootDir(const QDir &root);
QDir rootDir() const
{
return m_root;
}
void setRootDir(const QDir &root);
QDir rootDir() const
{
return m_root;
}
// WARNING: setting this to true may be bad for performance
void setWatchFiles(const bool watchFiles);
bool watchFiles() const
{
return m_watchFiles;
}
// WARNING: setting this to true may be bad for performance
void setWatchFiles(const bool watchFiles);
bool watchFiles() const
{
return m_watchFiles;
}
void setMatcher(IPathMatcher::Ptr matcher)
{
m_matcher = matcher;
}
void setMatcher(IPathMatcher::Ptr matcher)
{
m_matcher = matcher;
}
QStringList files() const
{
return m_files;
}
QStringList files() const
{
return m_files;
}
signals:
void filesChanged();
void fileChanged(const QString &path);
void filesChanged();
void fileChanged(const QString &path);
public slots:
void enable();
void disable();
void enable();
void disable();
private:
QDir m_root;
bool m_watchFiles = false;
bool m_isEnabled = false;
IPathMatcher::Ptr m_matcher;
QDir m_root;
bool m_watchFiles = false;
bool m_isEnabled = false;
IPathMatcher::Ptr m_matcher;
QFileSystemWatcher *m_watcher;
QFileSystemWatcher *m_watcher;
QStringList m_files;
void setFiles(const QStringList &files);
QStringList m_files;
void setFiles(const QStringList &files);
void addFilesToWatcherRecursive(const QDir &dir);
QStringList scanRecursive(const QDir &dir);
void addFilesToWatcherRecursive(const QDir &dir);
QStringList scanRecursive(const QDir &dir);
private slots:
void fileChange(const QString &path);
void directoryChange(const QString &path);
void fileChange(const QString &path);
void directoryChange(const QString &path);
};

View File

@ -7,292 +7,292 @@ template <char Tseparator>
class SeparatorPrefixTree
{
public:
SeparatorPrefixTree(QStringList paths)
{
insert(paths);
}
SeparatorPrefixTree(QStringList paths)
{
insert(paths);
}
SeparatorPrefixTree(bool contained = false)
{
m_contained = contained;
}
SeparatorPrefixTree(bool contained = false)
{
m_contained = contained;
}
void insert(QStringList paths)
{
for(auto &path: paths)
{
insert(path);
}
}
void insert(QStringList paths)
{
for(auto &path: paths)
{
insert(path);
}
}
/// insert an exact path into the tree
SeparatorPrefixTree & insert(QString path)
{
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
children[path] = SeparatorPrefixTree(true);
return children[path];
}
else
{
auto prefix = path.left(sepIndex);
if(!children.contains(prefix))
{
children[prefix] = SeparatorPrefixTree(false);
}
return children[prefix].insert(path.mid(sepIndex + 1));
}
}
/// insert an exact path into the tree
SeparatorPrefixTree & insert(QString path)
{
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
children[path] = SeparatorPrefixTree(true);
return children[path];
}
else
{
auto prefix = path.left(sepIndex);
if(!children.contains(prefix))
{
children[prefix] = SeparatorPrefixTree(false);
}
return children[prefix].insert(path.mid(sepIndex + 1));
}
}
/// is the path fully contained in the tree?
bool contains(QString path) const
{
auto node = find(path);
return node != nullptr;
}
/// is the path fully contained in the tree?
bool contains(QString path) const
{
auto node = find(path);
return node != nullptr;
}
/// does the tree cover a path? That means the prefix of the path is contained in the tree
bool covers(QString path) const
{
// if we found some valid node, it's good enough. the tree covers the path
if(m_contained)
{
return true;
}
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return false;
}
return (*found).covers(QString());
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return false;
}
return (*found).covers(path.mid(sepIndex + 1));
}
}
/// does the tree cover a path? That means the prefix of the path is contained in the tree
bool covers(QString path) const
{
// if we found some valid node, it's good enough. the tree covers the path
if(m_contained)
{
return true;
}
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return false;
}
return (*found).covers(QString());
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return false;
}
return (*found).covers(path.mid(sepIndex + 1));
}
}
/// return the contained path that covers the path specified
QString cover(QString path) const
{
// if we found some valid node, it's good enough. the tree covers the path
if(m_contained)
{
return QString("");
}
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return QString();
}
auto nested = (*found).cover(QString());
if(nested.isNull())
{
return nested;
}
if(nested.isEmpty())
return path;
return path + Tseparator + nested;
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return QString();
}
auto nested = (*found).cover(path.mid(sepIndex + 1));
if(nested.isNull())
{
return nested;
}
if(nested.isEmpty())
return prefix;
return prefix + Tseparator + nested;
}
}
/// return the contained path that covers the path specified
QString cover(QString path) const
{
// if we found some valid node, it's good enough. the tree covers the path
if(m_contained)
{
return QString("");
}
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return QString();
}
auto nested = (*found).cover(QString());
if(nested.isNull())
{
return nested;
}
if(nested.isEmpty())
return path;
return path + Tseparator + nested;
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return QString();
}
auto nested = (*found).cover(path.mid(sepIndex + 1));
if(nested.isNull())
{
return nested;
}
if(nested.isEmpty())
return prefix;
return prefix + Tseparator + nested;
}
}
/// Does the path-specified node exist in the tree? It does not have to be contained.
bool exists(QString path) const
{
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return false;
}
return true;
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return false;
}
return (*found).exists(path.mid(sepIndex + 1));
}
}
/// Does the path-specified node exist in the tree? It does not have to be contained.
bool exists(QString path) const
{
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return false;
}
return true;
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return false;
}
return (*found).exists(path.mid(sepIndex + 1));
}
}
/// find a node in the tree by name
const SeparatorPrefixTree * find(QString path) const
{
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return nullptr;
}
return &(*found);
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return nullptr;
}
return (*found).find(path.mid(sepIndex + 1));
}
}
/// find a node in the tree by name
const SeparatorPrefixTree * find(QString path) const
{
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
auto found = children.find(path);
if(found == children.end())
{
return nullptr;
}
return &(*found);
}
else
{
auto prefix = path.left(sepIndex);
auto found = children.find(prefix);
if(found == children.end())
{
return nullptr;
}
return (*found).find(path.mid(sepIndex + 1));
}
}
/// is this a leaf node?
bool leaf() const
{
return children.isEmpty();
}
/// is this a leaf node?
bool leaf() const
{
return children.isEmpty();
}
/// is this node actually contained in the tree, or is it purely structural?
bool contained() const
{
return m_contained;
}
/// is this node actually contained in the tree, or is it purely structural?
bool contained() const
{
return m_contained;
}
/// Remove a path from the tree
bool remove(QString path)
{
return removeInternal(path) != Failed;
}
/// Remove a path from the tree
bool remove(QString path)
{
return removeInternal(path) != Failed;
}
/// Clear all children of this node tree node
void clear()
{
children.clear();
}
/// Clear all children of this node tree node
void clear()
{
children.clear();
}
QStringList toStringList() const
{
QStringList collected;
// collecting these is more expensive.
auto iter = children.begin();
while(iter != children.end())
{
QStringList list = iter.value().toStringList();
for(int i = 0; i < list.size(); i++)
{
list[i] = iter.key() + Tseparator + list[i];
}
collected.append(list);
if((*iter).m_contained)
{
collected.append(iter.key());
}
iter++;
}
return collected;
}
QStringList toStringList() const
{
QStringList collected;
// collecting these is more expensive.
auto iter = children.begin();
while(iter != children.end())
{
QStringList list = iter.value().toStringList();
for(int i = 0; i < list.size(); i++)
{
list[i] = iter.key() + Tseparator + list[i];
}
collected.append(list);
if((*iter).m_contained)
{
collected.append(iter.key());
}
iter++;
}
return collected;
}
private:
enum Removal
{
Failed,
Succeeded,
HasChildren
};
Removal removeInternal(QString path = QString())
{
if(path.isEmpty())
{
if(!m_contained)
{
// remove all children - we are removing a prefix
clear();
return Succeeded;
}
m_contained = false;
if(children.size())
{
return HasChildren;
}
return Succeeded;
}
Removal remStatus = Failed;
QString childToRemove;
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
childToRemove = path;
auto found = children.find(childToRemove);
if(found == children.end())
{
return Failed;
}
remStatus = (*found).removeInternal();
}
else
{
childToRemove = path.left(sepIndex);
auto found = children.find(childToRemove);
if(found == children.end())
{
return Failed;
}
remStatus = (*found).removeInternal(path.mid(sepIndex + 1));
}
switch (remStatus)
{
case Failed:
case HasChildren:
{
return remStatus;
}
case Succeeded:
{
children.remove(childToRemove);
if(m_contained)
{
return HasChildren;
}
if(children.size())
{
return HasChildren;
}
return Succeeded;
}
}
return Failed;
}
enum Removal
{
Failed,
Succeeded,
HasChildren
};
Removal removeInternal(QString path = QString())
{
if(path.isEmpty())
{
if(!m_contained)
{
// remove all children - we are removing a prefix
clear();
return Succeeded;
}
m_contained = false;
if(children.size())
{
return HasChildren;
}
return Succeeded;
}
Removal remStatus = Failed;
QString childToRemove;
auto sepIndex = path.indexOf(Tseparator);
if(sepIndex == -1)
{
childToRemove = path;
auto found = children.find(childToRemove);
if(found == children.end())
{
return Failed;
}
remStatus = (*found).removeInternal();
}
else
{
childToRemove = path.left(sepIndex);
auto found = children.find(childToRemove);
if(found == children.end())
{
return Failed;
}
remStatus = (*found).removeInternal(path.mid(sepIndex + 1));
}
switch (remStatus)
{
case Failed:
case HasChildren:
{
return remStatus;
}
case Succeeded:
{
children.remove(childToRemove);
if(m_contained)
{
return HasChildren;
}
if(children.size())
{
return HasChildren;
}
return Succeeded;
}
}
return Failed;
}
private:
QMap<QString,SeparatorPrefixTree<Tseparator>> children;
bool m_contained = false;
QMap<QString,SeparatorPrefixTree<Tseparator>> children;
bool m_contained = false;
};

View File

@ -12,27 +12,27 @@ class Usable;
*/
class Usable
{
friend class UseLock;
friend class UseLock;
public:
std::size_t useCount()
{
return m_useCount;
}
bool isInUse()
{
return m_useCount > 0;
}
std::size_t useCount()
{
return m_useCount;
}
bool isInUse()
{
return m_useCount > 0;
}
protected:
virtual void decrementUses()
{
m_useCount--;
}
virtual void incrementUses()
{
m_useCount++;
}
virtual void decrementUses()
{
m_useCount--;
}
virtual void incrementUses()
{
m_useCount++;
}
private:
std::size_t m_useCount = 0;
std::size_t m_useCount = 0;
};
/**
@ -43,16 +43,16 @@ private:
class UseLock
{
public:
UseLock(std::shared_ptr<Usable> usable)
: m_usable(usable)
{
// this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
m_usable->incrementUses();
}
~UseLock()
{
m_usable->decrementUses();
}
UseLock(std::shared_ptr<Usable> usable)
: m_usable(usable)
{
// this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
m_usable->incrementUses();
}
~UseLock()
{
m_usable->decrementUses();
}
private:
std::shared_ptr<Usable> m_usable;
std::shared_ptr<Usable> m_usable;
};

View File

@ -7,79 +7,79 @@
Version::Version(const QString &str) : m_string(str)
{
parse();
parse();
}
bool Version::operator<(const Version &other) const
{
const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i)
{
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2)
{
return sec1 < sec2;
}
}
const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i)
{
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2)
{
return sec1 < sec2;
}
}
return false;
return false;
}
bool Version::operator<=(const Version &other) const
{
return *this < other || *this == other;
return *this < other || *this == other;
}
bool Version::operator>(const Version &other) const
{
const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i)
{
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2)
{
return sec1 > sec2;
}
}
const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i)
{
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2)
{
return sec1 > sec2;
}
}
return false;
return false;
}
bool Version::operator>=(const Version &other) const
{
return *this > other || *this == other;
return *this > other || *this == other;
}
bool Version::operator==(const Version &other) const
{
const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i)
{
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2)
{
return false;
}
}
const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i)
{
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
const Section sec2 =
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
if (sec1 != sec2)
{
return false;
}
}
return true;
return true;
}
bool Version::operator!=(const Version &other) const
{
return !operator==(other);
return !operator==(other);
}
void Version::parse()
{
m_sections.clear();
m_sections.clear();
// FIXME: this is bad. versions can contain a lot more separators...
QStringList parts = m_string.split('.');
// FIXME: this is bad. versions can contain a lot more separators...
QStringList parts = m_string.split('.');
for (const auto part : parts)
{
m_sections.append(Section(part));
}
for (const auto part : parts)
{
m_sections.append(Section(part));
}
}

View File

@ -10,98 +10,98 @@ class QUrl;
class MULTIMC_LOGIC_EXPORT Version
{
public:
Version(const QString &str);
Version() {}
Version(const QString &str);
Version() {}
bool operator<(const Version &other) const;
bool operator<=(const Version &other) const;
bool operator>(const Version &other) const;
bool operator>=(const Version &other) const;
bool operator==(const Version &other) const;
bool operator!=(const Version &other) const;
bool operator<(const Version &other) const;
bool operator<=(const Version &other) const;
bool operator>(const Version &other) const;
bool operator>=(const Version &other) const;
bool operator==(const Version &other) const;
bool operator!=(const Version &other) const;
QString toString() const
{
return m_string;
}
QString toString() const
{
return m_string;
}
private:
QString m_string;
struct Section
{
explicit Section(const QString &fullString)
{
m_fullString = fullString;
int cutoff = m_fullString.size();
for(int i = 0; i < m_fullString.size(); i++)
{
if(!m_fullString[i].isDigit())
{
cutoff = i;
break;
}
}
auto numPart = m_fullString.leftRef(cutoff);
if(numPart.size())
{
numValid = true;
m_numPart = numPart.toInt();
}
auto stringPart = m_fullString.midRef(cutoff);
if(stringPart.size())
{
m_stringPart = stringPart.toString();
}
}
explicit Section() {}
bool numValid = false;
int m_numPart = 0;
QString m_stringPart;
QString m_fullString;
QString m_string;
struct Section
{
explicit Section(const QString &fullString)
{
m_fullString = fullString;
int cutoff = m_fullString.size();
for(int i = 0; i < m_fullString.size(); i++)
{
if(!m_fullString[i].isDigit())
{
cutoff = i;
break;
}
}
auto numPart = m_fullString.leftRef(cutoff);
if(numPart.size())
{
numValid = true;
m_numPart = numPart.toInt();
}
auto stringPart = m_fullString.midRef(cutoff);
if(stringPart.size())
{
m_stringPart = stringPart.toString();
}
}
explicit Section() {}
bool numValid = false;
int m_numPart = 0;
QString m_stringPart;
QString m_fullString;
inline bool operator!=(const Section &other) const
{
if(numValid && other.numValid)
{
return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart;
}
else
{
return m_fullString != other.m_fullString;
}
}
inline bool operator<(const Section &other) const
{
if(numValid && other.numValid)
{
if(m_numPart < other.m_numPart)
return true;
if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
return true;
return false;
}
else
{
return m_fullString < other.m_fullString;
}
}
inline bool operator>(const Section &other) const
{
if(numValid && other.numValid)
{
if(m_numPart > other.m_numPart)
return true;
if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart)
return true;
return false;
}
else
{
return m_fullString > other.m_fullString;
}
}
};
QList<Section> m_sections;
inline bool operator!=(const Section &other) const
{
if(numValid && other.numValid)
{
return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart;
}
else
{
return m_fullString != other.m_fullString;
}
}
inline bool operator<(const Section &other) const
{
if(numValid && other.numValid)
{
if(m_numPart < other.m_numPart)
return true;
if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
return true;
return false;
}
else
{
return m_fullString < other.m_fullString;
}
}
inline bool operator>(const Section &other) const
{
if(numValid && other.numValid)
{
if(m_numPart > other.m_numPart)
return true;
if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart)
return true;
return false;
}
else
{
return m_fullString > other.m_fullString;
}
}
};
QList<Section> m_sections;
void parse();
void parse();
};

View File

@ -20,64 +20,64 @@
class ModUtilsTest : public QObject
{
Q_OBJECT
void setupVersions()
{
QTest::addColumn<QString>("first");
QTest::addColumn<QString>("second");
QTest::addColumn<bool>("lessThan");
QTest::addColumn<bool>("equal");
Q_OBJECT
void setupVersions()
{
QTest::addColumn<QString>("first");
QTest::addColumn<QString>("second");
QTest::addColumn<bool>("lessThan");
QTest::addColumn<bool>("equal");
QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true;
QTest::newRow("equal, implicit 1") << "1.2" << "1.2.0" << false << true;
QTest::newRow("equal, implicit 2") << "1.2.0" << "1.2" << false << true;
QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true;
QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true;
QTest::newRow("equal, implicit 1") << "1.2" << "1.2.0" << false << true;
QTest::newRow("equal, implicit 2") << "1.2.0" << "1.2" << false << true;
QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true;
QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false;
QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false;
QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false;
QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.1" << true << false;
QTest::newRow("lessThan, implicit 2") << "1.2" << "1.3.0" << true << false;
QTest::newRow("lessThan, implicit 3") << "1.2" << "2.2.0" << true << false;
QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false;
QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false;
QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false;
QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false;
QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.1" << true << false;
QTest::newRow("lessThan, implicit 2") << "1.2" << "1.3.0" << true << false;
QTest::newRow("lessThan, implicit 3") << "1.2" << "2.2.0" << true << false;
QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false;
QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false;
QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false;
QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false;
QTest::newRow("greaterThan, implicit 1") << "1.2.1" << "1.2" << false << false;
QTest::newRow("greaterThan, implicit 2") << "1.3.0" << "1.2" << false << false;
QTest::newRow("greaterThan, implicit 3") << "2.2.0" << "1.2" << false << false;
QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false;
}
QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false;
QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false;
QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false;
QTest::newRow("greaterThan, implicit 1") << "1.2.1" << "1.2" << false << false;
QTest::newRow("greaterThan, implicit 2") << "1.3.0" << "1.2" << false << false;
QTest::newRow("greaterThan, implicit 3") << "2.2.0" << "1.2" << false << false;
QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false;
}
private slots:
void initTestCase()
{
void initTestCase()
{
}
void cleanupTestCase()
{
}
void cleanupTestCase()
{
}
}
void test_versionCompare_data()
{
setupVersions();
}
void test_versionCompare()
{
QFETCH(QString, first);
QFETCH(QString, second);
QFETCH(bool, lessThan);
QFETCH(bool, equal);
void test_versionCompare_data()
{
setupVersions();
}
void test_versionCompare()
{
QFETCH(QString, first);
QFETCH(QString, second);
QFETCH(bool, lessThan);
QFETCH(bool, equal);
const auto v1 = Version(first);
const auto v2 = Version(second);
const auto v1 = Version(first);
const auto v2 = Version(second);
QCOMPARE(v1 < v2, lessThan);
QCOMPARE(v1 > v2, !lessThan && !equal);
QCOMPARE(v1 == v2, equal);
}
QCOMPARE(v1 < v2, lessThan);
QCOMPARE(v1 > v2, !lessThan && !equal);
QCOMPARE(v1 == v2, equal);
}
};
QTEST_GUILESS_MAIN(ModUtilsTest)

View File

@ -6,21 +6,21 @@
enum IconType : unsigned
{
Builtin,
Transient,
FileBased,
ICONS_TOTAL,
ToBeDeleted
Builtin,
Transient,
FileBased,
ICONS_TOTAL,
ToBeDeleted
};
class MULTIMC_LOGIC_EXPORT IIconList
{
public:
virtual ~IIconList();
virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
virtual bool deleteIcon(const QString &key) = 0;
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
virtual bool iconFileExists(const QString &key) const = 0;
virtual void installIcons(const QStringList &iconFiles) = 0;
virtual void installIcon(const QString &file, const QString &name) = 0;
virtual ~IIconList();
virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
virtual bool deleteIcon(const QString &key) = 0;
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
virtual bool iconFileExists(const QString &key) const = 0;
virtual void installIcons(const QStringList &iconFiles) = 0;
virtual void installIcon(const QString &file, const QString &name) = 0;
};

View File

@ -16,149 +16,149 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
void JavaChecker::performCheck()
{
QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar");
QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar");
QStringList args;
QStringList args;
process.reset(new QProcess());
if(m_args.size())
{
auto extraArgs = Commandline::splitArgs(m_args);
args.append(extraArgs);
}
if(m_minMem != 0)
{
args << QString("-Xms%1m").arg(m_minMem);
}
if(m_maxMem != 0)
{
args << QString("-Xmx%1m").arg(m_maxMem);
}
if(m_permGen != 64)
{
args << QString("-XX:PermSize=%1m").arg(m_permGen);
}
process.reset(new QProcess());
if(m_args.size())
{
auto extraArgs = Commandline::splitArgs(m_args);
args.append(extraArgs);
}
if(m_minMem != 0)
{
args << QString("-Xms%1m").arg(m_minMem);
}
if(m_maxMem != 0)
{
args << QString("-Xmx%1m").arg(m_maxMem);
}
if(m_permGen != 64)
{
args << QString("-XX:PermSize=%1m").arg(m_permGen);
}
args.append({"-jar", checkerJar});
process->setArguments(args);
process->setProgram(m_path);
process->setProcessChannelMode(QProcess::SeparateChannels);
process->setProcessEnvironment(CleanEnviroment());
qDebug() << "Running java checker: " + m_path + args.join(" ");;
args.append({"-jar", checkerJar});
process->setArguments(args);
process->setProgram(m_path);
process->setProcessChannelMode(QProcess::SeparateChannels);
process->setProcessEnvironment(CleanEnviroment());
qDebug() << "Running java checker: " + m_path + args.join(" ");;
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady()));
connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady()));
connect(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
killTimer.setSingleShot(true);
killTimer.start(15000);
process->start();
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError)));
connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady()));
connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady()));
connect(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
killTimer.setSingleShot(true);
killTimer.start(15000);
process->start();
}
void JavaChecker::stdoutReady()
{
QByteArray data = process->readAllStandardOutput();
QString added = QString::fromLocal8Bit(data);
added.remove('\r');
m_stdout += added;
QByteArray data = process->readAllStandardOutput();
QString added = QString::fromLocal8Bit(data);
added.remove('\r');
m_stdout += added;
}
void JavaChecker::stderrReady()
{
QByteArray data = process->readAllStandardError();
QString added = QString::fromLocal8Bit(data);
added.remove('\r');
m_stderr += added;
QByteArray data = process->readAllStandardError();
QString added = QString::fromLocal8Bit(data);
added.remove('\r');
m_stderr += added;
}
void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
{
killTimer.stop();
QProcessPtr _process;
_process.swap(process);
killTimer.stop();
QProcessPtr _process;
_process.swap(process);
JavaCheckResult result;
{
result.path = m_path;
result.id = m_id;
}
result.errorLog = m_stderr;
result.outLog = m_stdout;
qDebug() << "STDOUT" << m_stdout;
qWarning() << "STDERR" << m_stderr;
qDebug() << "Java checker finished with status " << status << " exit code " << exitcode;
JavaCheckResult result;
{
result.path = m_path;
result.id = m_id;
}
result.errorLog = m_stderr;
result.outLog = m_stdout;
qDebug() << "STDOUT" << m_stdout;
qWarning() << "STDERR" << m_stderr;
qDebug() << "Java checker finished with status " << status << " exit code " << exitcode;
if (status == QProcess::CrashExit || exitcode == 1)
{
result.validity = JavaCheckResult::Validity::Errored;
emit checkFinished(result);
return;
}
if (status == QProcess::CrashExit || exitcode == 1)
{
result.validity = JavaCheckResult::Validity::Errored;
emit checkFinished(result);
return;
}
bool success = true;
bool success = true;
QMap<QString, QString> results;
QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
for(QString line : lines)
{
line = line.trimmed();
QMap<QString, QString> results;
QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts);
for(QString line : lines)
{
line = line.trimmed();
auto parts = line.split('=', QString::SkipEmptyParts);
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
{
success = false;
}
else
{
results.insert(parts[0], parts[1]);
}
}
auto parts = line.split('=', QString::SkipEmptyParts);
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
{
success = false;
}
else
{
results.insert(parts[0], parts[1]);
}
}
if(!results.contains("os.arch") || !results.contains("java.version") || !success)
{
result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
emit checkFinished(result);
return;
}
if(!results.contains("os.arch") || !results.contains("java.version") || !success)
{
result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
emit checkFinished(result);
return;
}
auto os_arch = results["os.arch"];
auto java_version = results["java.version"];
bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
auto os_arch = results["os.arch"];
auto java_version = results["java.version"];
bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
result.validity = JavaCheckResult::Validity::Valid;
result.is_64bit = is_64;
result.mojangPlatform = is_64 ? "64" : "32";
result.realPlatform = os_arch;
result.javaVersion = java_version;
qDebug() << "Java checker succeeded.";
emit checkFinished(result);
result.validity = JavaCheckResult::Validity::Valid;
result.is_64bit = is_64;
result.mojangPlatform = is_64 ? "64" : "32";
result.realPlatform = os_arch;
result.javaVersion = java_version;
qDebug() << "Java checker succeeded.";
emit checkFinished(result);
}
void JavaChecker::error(QProcess::ProcessError err)
{
if(err == QProcess::FailedToStart)
{
killTimer.stop();
qDebug() << "Java checker has failed to start.";
JavaCheckResult result;
{
result.path = m_path;
result.id = m_id;
}
if(err == QProcess::FailedToStart)
{
killTimer.stop();
qDebug() << "Java checker has failed to start.";
JavaCheckResult result;
{
result.path = m_path;
result.id = m_id;
}
emit checkFinished(result);
return;
}
emit checkFinished(result);
return;
}
}
void JavaChecker::timeout()
{
// NO MERCY. NO ABUSE.
if(process)
{
qDebug() << "Java checker has been killed by timeout.";
process->kill();
}
// NO MERCY. NO ABUSE.
if(process)
{
qDebug() << "Java checker has been killed by timeout.";
process->kill();
}
}

View File

@ -11,50 +11,50 @@ class JavaChecker;
struct MULTIMC_LOGIC_EXPORT JavaCheckResult
{
QString path;
QString mojangPlatform;
QString realPlatform;
JavaVersion javaVersion;
QString outLog;
QString errorLog;
bool is_64bit = false;
int id;
enum class Validity
{
Errored,
ReturnedInvalidData,
Valid
} validity = Validity::Errored;
QString path;
QString mojangPlatform;
QString realPlatform;
JavaVersion javaVersion;
QString outLog;
QString errorLog;
bool is_64bit = false;
int id;
enum class Validity
{
Errored,
ReturnedInvalidData,
Valid
} validity = Validity::Errored;
};
typedef std::shared_ptr<QProcess> QProcessPtr;
typedef std::shared_ptr<JavaChecker> JavaCheckerPtr;
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
{
Q_OBJECT
Q_OBJECT
public:
explicit JavaChecker(QObject *parent = 0);
void performCheck();
explicit JavaChecker(QObject *parent = 0);
void performCheck();
QString m_path;
QString m_args;
int m_id = 0;
int m_minMem = 0;
int m_maxMem = 0;
int m_permGen = 64;
QString m_path;
QString m_args;
int m_id = 0;
int m_minMem = 0;
int m_maxMem = 0;
int m_permGen = 64;
signals:
void checkFinished(JavaCheckResult result);
void checkFinished(JavaCheckResult result);
private:
QProcessPtr process;
QTimer killTimer;
QString m_stdout;
QString m_stderr;
QProcessPtr process;
QTimer killTimer;
QString m_stdout;
QString m_stderr;
public
slots:
void timeout();
void finished(int exitcode, QProcess::ExitStatus);
void error(QProcess::ProcessError);
void stdoutReady();
void stderrReady();
void timeout();
void finished(int exitcode, QProcess::ExitStatus);
void error(QProcess::ProcessError);
void stdoutReady();
void stderrReady();
};

View File

@ -19,26 +19,26 @@
void JavaCheckerJob::partFinished(JavaCheckResult result)
{
num_finished++;
qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
<< javacheckers.size();
setProgress(num_finished, javacheckers.size());
num_finished++;
qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
<< javacheckers.size();
setProgress(num_finished, javacheckers.size());
javaresults.replace(result.id, result);
javaresults.replace(result.id, result);
if (num_finished == javacheckers.size())
{
emitSucceeded();
}
if (num_finished == javacheckers.size())
{
emitSucceeded();
}
}
void JavaCheckerJob::executeTask()
{
qDebug() << m_job_name.toLocal8Bit() << " started.";
for (auto iter : javacheckers)
{
javaresults.append(JavaCheckResult());
connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult)));
iter->performCheck();
}
qDebug() << m_job_name.toLocal8Bit() << " started.";
for (auto iter : javacheckers)
{
javaresults.append(JavaCheckResult());
connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult)));
iter->performCheck();
}
}

View File

@ -25,37 +25,37 @@ typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr;
// FIXME: this just seems horribly redundant
class JavaCheckerJob : public Task
{
Q_OBJECT
Q_OBJECT
public:
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
virtual ~JavaCheckerJob() {};
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
virtual ~JavaCheckerJob() {};
bool addJavaCheckerAction(JavaCheckerPtr base)
{
javacheckers.append(base);
// if this is already running, the action needs to be started right away!
if (isRunning())
{
setProgress(num_finished, javacheckers.size());
connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
base->performCheck();
}
return true;
}
QList<JavaCheckResult> getResults()
{
return javaresults;
}
bool addJavaCheckerAction(JavaCheckerPtr base)
{
javacheckers.append(base);
// if this is already running, the action needs to be started right away!
if (isRunning())
{
setProgress(num_finished, javacheckers.size());
connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
base->performCheck();
}
return true;
}
QList<JavaCheckResult> getResults()
{
return javaresults;
}
private slots:
void partFinished(JavaCheckResult result);
void partFinished(JavaCheckResult result);
protected:
virtual void executeTask() override;
virtual void executeTask() override;
private:
QString m_job_name;
QList<JavaCheckerPtr> javacheckers;
QList<JavaCheckResult> javaresults;
int num_finished = 0;
QString m_job_name;
QList<JavaCheckerPtr> javacheckers;
QList<JavaCheckResult> javaresults;
int num_finished = 0;
};

View File

@ -3,26 +3,26 @@
bool JavaInstall::operator<(const JavaInstall &rhs)
{
auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
if(archCompare != 0)
return archCompare < 0;
if(id < rhs.id)
{
return true;
}
if(id > rhs.id)
{
return false;
}
return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
if(archCompare != 0)
return archCompare < 0;
if(id < rhs.id)
{
return true;
}
if(id > rhs.id)
{
return false;
}
return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
}
bool JavaInstall::operator==(const JavaInstall &rhs)
{
return arch == rhs.arch && id == rhs.id && path == rhs.path;
return arch == rhs.arch && id == rhs.id && path == rhs.path;
}
bool JavaInstall::operator>(const JavaInstall &rhs)
{
return (!operator<(rhs)) && (!operator==(rhs));
return (!operator<(rhs)) && (!operator==(rhs));
}

View File

@ -5,34 +5,34 @@
struct JavaInstall : public BaseVersion
{
JavaInstall(){}
JavaInstall(QString id, QString arch, QString path)
: id(id), arch(arch), path(path)
{
}
virtual QString descriptor()
{
return id.toString();
}
JavaInstall(){}
JavaInstall(QString id, QString arch, QString path)
: id(id), arch(arch), path(path)
{
}
virtual QString descriptor()
{
return id.toString();
}
virtual QString name()
{
return id.toString();
}
virtual QString name()
{
return id.toString();
}
virtual QString typeString() const
{
return arch;
}
virtual QString typeString() const
{
return arch;
}
bool operator<(const JavaInstall & rhs);
bool operator==(const JavaInstall & rhs);
bool operator>(const JavaInstall & rhs);
bool operator<(const JavaInstall & rhs);
bool operator==(const JavaInstall & rhs);
bool operator>(const JavaInstall & rhs);
JavaVersion id;
QString arch;
QString path;
bool recommended = false;
JavaVersion id;
QString arch;
QString path;
bool recommended = false;
};
typedef std::shared_ptr<JavaInstall> JavaInstallPtr;

View File

@ -31,111 +31,111 @@ JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
{
load();
return getCurrentTask();
load();
return getCurrentTask();
}
shared_qobject_ptr<Task> JavaInstallList::getCurrentTask()
{
if(m_status == Status::InProgress)
{
return m_loadTask;
}
return nullptr;
if(m_status == Status::InProgress)
{
return m_loadTask;
}
return nullptr;
}
void JavaInstallList::load()
{
if(m_status != Status::InProgress)
{
m_status = Status::InProgress;
m_loadTask = new JavaListLoadTask(this);
m_loadTask->start();
}
if(m_status != Status::InProgress)
{
m_status = Status::InProgress;
m_loadTask = new JavaListLoadTask(this);
m_loadTask->start();
}
}
const BaseVersionPtr JavaInstallList::at(int i) const
{
return m_vlist.at(i);
return m_vlist.at(i);
}
bool JavaInstallList::isLoaded()
{
return m_status == JavaInstallList::Status::Done;
return m_status == JavaInstallList::Status::Done;
}
int JavaInstallList::count() const
{
return m_vlist.count();
return m_vlist.count();
}
QVariant JavaInstallList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (!index.isValid())
return QVariant();
if (index.row() > count())
return QVariant();
if (index.row() > count())
return QVariant();
auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
switch (role)
{
case VersionPointerRole:
return qVariantFromValue(m_vlist[index.row()]);
case VersionIdRole:
return version->descriptor();
case VersionRole:
return version->id.toString();
case RecommendedRole:
return version->recommended;
case PathRole:
return version->path;
case ArchitectureRole:
return version->arch;
default:
return QVariant();
}
auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
switch (role)
{
case VersionPointerRole:
return qVariantFromValue(m_vlist[index.row()]);
case VersionIdRole:
return version->descriptor();
case VersionRole:
return version->id.toString();
case RecommendedRole:
return version->recommended;
case PathRole:
return version->path;
case ArchitectureRole:
return version->arch;
default:
return QVariant();
}
}
BaseVersionList::RoleList JavaInstallList::providesRoles() const
{
return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole};
return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole};
}
void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
{
beginResetModel();
m_vlist = versions;
sortVersions();
if(m_vlist.size())
{
auto best = std::dynamic_pointer_cast<JavaInstall>(m_vlist[0]);
best->recommended = true;
}
endResetModel();
m_status = Status::Done;
m_loadTask.reset();
beginResetModel();
m_vlist = versions;
sortVersions();
if(m_vlist.size())
{
auto best = std::dynamic_pointer_cast<JavaInstall>(m_vlist[0]);
best->recommended = true;
}
endResetModel();
m_status = Status::Done;
m_loadTask.reset();
}
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
{
auto rleft = std::dynamic_pointer_cast<JavaInstall>(left);
auto rright = std::dynamic_pointer_cast<JavaInstall>(right);
return (*rleft) > (*rright);
auto rleft = std::dynamic_pointer_cast<JavaInstall>(left);
auto rright = std::dynamic_pointer_cast<JavaInstall>(right);
return (*rleft) > (*rright);
}
void JavaInstallList::sortVersions()
{
beginResetModel();
std::sort(m_vlist.begin(), m_vlist.end(), sortJavas);
endResetModel();
beginResetModel();
std::sort(m_vlist.begin(), m_vlist.end(), sortJavas);
endResetModel();
}
JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task()
{
m_list = vlist;
m_currentRecommended = NULL;
m_list = vlist;
m_currentRecommended = NULL;
}
JavaListLoadTask::~JavaListLoadTask()
@ -144,65 +144,65 @@ JavaListLoadTask::~JavaListLoadTask()
void JavaListLoadTask::executeTask()
{
setStatus(tr("Detecting Java installations..."));
setStatus(tr("Detecting Java installations..."));
JavaUtils ju;
QList<QString> candidate_paths = ju.FindJavaPaths();
JavaUtils ju;
QList<QString> candidate_paths = ju.FindJavaPaths();
m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
connect(m_job.get(), &Task::progress, this, &Task::setProgress);
m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
connect(m_job.get(), &Task::progress, this, &Task::setProgress);
qDebug() << "Probing the following Java paths: ";
int id = 0;
for(QString candidate : candidate_paths)
{
qDebug() << " " << candidate;
qDebug() << "Probing the following Java paths: ";
int id = 0;
for(QString candidate : candidate_paths)
{
qDebug() << " " << candidate;
auto candidate_checker = new JavaChecker();
candidate_checker->m_path = candidate;
candidate_checker->m_id = id;
m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
auto candidate_checker = new JavaChecker();
candidate_checker->m_path = candidate;
candidate_checker->m_id = id;
m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
id++;
}
id++;
}
m_job->start();
m_job->start();
}
void JavaListLoadTask::javaCheckerFinished()
{
QList<JavaInstallPtr> candidates;
auto results = m_job->getResults();
QList<JavaInstallPtr> candidates;
auto results = m_job->getResults();
qDebug() << "Found the following valid Java installations:";
for(JavaCheckResult result : results)
{
if(result.validity == JavaCheckResult::Validity::Valid)
{
JavaInstallPtr javaVersion(new JavaInstall());
qDebug() << "Found the following valid Java installations:";
for(JavaCheckResult result : results)
{
if(result.validity == JavaCheckResult::Validity::Valid)
{
JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = result.javaVersion;
javaVersion->arch = result.mojangPlatform;
javaVersion->path = result.path;
candidates.append(javaVersion);
javaVersion->id = result.javaVersion;
javaVersion->arch = result.mojangPlatform;
javaVersion->path = result.path;
candidates.append(javaVersion);
qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;
}
}
qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;
}
}
QList<BaseVersionPtr> javas_bvp;
for (auto java : candidates)
{
//qDebug() << java->id << java->arch << " at " << java->path;
BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
QList<BaseVersionPtr> javas_bvp;
for (auto java : candidates)
{
//qDebug() << java->id << java->arch << " at " << java->path;
BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
if (bp_java)
{
javas_bvp.append(java);
}
}
if (bp_java)
{
javas_bvp.append(java);
}
}
m_list->updateListData(javas_bvp);
emitSucceeded();
m_list->updateListData(javas_bvp);
emitSucceeded();
}

View File

@ -30,52 +30,52 @@ class JavaListLoadTask;
class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList
{
Q_OBJECT
enum class Status
{
NotDone,
InProgress,
Done
};
Q_OBJECT
enum class Status
{
NotDone,
InProgress,
Done
};
public:
explicit JavaInstallList(QObject *parent = 0);
explicit JavaInstallList(QObject *parent = 0);
shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
void sortVersions() override;
shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
void sortVersions() override;
QVariant data(const QModelIndex &index, int role) const override;
RoleList providesRoles() const override;
QVariant data(const QModelIndex &index, int role) const override;
RoleList providesRoles() const override;
public slots:
void updateListData(QList<BaseVersionPtr> versions) override;
void updateListData(QList<BaseVersionPtr> versions) override;
protected:
void load();
shared_qobject_ptr<Task> getCurrentTask();
void load();
shared_qobject_ptr<Task> getCurrentTask();
protected:
Status m_status = Status::NotDone;
shared_qobject_ptr<JavaListLoadTask> m_loadTask;
QList<BaseVersionPtr> m_vlist;
Status m_status = Status::NotDone;
shared_qobject_ptr<JavaListLoadTask> m_loadTask;
QList<BaseVersionPtr> m_vlist;
};
class JavaListLoadTask : public Task
{
Q_OBJECT
Q_OBJECT
public:
explicit JavaListLoadTask(JavaInstallList *vlist);
virtual ~JavaListLoadTask();
explicit JavaListLoadTask(JavaInstallList *vlist);
virtual ~JavaListLoadTask();
void executeTask() override;
void executeTask() override;
public slots:
void javaCheckerFinished();
void javaCheckerFinished();
protected:
std::shared_ptr<JavaCheckerJob> m_job;
JavaInstallList *m_list;
JavaInstall *m_currentRecommended;
std::shared_ptr<JavaCheckerJob> m_job;
JavaInstallList *m_list;
JavaInstall *m_currentRecommended;
};

View File

@ -34,312 +34,312 @@ JavaUtils::JavaUtils()
#ifdef Q_OS_LINUX
static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
{
QDir mmcBin(QCoreApplication::applicationDirPath());
auto items = LD_LIBRARY_PATH.split(':');
QStringList final;
for(auto & item: items)
{
QDir test(item);
if(test == mmcBin)
{
qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item;
continue;
}
final.append(item);
}
return final.join(':');
QDir mmcBin(QCoreApplication::applicationDirPath());
auto items = LD_LIBRARY_PATH.split(':');
QStringList final;
for(auto & item: items)
{
QDir test(item);
if(test == mmcBin)
{
qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item;
continue;
}
final.append(item);
}
return final.join(':');
}
#endif
QProcessEnvironment CleanEnviroment()
{
// prepare the process environment
QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
QProcessEnvironment env;
// prepare the process environment
QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
QProcessEnvironment env;
QStringList ignored =
{
"JAVA_ARGS",
"CLASSPATH",
"CONFIGPATH",
"JAVA_HOME",
"JRE_HOME",
"_JAVA_OPTIONS",
"JAVA_OPTIONS",
"JAVA_TOOL_OPTIONS"
};
for(auto key: rawenv.keys())
{
auto value = rawenv.value(key);
// filter out dangerous java crap
if(ignored.contains(key))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// filter MultiMC-related things
if(key.startsWith("QT_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
QStringList ignored =
{
"JAVA_ARGS",
"CLASSPATH",
"CONFIGPATH",
"JAVA_HOME",
"JRE_HOME",
"_JAVA_OPTIONS",
"JAVA_OPTIONS",
"JAVA_TOOL_OPTIONS"
};
for(auto key: rawenv.keys())
{
auto value = rawenv.value(key);
// filter out dangerous java crap
if(ignored.contains(key))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// filter MultiMC-related things
if(key.startsWith("QT_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
#ifdef Q_OS_LINUX
// Do not pass LD_* variables to java. They were intended for MultiMC
if(key.startsWith("LD_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// Strip IBus
// IBus is a Linux IME framework. For some reason, it breaks MC?
if (key == "XMODIFIERS" && value.contains(IBUS))
{
QString save = value;
value.replace(IBUS, "");
qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
}
if(key == "GAME_PRELOAD")
{
env.insert("LD_PRELOAD", value);
continue;
}
if(key == "GAME_LIBRARY_PATH")
{
env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value));
continue;
}
// Do not pass LD_* variables to java. They were intended for MultiMC
if(key.startsWith("LD_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// Strip IBus
// IBus is a Linux IME framework. For some reason, it breaks MC?
if (key == "XMODIFIERS" && value.contains(IBUS))
{
QString save = value;
value.replace(IBUS, "");
qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
}
if(key == "GAME_PRELOAD")
{
env.insert("LD_PRELOAD", value);
continue;
}
if(key == "GAME_LIBRARY_PATH")
{
env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value));
continue;
}
#endif
// qDebug() << "Env: " << key << value;
env.insert(key, value);
}
// qDebug() << "Env: " << key << value;
env.insert(key, value);
}
#ifdef Q_OS_LINUX
// HACK: Workaround for QTBUG42500
if(!env.contains("LD_LIBRARY_PATH"))
{
env.insert("LD_LIBRARY_PATH", "");
}
// HACK: Workaround for QTBUG42500
if(!env.contains("LD_LIBRARY_PATH"))
{
env.insert("LD_LIBRARY_PATH", "");
}
#endif
return env;
return env;
}
JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
{
JavaInstallPtr javaVersion(new JavaInstall());
JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = id;
javaVersion->arch = arch;
javaVersion->path = path;
javaVersion->id = id;
javaVersion->arch = arch;
javaVersion->path = path;
return javaVersion;
return javaVersion;
}
JavaInstallPtr JavaUtils::GetDefaultJava()
{
JavaInstallPtr javaVersion(new JavaInstall());
JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = "java";
javaVersion->arch = "unknown";
javaVersion->id = "java";
javaVersion->arch = "unknown";
#if defined(Q_OS_WIN32)
javaVersion->path = "javaw";
javaVersion->path = "javaw";
#else
javaVersion->path = "java";
javaVersion->path = "java";
#endif
return javaVersion;
return javaVersion;
}
#if defined(Q_OS_WIN32)
QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName)
{
QList<JavaInstallPtr> javas;
QList<JavaInstallPtr> javas;
QString archType = "unknown";
if (keyType == KEY_WOW64_64KEY)
archType = "64";
else if (keyType == KEY_WOW64_32KEY)
archType = "32";
QString archType = "unknown";
if (keyType == KEY_WOW64_64KEY)
archType = "64";
else if (keyType == KEY_WOW64_32KEY)
archType = "32";
HKEY jreKey;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0,
KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
{
// Read the current type version from the registry.
// This will be used to find any key that contains the JavaHome value.
char *value = new char[0];
DWORD valueSz = 0;
if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) ==
ERROR_MORE_DATA)
{
value = new char[valueSz];
RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
}
HKEY jreKey;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0,
KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS)
{
// Read the current type version from the registry.
// This will be used to find any key that contains the JavaHome value.
char *value = new char[0];
DWORD valueSz = 0;
if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) ==
ERROR_MORE_DATA)
{
value = new char[valueSz];
RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
}
QString recommended = value;
QString recommended = value;
TCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode;
TCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode;
// Get the number of subkeys
RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL,
NULL, NULL);
// Get the number of subkeys
RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL,
NULL, NULL);
// Iterate until RegEnumKeyEx fails
if (numSubKeys > 0)
{
for (DWORD i = 0; i < numSubKeys; i++)
{
subKeyNameSize = 255;
retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
NULL);
if (retCode == ERROR_SUCCESS)
{
// Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + subKeyName;
// Iterate until RegEnumKeyEx fails
if (numSubKeys > 0)
{
for (DWORD i = 0; i < numSubKeys; i++)
{
subKeyNameSize = 255;
retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
NULL);
if (retCode == ERROR_SUCCESS)
{
// Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + subKeyName;
HKEY newKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0,
KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
{
// Read the JavaHome value to find where Java is installed.
value = new char[0];
valueSz = 0;
if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz) == ERROR_MORE_DATA)
{
value = new char[valueSz];
RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz);
HKEY newKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0,
KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS)
{
// Read the JavaHome value to find where Java is installed.
value = new char[0];
valueSz = 0;
if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz) == ERROR_MORE_DATA)
{
value = new char[valueSz];
RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz);
// Now, we construct the version object and add it to the list.
JavaInstallPtr javaVersion(new JavaInstall());
// Now, we construct the version object and add it to the list.
JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = subKeyName;
javaVersion->arch = archType;
javaVersion->path =
QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
javas.append(javaVersion);
}
javaVersion->id = subKeyName;
javaVersion->arch = archType;
javaVersion->path =
QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
javas.append(javaVersion);
}
RegCloseKey(newKey);
}
}
}
}
RegCloseKey(newKey);
}
}
}
}
RegCloseKey(jreKey);
}
RegCloseKey(jreKey);
}
return javas;
return javas;
}
QList<QString> JavaUtils::FindJavaPaths()
{
QList<JavaInstallPtr> java_candidates;
QList<JavaInstallPtr> java_candidates;
QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
java_candidates.append(JRE64s);
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK64s);
java_candidates.append(JRE32s);
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK32s);
java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
java_candidates.append(JRE64s);
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK64s);
java_candidates.append(JRE32s);
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK32s);
java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
QList<QString> candidates;
for(JavaInstallPtr java_candidate : java_candidates)
{
if(!candidates.contains(java_candidate->path))
{
candidates.append(java_candidate->path);
}
}
QList<QString> candidates;
for(JavaInstallPtr java_candidate : java_candidates)
{
if(!candidates.contains(java_candidate->path))
{
candidates.append(java_candidate->path);
}
}
return candidates;
return candidates;
}
#elif defined(Q_OS_MAC)
QList<QString> JavaUtils::FindJavaPaths()
{
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java");
javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java");
javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java");
QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/");
QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString &java, libraryJVMJavas) {
javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java");
}
QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/");
QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString &java, systemLibraryJVMJavas) {
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
}
return javas;
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java");
javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java");
javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java");
QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/");
QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString &java, libraryJVMJavas) {
javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java");
}
QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/");
QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QString &java, systemLibraryJVMJavas) {
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
}
return javas;
}
#elif defined(Q_OS_LINUX)
QList<QString> JavaUtils::FindJavaPaths()
{
qDebug() << "Linux Java detection incomplete - defaulting to \"java\"";
qDebug() << "Linux Java detection incomplete - defaulting to \"java\"";
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
auto scanJavaDir = [&](const QString & dirPath)
{
QDir dir(dirPath);
if(!dir.exists())
return;
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
for(auto & entry: entries)
{
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
auto scanJavaDir = [&](const QString & dirPath)
{
QDir dir(dirPath);
if(!dir.exists())
return;
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
for(auto & entry: entries)
{
QString prefix;
if(entry.isAbsolute())
{
prefix = entry.absoluteFilePath();
}
else
{
prefix = entry.filePath();
}
QString prefix;
if(entry.isAbsolute())
{
prefix = entry.absoluteFilePath();
}
else
{
prefix = entry.filePath();
}
javas.append(FS::PathCombine(prefix, "jre/bin/java"));
javas.append(FS::PathCombine(prefix, "bin/java"));
}
};
// oracle RPMs
scanJavaDir("/usr/java");
// general locations used by distro packaging
scanJavaDir("/usr/lib/jvm");
scanJavaDir("/usr/lib32/jvm");
// javas stored in MultiMC's folder
scanJavaDir("java");
return javas;
javas.append(FS::PathCombine(prefix, "jre/bin/java"));
javas.append(FS::PathCombine(prefix, "bin/java"));
}
};
// oracle RPMs
scanJavaDir("/usr/java");
// general locations used by distro packaging
scanJavaDir("/usr/lib/jvm");
scanJavaDir("/usr/lib32/jvm");
// javas stored in MultiMC's folder
scanJavaDir("java");
return javas;
}
#else
QList<QString> JavaUtils::FindJavaPaths()
{
qDebug() << "Unknown operating system build - defaulting to \"java\"";
qDebug() << "Unknown operating system build - defaulting to \"java\"";
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
QList<QString> javas;
javas.append(this->GetDefaultJava()->path);
return javas;
return javas;
}
#endif

View File

@ -30,15 +30,15 @@ QProcessEnvironment CleanEnviroment();
class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject
{
Q_OBJECT
Q_OBJECT
public:
JavaUtils();
JavaUtils();
JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown");
QList<QString> FindJavaPaths();
JavaInstallPtr GetDefaultJava();
JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown");
QList<QString> FindJavaPaths();
JavaInstallPtr GetDefaultJava();
#ifdef Q_OS_WIN
QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
#endif
};

View File

@ -6,116 +6,116 @@
JavaVersion & JavaVersion::operator=(const QString & javaVersionString)
{
m_string = javaVersionString;
m_string = javaVersionString;
auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int
{
auto str = match.captured(what);
if(str.isEmpty())
{
return 0;
}
return str.toInt();
};
auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int
{
auto str = match.captured(what);
if(str.isEmpty())
{
return 0;
}
return str.toInt();
};
QRegularExpression pattern;
if(javaVersionString.startsWith("1."))
{
pattern = QRegularExpression ("1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?");
}
else
{
pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
}
QRegularExpression pattern;
if(javaVersionString.startsWith("1."))
{
pattern = QRegularExpression ("1[.](?<major>[0-9]+)([.](?<minor>[0-9]+))?(_(?<security>[0-9]+)?)?(-(?<prerelease>[a-zA-Z0-9]+))?");
}
else
{
pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
}
auto match = pattern.match(m_string);
m_parseable = match.hasMatch();
m_major = getCapturedInteger(match, "major");
m_minor = getCapturedInteger(match, "minor");
m_security = getCapturedInteger(match, "security");
m_prerelease = match.captured("prerelease");
return *this;
auto match = pattern.match(m_string);
m_parseable = match.hasMatch();
m_major = getCapturedInteger(match, "major");
m_minor = getCapturedInteger(match, "minor");
m_security = getCapturedInteger(match, "security");
m_prerelease = match.captured("prerelease");
return *this;
}
JavaVersion::JavaVersion(const QString &rhs)
{
operator=(rhs);
operator=(rhs);
}
QString JavaVersion::toString()
{
return m_string;
return m_string;
}
bool JavaVersion::requiresPermGen()
{
if(m_parseable)
{
return m_major < 8;
}
return true;
if(m_parseable)
{
return m_major < 8;
}
return true;
}
bool JavaVersion::operator<(const JavaVersion &rhs)
{
if(m_parseable && rhs.m_parseable)
{
auto major = m_major;
auto rmajor = rhs.m_major;
if(m_parseable && rhs.m_parseable)
{
auto major = m_major;
auto rmajor = rhs.m_major;
// HACK: discourage using java 9
if(major > 8)
major = -major;
if(rmajor > 8)
rmajor = -rmajor;
// HACK: discourage using java 9
if(major > 8)
major = -major;
if(rmajor > 8)
rmajor = -rmajor;
if(major < rmajor)
return true;
if(major > rmajor)
return false;
if(m_minor < rhs.m_minor)
return true;
if(m_minor > rhs.m_minor)
return false;
if(m_security < rhs.m_security)
return true;
if(m_security > rhs.m_security)
return false;
if(major < rmajor)
return true;
if(major > rmajor)
return false;
if(m_minor < rhs.m_minor)
return true;
if(m_minor > rhs.m_minor)
return false;
if(m_security < rhs.m_security)
return true;
if(m_security > rhs.m_security)
return false;
// everything else being equal, consider prerelease status
bool thisPre = !m_prerelease.isEmpty();
bool rhsPre = !rhs.m_prerelease.isEmpty();
if(thisPre && !rhsPre)
{
// this is a prerelease and the other one isn't -> lesser
return true;
}
else if(!thisPre && rhsPre)
{
// this isn't a prerelease and the other one is -> greater
return false;
}
else if(thisPre && rhsPre)
{
// both are prereleases - use natural compare...
return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
}
// neither is prerelease, so they are the same -> this cannot be less than rhs
return false;
}
else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
// everything else being equal, consider prerelease status
bool thisPre = !m_prerelease.isEmpty();
bool rhsPre = !rhs.m_prerelease.isEmpty();
if(thisPre && !rhsPre)
{
// this is a prerelease and the other one isn't -> lesser
return true;
}
else if(!thisPre && rhsPre)
{
// this isn't a prerelease and the other one is -> greater
return false;
}
else if(thisPre && rhsPre)
{
// both are prereleases - use natural compare...
return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
}
// neither is prerelease, so they are the same -> this cannot be less than rhs
return false;
}
else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
}
bool JavaVersion::operator==(const JavaVersion &rhs)
{
if(m_parseable && rhs.m_parseable)
{
return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease;
}
return m_string == rhs.m_string;
if(m_parseable && rhs.m_parseable)
{
return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease;
}
return m_string == rhs.m_string;
}
bool JavaVersion::operator>(const JavaVersion &rhs)
{
return (!operator<(rhs)) && (!operator==(rhs));
return (!operator<(rhs)) && (!operator==(rhs));
}

View File

@ -5,46 +5,46 @@
// NOTE: apparently the GNU C library pollutes the global namespace with these... undef them.
#ifdef major
#undef major
#undef major
#endif
#ifdef minor
#undef minor
#undef minor
#endif
class MULTIMC_LOGIC_EXPORT JavaVersion
{
friend class JavaVersionTest;
friend class JavaVersionTest;
public:
JavaVersion() {};
JavaVersion(const QString & rhs);
JavaVersion() {};
JavaVersion(const QString & rhs);
JavaVersion & operator=(const QString & rhs);
JavaVersion & operator=(const QString & rhs);
bool operator<(const JavaVersion & rhs);
bool operator==(const JavaVersion & rhs);
bool operator>(const JavaVersion & rhs);
bool operator<(const JavaVersion & rhs);
bool operator==(const JavaVersion & rhs);
bool operator>(const JavaVersion & rhs);
bool requiresPermGen();
bool requiresPermGen();
QString toString();
QString toString();
int major()
{
return m_major;
}
int minor()
{
return m_minor;
}
int security()
{
return m_security;
}
int major()
{
return m_major;
}
int minor()
{
return m_minor;
}
int security()
{
return m_security;
}
private:
QString m_string;
int m_major = 0;
int m_minor = 0;
int m_security = 0;
bool m_parseable = false;
QString m_prerelease;
QString m_string;
int m_major = 0;
int m_minor = 0;
int m_security = 0;
bool m_parseable = false;
QString m_prerelease;
};

View File

@ -5,110 +5,110 @@
class JavaVersionTest : public QObject
{
Q_OBJECT
Q_OBJECT
private
slots:
void test_Parse_data()
{
QTest::addColumn<QString>("string");
QTest::addColumn<int>("major");
QTest::addColumn<int>("minor");
QTest::addColumn<int>("security");
QTest::addColumn<QString>("prerelease");
void test_Parse_data()
{
QTest::addColumn<QString>("string");
QTest::addColumn<int>("major");
QTest::addColumn<int>("minor");
QTest::addColumn<int>("security");
QTest::addColumn<QString>("prerelease");
QTest::newRow("old format") << "1.6.0_33" << 6 << 0 << 33 << QString();
QTest::newRow("old format prerelease") << "1.9.0_1-ea" << 9 << 0 << 1 << "ea";
QTest::newRow("old format") << "1.6.0_33" << 6 << 0 << 33 << QString();
QTest::newRow("old format prerelease") << "1.9.0_1-ea" << 9 << 0 << 1 << "ea";
QTest::newRow("new format major") << "9" << 9 << 0 << 0 << QString();
QTest::newRow("new format minor") << "9.1" << 9 << 1 << 0 << QString();
QTest::newRow("new format security") << "9.0.1" << 9 << 0 << 1 << QString();
QTest::newRow("new format prerelease") << "9-ea" << 9 << 0 << 0 << "ea";
QTest::newRow("new format long prerelease") << "9.0.1-ea" << 9 << 0 << 1 << "ea";
}
void test_Parse()
{
QFETCH(QString, string);
QFETCH(int, major);
QFETCH(int, minor);
QFETCH(int, security);
QFETCH(QString, prerelease);
QTest::newRow("new format major") << "9" << 9 << 0 << 0 << QString();
QTest::newRow("new format minor") << "9.1" << 9 << 1 << 0 << QString();
QTest::newRow("new format security") << "9.0.1" << 9 << 0 << 1 << QString();
QTest::newRow("new format prerelease") << "9-ea" << 9 << 0 << 0 << "ea";
QTest::newRow("new format long prerelease") << "9.0.1-ea" << 9 << 0 << 1 << "ea";
}
void test_Parse()
{
QFETCH(QString, string);
QFETCH(int, major);
QFETCH(int, minor);
QFETCH(int, security);
QFETCH(QString, prerelease);
JavaVersion test(string);
QCOMPARE(test.m_string, string);
QCOMPARE(test.toString(), string);
QCOMPARE(test.m_major, major);
QCOMPARE(test.m_minor, minor);
QCOMPARE(test.m_security, security);
QCOMPARE(test.m_prerelease, prerelease);
}
JavaVersion test(string);
QCOMPARE(test.m_string, string);
QCOMPARE(test.toString(), string);
QCOMPARE(test.m_major, major);
QCOMPARE(test.m_minor, minor);
QCOMPARE(test.m_security, security);
QCOMPARE(test.m_prerelease, prerelease);
}
void test_Sort_data()
{
QTest::addColumn<QString>("lhs");
QTest::addColumn<QString>("rhs");
QTest::addColumn<bool>("smaller");
QTest::addColumn<bool>("equal");
QTest::addColumn<bool>("bigger");
void test_Sort_data()
{
QTest::addColumn<QString>("lhs");
QTest::addColumn<QString>("rhs");
QTest::addColumn<bool>("smaller");
QTest::addColumn<bool>("equal");
QTest::addColumn<bool>("bigger");
// old format and new format equivalence
QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false;
// old format major version
QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false;
// new format - first release vs first security patch
QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false;
QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true;
// new format - first minor vs first release/security patch
QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true;
QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false;
QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true;
QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false;
// new format - omitted numbers
QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false;
QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false;
QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false;
// early access and prereleases compared to final release
QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false;
QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false;
QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true;
// prerelease difference only testing
QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false;
QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false;
QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false;
QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false;
QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false;
QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false;
QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false;
}
void test_Sort()
{
QFETCH(QString, lhs);
QFETCH(QString, rhs);
QFETCH(bool, smaller);
QFETCH(bool, equal);
QFETCH(bool, bigger);
JavaVersion lver(lhs);
JavaVersion rver(rhs);
QCOMPARE(lver < rver, smaller);
QCOMPARE(lver == rver, equal);
QCOMPARE(lver > rver, bigger);
}
void test_PermGen_data()
{
QTest::addColumn<QString>("version");
QTest::addColumn<bool>("needs_permgen");
QTest::newRow("1.6.0_33") << "1.6.0_33" << true;
QTest::newRow("1.7.0_60") << "1.7.0_60" << true;
QTest::newRow("1.8.0_22") << "1.8.0_22" << false;
QTest::newRow("9-ea") << "9-ea" << false;
QTest::newRow("9.2.4") << "9.2.4" << false;
}
void test_PermGen()
{
QFETCH(QString, version);
QFETCH(bool, needs_permgen);
JavaVersion v(version);
QCOMPARE(needs_permgen, v.requiresPermGen());
}
// old format and new format equivalence
QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false;
// old format major version
QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false;
// new format - first release vs first security patch
QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false;
QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true;
// new format - first minor vs first release/security patch
QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true;
QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false;
QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true;
QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false;
// new format - omitted numbers
QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false;
QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false;
QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false;
// early access and prereleases compared to final release
QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false;
QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false;
QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true;
// prerelease difference only testing
QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false;
QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false;
QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false;
QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false;
QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false;
QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false;
QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false;
}
void test_Sort()
{
QFETCH(QString, lhs);
QFETCH(QString, rhs);
QFETCH(bool, smaller);
QFETCH(bool, equal);
QFETCH(bool, bigger);
JavaVersion lver(lhs);
JavaVersion rver(rhs);
QCOMPARE(lver < rver, smaller);
QCOMPARE(lver == rver, equal);
QCOMPARE(lver > rver, bigger);
}
void test_PermGen_data()
{
QTest::addColumn<QString>("version");
QTest::addColumn<bool>("needs_permgen");
QTest::newRow("1.6.0_33") << "1.6.0_33" << true;
QTest::newRow("1.7.0_60") << "1.7.0_60" << true;
QTest::newRow("1.8.0_22") << "1.8.0_22" << false;
QTest::newRow("9-ea") << "9-ea" << false;
QTest::newRow("9.2.4") << "9.2.4" << false;
}
void test_PermGen()
{
QFETCH(QString, version);
QFETCH(bool, needs_permgen);
JavaVersion v(version);
QCOMPARE(needs_permgen, v.requiresPermGen());
}
};
QTEST_GUILESS_MAIN(JavaVersionTest)

View File

@ -22,115 +22,115 @@
void CheckJava::executeTask()
{
auto instance = m_parent->instance();
auto settings = instance->settings();
m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
auto instance = m_parent->instance();
auto settings = instance->settings();
m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
if (realJavaPath.isEmpty())
{
if (perInstance)
{
emit logLine(
tr("The java binary \"%1\" couldn't be found. Please fix the java path "
"override in the instance's settings or disable it.").arg(m_javaPath),
MessageLevel::Warning);
}
else
{
emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in "
"the settings.").arg(m_javaPath),
MessageLevel::Warning);
}
emitFailed(tr("Java path is not valid."));
return;
}
else
{
emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC);
}
auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
if (realJavaPath.isEmpty())
{
if (perInstance)
{
emit logLine(
tr("The java binary \"%1\" couldn't be found. Please fix the java path "
"override in the instance's settings or disable it.").arg(m_javaPath),
MessageLevel::Warning);
}
else
{
emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in "
"the settings.").arg(m_javaPath),
MessageLevel::Warning);
}
emitFailed(tr("Java path is not valid."));
return;
}
else
{
emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC);
}
QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString();
m_javaUnixTime = javaUnixTime;
// if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
{
m_JavaChecker = std::make_shared<JavaChecker>();
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath;
m_JavaChecker->performCheck();
return;
}
else
{
auto verString = instance->settings()->get("JavaVersion").toString();
auto archString = instance->settings()->get("JavaArchitecture").toString();
printJavaInfo(verString, archString);
}
emitSucceeded();
QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString();
m_javaUnixTime = javaUnixTime;
// if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
{
m_JavaChecker = std::make_shared<JavaChecker>();
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath;
m_JavaChecker->performCheck();
return;
}
else
{
auto verString = instance->settings()->get("JavaVersion").toString();
auto archString = instance->settings()->get("JavaArchitecture").toString();
printJavaInfo(verString, archString);
}
emitSucceeded();
}
void CheckJava::checkJavaFinished(JavaCheckResult result)
{
switch (result.validity)
{
case JavaCheckResult::Validity::Errored:
{
// Error message displayed if java can't start
emit logLine(tr("Could not start java:"), MessageLevel::Error);
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
printSystemInfo(false, false);
emitFailed(tr("Could not start java!"));
return;
}
case JavaCheckResult::Validity::ReturnedInvalidData:
{
emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC);
printSystemInfo(false, false);
emitSucceeded();
return;
}
case JavaCheckResult::Validity::Valid:
{
auto instance = m_parent->instance();
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
instance->settings()->set("JavaVersion", result.javaVersion.toString());
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaTimestamp", m_javaUnixTime);
emitSucceeded();
return;
}
}
switch (result.validity)
{
case JavaCheckResult::Validity::Errored:
{
// Error message displayed if java can't start
emit logLine(tr("Could not start java:"), MessageLevel::Error);
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
printSystemInfo(false, false);
emitFailed(tr("Could not start java!"));
return;
}
case JavaCheckResult::Validity::ReturnedInvalidData:
{
emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC);
printSystemInfo(false, false);
emitSucceeded();
return;
}
case JavaCheckResult::Validity::Valid:
{
auto instance = m_parent->instance();
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
instance->settings()->set("JavaVersion", result.javaVersion.toString());
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaTimestamp", m_javaUnixTime);
emitSucceeded();
return;
}
}
}
void CheckJava::printJavaInfo(const QString& version, const QString& architecture)
{
emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
printSystemInfo(true, architecture == "64");
emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
printSystemInfo(true, architecture == "64");
}
void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)
{
auto cpu64 = Sys::isCPU64bit();
auto system64 = Sys::isSystem64bit();
if(cpu64 != system64)
{
emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
}
if(javaIsKnown)
{
if(javaIs64bit != system64)
{
emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
}
}
auto cpu64 = Sys::isCPU64bit();
auto system64 = Sys::isSystem64bit();
if(cpu64 != system64)
{
emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
}
if(javaIsKnown)
{
if(javaIs64bit != system64)
{
emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
}
}
}

View File

@ -21,25 +21,25 @@
class CheckJava: public LaunchStep
{
Q_OBJECT
Q_OBJECT
public:
explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){};
virtual ~CheckJava() {};
explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){};
virtual ~CheckJava() {};
virtual void executeTask();
virtual bool canAbort() const
{
return false;
}
virtual void executeTask();
virtual bool canAbort() const
{
return false;
}
private slots:
void checkJavaFinished(JavaCheckResult result);
void checkJavaFinished(JavaCheckResult result);
private:
void printJavaInfo(const QString & version, const QString & architecture);
void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
void printJavaInfo(const QString & version, const QString & architecture);
void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
private:
QString m_javaPath;
qlonglong m_javaUnixTime;
JavaCheckerPtr m_JavaChecker;
QString m_javaPath;
qlonglong m_javaUnixTime;
JavaCheckerPtr m_JavaChecker;
};

View File

@ -18,10 +18,10 @@
void LaunchStep::bind(LaunchTask *parent)
{
m_parent = parent;
connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished);
connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested);
m_parent = parent;
connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished);
connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested);
}

View File

@ -23,28 +23,28 @@
class LaunchTask;
class LaunchStep: public Task
{
Q_OBJECT
Q_OBJECT
public: /* methods */
explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent)
{
bind(parent);
};
virtual ~LaunchStep() {};
explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent)
{
bind(parent);
};
virtual ~LaunchStep() {};
protected: /* methods */
virtual void bind(LaunchTask *parent);
virtual void bind(LaunchTask *parent);
signals:
void logLines(QStringList lines, MessageLevel::Enum level);
void logLine(QString line, MessageLevel::Enum level);
void readyForLaunch();
void progressReportingRequest();
void logLines(QStringList lines, MessageLevel::Enum level);
void logLine(QString line, MessageLevel::Enum level);
void readyForLaunch();
void progressReportingRequest();
public slots:
virtual void proceed() {};
// called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends
virtual void finalize() {};
virtual void proceed() {};
// called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends
virtual void finalize() {};
protected: /* data */
LaunchTask *m_parent;
LaunchTask *m_parent;
};

View File

@ -30,14 +30,14 @@
void LaunchTask::init()
{
m_instance->setRunning(true);
m_instance->setRunning(true);
}
std::shared_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
{
std::shared_ptr<LaunchTask> proc(new LaunchTask(inst));
proc->init();
return proc;
std::shared_ptr<LaunchTask> proc(new LaunchTask(inst));
proc->init();
return proc;
}
LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
@ -46,235 +46,235 @@ LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
void LaunchTask::appendStep(std::shared_ptr<LaunchStep> step)
{
m_steps.append(step);
m_steps.append(step);
}
void LaunchTask::prependStep(std::shared_ptr<LaunchStep> step)
{
m_steps.prepend(step);
m_steps.prepend(step);
}
void LaunchTask::executeTask()
{
m_instance->setCrashed(false);
if(!m_steps.size())
{
state = LaunchTask::Finished;
emitSucceeded();
}
state = LaunchTask::Running;
onStepFinished();
m_instance->setCrashed(false);
if(!m_steps.size())
{
state = LaunchTask::Finished;
emitSucceeded();
}
state = LaunchTask::Running;
onStepFinished();
}
void LaunchTask::onReadyForLaunch()
{
state = LaunchTask::Waiting;
emit readyForLaunch();
state = LaunchTask::Waiting;
emit readyForLaunch();
}
void LaunchTask::onStepFinished()
{
// initial -> just start the first step
if(currentStep == -1)
{
currentStep ++;
m_steps[currentStep]->start();
return;
}
// initial -> just start the first step
if(currentStep == -1)
{
currentStep ++;
m_steps[currentStep]->start();
return;
}
auto step = m_steps[currentStep];
if(step->wasSuccessful())
{
// end?
if(currentStep == m_steps.size() - 1)
{
finalizeSteps(true, QString());
}
else
{
currentStep ++;
step = m_steps[currentStep];
step->start();
}
}
else
{
finalizeSteps(false, step->failReason());
}
auto step = m_steps[currentStep];
if(step->wasSuccessful())
{
// end?
if(currentStep == m_steps.size() - 1)
{
finalizeSteps(true, QString());
}
else
{
currentStep ++;
step = m_steps[currentStep];
step->start();
}
}
else
{
finalizeSteps(false, step->failReason());
}
}
void LaunchTask::finalizeSteps(bool successful, const QString& error)
{
for(auto step = currentStep; step >= 0; step--)
{
m_steps[step]->finalize();
}
if(successful)
{
emitSucceeded();
}
else
{
emitFailed(error);
}
for(auto step = currentStep; step >= 0; step--)
{
m_steps[step]->finalize();
}
if(successful)
{
emitSucceeded();
}
else
{
emitFailed(error);
}
}
void LaunchTask::onProgressReportingRequested()
{
state = LaunchTask::Waiting;
emit requestProgress(m_steps[currentStep].get());
state = LaunchTask::Waiting;
emit requestProgress(m_steps[currentStep].get());
}
void LaunchTask::setCensorFilter(QMap<QString, QString> filter)
{
m_censorFilter = filter;
m_censorFilter = filter;
}
QString LaunchTask::censorPrivateInfo(QString in)
{
auto iter = m_censorFilter.begin();
while (iter != m_censorFilter.end())
{
in.replace(iter.key(), iter.value());
iter++;
}
return in;
auto iter = m_censorFilter.begin();
while (iter != m_censorFilter.end())
{
in.replace(iter.key(), iter.value());
iter++;
}
return in;
}
void LaunchTask::proceed()
{
if(state != LaunchTask::Waiting)
{
return;
}
m_steps[currentStep]->proceed();
if(state != LaunchTask::Waiting)
{
return;
}
m_steps[currentStep]->proceed();
}
bool LaunchTask::canAbort() const
{
switch(state)
{
case LaunchTask::Aborted:
case LaunchTask::Failed:
case LaunchTask::Finished:
return false;
case LaunchTask::NotStarted:
return true;
case LaunchTask::Running:
case LaunchTask::Waiting:
{
auto step = m_steps[currentStep];
return step->canAbort();
}
}
return false;
switch(state)
{
case LaunchTask::Aborted:
case LaunchTask::Failed:
case LaunchTask::Finished:
return false;
case LaunchTask::NotStarted:
return true;
case LaunchTask::Running:
case LaunchTask::Waiting:
{
auto step = m_steps[currentStep];
return step->canAbort();
}
}
return false;
}
bool LaunchTask::abort()
{
switch(state)
{
case LaunchTask::Aborted:
case LaunchTask::Failed:
case LaunchTask::Finished:
return true;
case LaunchTask::NotStarted:
{
state = LaunchTask::Aborted;
emitFailed("Aborted");
return true;
}
case LaunchTask::Running:
case LaunchTask::Waiting:
{
auto step = m_steps[currentStep];
if(!step->canAbort())
{
return false;
}
if(step->abort())
{
state = LaunchTask::Aborted;
return true;
}
}
default:
break;
}
return false;
switch(state)
{
case LaunchTask::Aborted:
case LaunchTask::Failed:
case LaunchTask::Finished:
return true;
case LaunchTask::NotStarted:
{
state = LaunchTask::Aborted;
emitFailed("Aborted");
return true;
}
case LaunchTask::Running:
case LaunchTask::Waiting:
{
auto step = m_steps[currentStep];
if(!step->canAbort())
{
return false;
}
if(step->abort())
{
state = LaunchTask::Aborted;
return true;
}
}
default:
break;
}
return false;
}
shared_qobject_ptr<LogModel> LaunchTask::getLogModel()
{
if(!m_logModel)
{
m_logModel.reset(new LogModel());
m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
// FIXME: should this really be here?
m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
"You may have to fix your mods because the game is still logging to files and"
" likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
}
return m_logModel;
if(!m_logModel)
{
m_logModel.reset(new LogModel());
m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
// FIXME: should this really be here?
m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
"You may have to fix your mods because the game is still logging to files and"
" likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
}
return m_logModel;
}
void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum defaultLevel)
{
for (auto & line: lines)
{
onLogLine(line, defaultLevel);
}
for (auto & line: lines)
{
onLogLine(line, defaultLevel);
}
}
void LaunchTask::onLogLine(QString line, MessageLevel::Enum level)
{
// if the launcher part set a log level, use it
auto innerLevel = MessageLevel::fromLine(line);
if(innerLevel != MessageLevel::Unknown)
{
level = innerLevel;
}
// if the launcher part set a log level, use it
auto innerLevel = MessageLevel::fromLine(line);
if(innerLevel != MessageLevel::Unknown)
{
level = innerLevel;
}
// If the level is still undetermined, guess level
if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown)
{
level = m_instance->guessLevel(line, level);
}
// If the level is still undetermined, guess level
if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown)
{
level = m_instance->guessLevel(line, level);
}
// censor private user info
line = censorPrivateInfo(line);
// censor private user info
line = censorPrivateInfo(line);
auto &model = *getLogModel();
model.append(level, line);
auto &model = *getLogModel();
model.append(level, line);
}
void LaunchTask::emitSucceeded()
{
m_instance->setRunning(false);
Task::emitSucceeded();
m_instance->setRunning(false);
Task::emitSucceeded();
}
void LaunchTask::emitFailed(QString reason)
{
m_instance->setRunning(false);
m_instance->setCrashed(true);
Task::emitFailed(reason);
m_instance->setRunning(false);
m_instance->setCrashed(true);
Task::emitFailed(reason);
}
QString LaunchTask::substituteVariables(const QString &cmd) const
{
QString out = cmd;
auto variables = m_instance->getVariables();
for (auto it = variables.begin(); it != variables.end(); ++it)
{
out.replace("$" + it.key(), it.value());
}
auto env = QProcessEnvironment::systemEnvironment();
for (auto var : env.keys())
{
out.replace("$" + var, env.value(var));
}
return out;
QString out = cmd;
auto variables = m_instance->getVariables();
for (auto it = variables.begin(); it != variables.end(); ++it)
{
out.replace("$" + it.key(), it.value());
}
auto env = QProcessEnvironment::systemEnvironment();
for (auto var : env.keys())
{
out.replace("$" + var, env.value(var));
}
return out;
}

View File

@ -6,7 +6,7 @@
* 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
* 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,
@ -28,98 +28,98 @@
class MULTIMC_LOGIC_EXPORT LaunchTask: public Task
{
Q_OBJECT
Q_OBJECT
protected:
explicit LaunchTask(InstancePtr instance);
void init();
explicit LaunchTask(InstancePtr instance);
void init();
public:
enum State
{
NotStarted,
Running,
Waiting,
Failed,
Aborted,
Finished
};
enum State
{
NotStarted,
Running,
Waiting,
Failed,
Aborted,
Finished
};
public: /* methods */
static std::shared_ptr<LaunchTask> create(InstancePtr inst);
virtual ~LaunchTask() {};
static std::shared_ptr<LaunchTask> create(InstancePtr inst);
virtual ~LaunchTask() {};
void appendStep(std::shared_ptr<LaunchStep> step);
void prependStep(std::shared_ptr<LaunchStep> step);
void setCensorFilter(QMap<QString, QString> filter);
void appendStep(std::shared_ptr<LaunchStep> step);
void prependStep(std::shared_ptr<LaunchStep> step);
void setCensorFilter(QMap<QString, QString> filter);
InstancePtr instance()
{
return m_instance;
}
InstancePtr instance()
{
return m_instance;
}
void setPid(qint64 pid)
{
m_pid = pid;
}
void setPid(qint64 pid)
{
m_pid = pid;
}
qint64 pid()
{
return m_pid;
}
qint64 pid()
{
return m_pid;
}
/**
* @brief prepare the process for launch (for multi-stage launch)
*/
virtual void executeTask() override;
/**
* @brief prepare the process for launch (for multi-stage launch)
*/
virtual void executeTask() override;
/**
* @brief launch the armed instance
*/
void proceed();
/**
* @brief launch the armed instance
*/
void proceed();
/**
* @brief abort launch
*/
bool abort() override;
/**
* @brief abort launch
*/
bool abort() override;
bool canAbort() const override;
bool canAbort() const override;
shared_qobject_ptr<LogModel> getLogModel();
shared_qobject_ptr<LogModel> getLogModel();
public:
QString substituteVariables(const QString &cmd) const;
QString censorPrivateInfo(QString in);
QString substituteVariables(const QString &cmd) const;
QString censorPrivateInfo(QString in);
protected: /* methods */
virtual void emitFailed(QString reason) override;
virtual void emitSucceeded() override;
virtual void emitFailed(QString reason) override;
virtual void emitSucceeded() override;
signals:
/**
* @brief emitted when the launch preparations are done
*/
void readyForLaunch();
/**
* @brief emitted when the launch preparations are done
*/
void readyForLaunch();
void requestProgress(Task *task);
void requestProgress(Task *task);
void requestLogging();
void requestLogging();
public slots:
void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onReadyForLaunch();
void onStepFinished();
void onProgressReportingRequested();
void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onReadyForLaunch();
void onStepFinished();
void onProgressReportingRequested();
private: /*methods */
void finalizeSteps(bool successful, const QString & error);
void finalizeSteps(bool successful, const QString & error);
protected: /* data */
InstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel;
QList <std::shared_ptr<LaunchStep>> m_steps;
QMap<QString, QString> m_censorFilter;
int currentStep = -1;
State state = NotStarted;
qint64 m_pid = -1;
InstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel;
QList <std::shared_ptr<LaunchStep>> m_steps;
QMap<QString, QString> m_censorFilter;
int currentStep = -1;
State state = NotStarted;
qint64 m_pid = -1;
};

View File

@ -2,166 +2,166 @@
LogModel::LogModel(QObject *parent):QAbstractListModel(parent)
{
m_content.resize(m_maxLines);
m_content.resize(m_maxLines);
}
int LogModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
if (parent.isValid())
return 0;
return m_numLines;
return m_numLines;
}
QVariant LogModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_numLines)
return QVariant();
if (index.row() < 0 || index.row() >= m_numLines)
return QVariant();
auto row = index.row();
auto realRow = (row + m_firstLine) % m_maxLines;
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
return m_content[realRow].line;
}
if(role == LevelRole)
{
return m_content[realRow].level;
}
auto row = index.row();
auto realRow = (row + m_firstLine) % m_maxLines;
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
return m_content[realRow].line;
}
if(role == LevelRole)
{
return m_content[realRow].level;
}
return QVariant();
return QVariant();
}
void LogModel::append(MessageLevel::Enum level, QString line)
{
if(m_suspended)
{
return;
}
int lineNum = (m_firstLine + m_numLines) % m_maxLines;
// overflow
if(m_numLines == m_maxLines)
{
if(m_stopOnOverflow)
{
// nothing more to do, the buffer is full
return;
}
beginRemoveRows(QModelIndex(), 0, 0);
m_firstLine = (m_firstLine + 1) % m_maxLines;
m_numLines --;
endRemoveRows();
}
else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow)
{
level = MessageLevel::Fatal;
line = m_overflowMessage;
}
beginInsertRows(QModelIndex(), m_numLines, m_numLines);
m_numLines ++;
m_content[lineNum].level = level;
m_content[lineNum].line = line;
endInsertRows();
if(m_suspended)
{
return;
}
int lineNum = (m_firstLine + m_numLines) % m_maxLines;
// overflow
if(m_numLines == m_maxLines)
{
if(m_stopOnOverflow)
{
// nothing more to do, the buffer is full
return;
}
beginRemoveRows(QModelIndex(), 0, 0);
m_firstLine = (m_firstLine + 1) % m_maxLines;
m_numLines --;
endRemoveRows();
}
else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow)
{
level = MessageLevel::Fatal;
line = m_overflowMessage;
}
beginInsertRows(QModelIndex(), m_numLines, m_numLines);
m_numLines ++;
m_content[lineNum].level = level;
m_content[lineNum].line = line;
endInsertRows();
}
void LogModel::suspend(bool suspend)
{
m_suspended = suspend;
m_suspended = suspend;
}
bool LogModel::suspended()
{
return m_suspended;
return m_suspended;
}
void LogModel::clear()
{
beginResetModel();
m_firstLine = 0;
m_numLines = 0;
endResetModel();
beginResetModel();
m_firstLine = 0;
m_numLines = 0;
endResetModel();
}
QString LogModel::toPlainText()
{
QString out;
out.reserve(m_numLines * 80);
for(int i = 0; i < m_numLines; i++)
{
QString & line = m_content[(m_firstLine + i) % m_maxLines].line;
out.append(line + '\n');
}
out.squeeze();
return out;
QString out;
out.reserve(m_numLines * 80);
for(int i = 0; i < m_numLines; i++)
{
QString & line = m_content[(m_firstLine + i) % m_maxLines].line;
out.append(line + '\n');
}
out.squeeze();
return out;
}
void LogModel::setMaxLines(int maxLines)
{
// no-op
if(maxLines == m_maxLines)
{
return;
}
// if it all still fits in the buffer, just resize it
if(m_firstLine + m_numLines < m_maxLines)
{
m_maxLines = maxLines;
m_content.resize(maxLines);
return;
}
// otherwise, we need to reorganize the data because it crosses the wrap boundary
QVector<entry> newContent;
newContent.resize(maxLines);
if(m_numLines <= maxLines)
{
// if it all fits in the new buffer, just copy it over
for(int i = 0; i < m_numLines; i++)
{
newContent[i] = m_content[(m_firstLine + i) % m_maxLines];
}
m_content.swap(newContent);
}
else
{
// if it doesn't fit, part of the data needs to be thrown away (the oldest log messages)
int lead = m_numLines - maxLines;
beginRemoveRows(QModelIndex(), 0, lead - 1);
for(int i = 0; i < maxLines; i++)
{
newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines];
}
m_numLines = m_maxLines;
m_content.swap(newContent);
endRemoveRows();
}
m_firstLine = 0;
m_maxLines = maxLines;
// no-op
if(maxLines == m_maxLines)
{
return;
}
// if it all still fits in the buffer, just resize it
if(m_firstLine + m_numLines < m_maxLines)
{
m_maxLines = maxLines;
m_content.resize(maxLines);
return;
}
// otherwise, we need to reorganize the data because it crosses the wrap boundary
QVector<entry> newContent;
newContent.resize(maxLines);
if(m_numLines <= maxLines)
{
// if it all fits in the new buffer, just copy it over
for(int i = 0; i < m_numLines; i++)
{
newContent[i] = m_content[(m_firstLine + i) % m_maxLines];
}
m_content.swap(newContent);
}
else
{
// if it doesn't fit, part of the data needs to be thrown away (the oldest log messages)
int lead = m_numLines - maxLines;
beginRemoveRows(QModelIndex(), 0, lead - 1);
for(int i = 0; i < maxLines; i++)
{
newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines];
}
m_numLines = m_maxLines;
m_content.swap(newContent);
endRemoveRows();
}
m_firstLine = 0;
m_maxLines = maxLines;
}
int LogModel::getMaxLines()
{
return m_maxLines;
return m_maxLines;
}
void LogModel::setStopOnOverflow(bool stop)
{
m_stopOnOverflow = stop;
m_stopOnOverflow = stop;
}
void LogModel::setOverflowMessage(const QString& overflowMessage)
{
m_overflowMessage = overflowMessage;
m_overflowMessage = overflowMessage;
}
void LogModel::setLineWrap(bool state)
{
if(m_lineWrap != state)
{
m_lineWrap = state;
}
if(m_lineWrap != state)
{
m_lineWrap = state;
}
}
bool LogModel::wrapLines() const
{
return m_lineWrap;
return m_lineWrap;
}

View File

@ -8,53 +8,53 @@
class MULTIMC_LOGIC_EXPORT LogModel : public QAbstractListModel
{
Q_OBJECT
Q_OBJECT
public:
explicit LogModel(QObject *parent = 0);
explicit LogModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
void append(MessageLevel::Enum, QString line);
void clear();
void append(MessageLevel::Enum, QString line);
void clear();
void suspend(bool suspend);
bool suspended();
void suspend(bool suspend);
bool suspended();
QString toPlainText();
QString toPlainText();
int getMaxLines();
void setMaxLines(int maxLines);
void setStopOnOverflow(bool stop);
void setOverflowMessage(const QString & overflowMessage);
int getMaxLines();
void setMaxLines(int maxLines);
void setStopOnOverflow(bool stop);
void setOverflowMessage(const QString & overflowMessage);
void setLineWrap(bool state);
bool wrapLines() const;
void setLineWrap(bool state);
bool wrapLines() const;
enum Roles
{
LevelRole = Qt::UserRole
};
enum Roles
{
LevelRole = Qt::UserRole
};
private /* types */:
struct entry
{
MessageLevel::Enum level;
QString line;
};
struct entry
{
MessageLevel::Enum level;
QString line;
};
private: /* data */
QVector <entry> m_content;
int m_maxLines = 1000;
// first line in the circular buffer
int m_firstLine = 0;
// number of lines occupied in the circular buffer
int m_numLines = 0;
bool m_stopOnOverflow = false;
QString m_overflowMessage = "OVERFLOW";
bool m_suspended = false;
bool m_lineWrap = true;
QVector <entry> m_content;
int m_maxLines = 1000;
// first line in the circular buffer
int m_firstLine = 0;
// number of lines occupied in the circular buffer
int m_numLines = 0;
bool m_stopOnOverflow = false;
QString m_overflowMessage = "OVERFLOW";
bool m_suspended = false;
bool m_lineWrap = true;
private:
Q_DISABLE_COPY(LogModel)
Q_DISABLE_COPY(LogModel)
};

View File

@ -18,67 +18,67 @@
PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{
auto instance = m_parent->instance();
m_command = instance->getPostExitCommand();
m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state);
auto instance = m_parent->instance();
m_command = instance->getPostExitCommand();
m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state);
}
void PostLaunchCommand::executeTask()
{
QString postlaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC);
m_process.start(postlaunch_cmd);
QString postlaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC);
m_process.start(postlaunch_cmd);
}
void PostLaunchCommand::on_state(LoggedProcess::State state)
{
auto getError = [&]()
{
return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
};
switch(state)
{
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart:
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
return;
}
case LoggedProcess::Finished:
{
if(m_process.exitCode() != 0)
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
}
else
{
emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded();
}
}
default:
break;
}
auto getError = [&]()
{
return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
};
switch(state)
{
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart:
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
return;
}
case LoggedProcess::Finished:
{
if(m_process.exitCode() != 0)
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
}
else
{
emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded();
}
}
default:
break;
}
}
void PostLaunchCommand::setWorkingDirectory(const QString &wd)
{
m_process.setWorkingDirectory(wd);
m_process.setWorkingDirectory(wd);
}
bool PostLaunchCommand::abort()
{
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
}

View File

@ -20,22 +20,22 @@
class PostLaunchCommand: public LaunchStep
{
Q_OBJECT
Q_OBJECT
public:
explicit PostLaunchCommand(LaunchTask *parent);
virtual ~PostLaunchCommand() {};
explicit PostLaunchCommand(LaunchTask *parent);
virtual ~PostLaunchCommand() {};
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
private slots:
void on_state(LoggedProcess::State state);
void on_state(LoggedProcess::State state);
private:
LoggedProcess m_process;
QString m_command;
LoggedProcess m_process;
QString m_command;
};

View File

@ -18,68 +18,68 @@
PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{
auto instance = m_parent->instance();
m_command = instance->getPreLaunchCommand();
m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state);
auto instance = m_parent->instance();
m_command = instance->getPreLaunchCommand();
m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state);
}
void PreLaunchCommand::executeTask()
{
//FIXME: where to put this?
QString prelaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC);
m_process.start(prelaunch_cmd);
//FIXME: where to put this?
QString prelaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC);
m_process.start(prelaunch_cmd);
}
void PreLaunchCommand::on_state(LoggedProcess::State state)
{
auto getError = [&]()
{
return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
};
switch(state)
{
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart:
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
return;
}
case LoggedProcess::Finished:
{
if(m_process.exitCode() != 0)
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
}
else
{
emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded();
}
}
default:
break;
}
auto getError = [&]()
{
return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
};
switch(state)
{
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart:
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
return;
}
case LoggedProcess::Finished:
{
if(m_process.exitCode() != 0)
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
}
else
{
emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded();
}
}
default:
break;
}
}
void PreLaunchCommand::setWorkingDirectory(const QString &wd)
{
m_process.setWorkingDirectory(wd);
m_process.setWorkingDirectory(wd);
}
bool PreLaunchCommand::abort()
{
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
}

View File

@ -20,22 +20,22 @@
class PreLaunchCommand: public LaunchStep
{
Q_OBJECT
Q_OBJECT
public:
explicit PreLaunchCommand(LaunchTask *parent);
virtual ~PreLaunchCommand() {};
explicit PreLaunchCommand(LaunchTask *parent);
virtual ~PreLaunchCommand() {};
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
private slots:
void on_state(LoggedProcess::State state);
void on_state(LoggedProcess::State state);
private:
LoggedProcess m_process;
QString m_command;
LoggedProcess m_process;
QString m_command;
};

View File

@ -2,28 +2,28 @@
TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent)
{
m_lines = lines;
m_level = level;
m_lines = lines;
m_level = level;
}
TextPrint::TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level) : LaunchStep(parent)
{
m_lines.append(line);
m_level = level;
m_lines.append(line);
m_level = level;
}
void TextPrint::executeTask()
{
emit logLines(m_lines, m_level);
emitSucceeded();
emit logLines(m_lines, m_level);
emitSucceeded();
}
bool TextPrint::canAbort() const
{
return true;
return true;
}
bool TextPrint::abort()
{
emitFailed("Aborted.");
return true;
emitFailed("Aborted.");
return true;
}

View File

@ -27,17 +27,17 @@
class MULTIMC_LOGIC_EXPORT TextPrint: public LaunchStep
{
Q_OBJECT
Q_OBJECT
public:
explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level);
explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level);
virtual ~TextPrint(){};
explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level);
explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level);
virtual ~TextPrint(){};
virtual void executeTask();
virtual bool canAbort() const;
virtual bool abort();
virtual void executeTask();
virtual bool canAbort() const;
virtual bool abort();
private:
QStringList m_lines;
MessageLevel::Enum m_level;
QStringList m_lines;
MessageLevel::Enum m_level;
};

View File

@ -18,63 +18,63 @@
void Update::executeTask()
{
if(m_aborted)
{
emitFailed(tr("Task aborted."));
return;
}
m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
if(m_updateTask)
{
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress);
connect(m_updateTask.get(), &Task::status, this, &Task::setStatus);
emit progressReportingRequest();
return;
}
emitSucceeded();
if(m_aborted)
{
emitFailed(tr("Task aborted."));
return;
}
m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
if(m_updateTask)
{
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress);
connect(m_updateTask.get(), &Task::status, this, &Task::setStatus);
emit progressReportingRequest();
return;
}
emitSucceeded();
}
void Update::proceed()
{
m_updateTask->start();
m_updateTask->start();
}
void Update::updateFinished()
{
if(m_updateTask->wasSuccessful())
{
m_updateTask.reset();
emitSucceeded();
}
else
{
QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason());
m_updateTask.reset();
emit logLine(reason, MessageLevel::Fatal);
emitFailed(reason);
}
if(m_updateTask->wasSuccessful())
{
m_updateTask.reset();
emitSucceeded();
}
else
{
QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason());
m_updateTask.reset();
emit logLine(reason, MessageLevel::Fatal);
emitFailed(reason);
}
}
bool Update::canAbort() const
{
if(m_updateTask)
{
return m_updateTask->canAbort();
}
return true;
if(m_updateTask)
{
return m_updateTask->canAbort();
}
return true;
}
bool Update::abort()
{
m_aborted = true;
if(m_updateTask)
{
if(m_updateTask->canAbort())
{
return m_updateTask->abort();
}
}
return true;
m_aborted = true;
if(m_updateTask)
{
if(m_updateTask->canAbort())
{
return m_updateTask->abort();
}
}
return true;
}

View File

@ -24,22 +24,22 @@
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
class Update: public LaunchStep
{
Q_OBJECT
Q_OBJECT
public:
explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
virtual ~Update() {};
explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
virtual ~Update() {};
void executeTask() override;
bool canAbort() const override;
void proceed() override;
void executeTask() override;
bool canAbort() const override;
void proceed() override;
public slots:
bool abort() override;
bool abort() override;
private slots:
void updateFinished();
void updateFinished();
private:
shared_qobject_ptr<Task> m_updateTask;
bool m_aborted = false;
Net::Mode m_mode = Net::Mode::Offline;
shared_qobject_ptr<Task> m_updateTask;
bool m_aborted = false;
Net::Mode m_mode = Net::Mode::Offline;
};

View File

@ -27,45 +27,45 @@
class ParsingValidator : public Net::Validator
{
public: /* con/des */
ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity)
{
};
virtual ~ParsingValidator()
{
};
ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity)
{
};
virtual ~ParsingValidator()
{
};
public: /* methods */
bool init(QNetworkRequest &) override
{
return true;
}
bool write(QByteArray & data) override
{
this->data.append(data);
return true;
}
bool abort() override
{
return true;
}
bool validate(QNetworkReply &) override
{
auto fname = m_entity->localFilename();
try
{
m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
return true;
}
catch (const Exception &e)
{
qWarning() << "Unable to parse response:" << e.cause();
return false;
}
}
bool init(QNetworkRequest &) override
{
return true;
}
bool write(QByteArray & data) override
{
this->data.append(data);
return true;
}
bool abort() override
{
return true;
}
bool validate(QNetworkReply &) override
{
auto fname = m_entity->localFilename();
try
{
m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
return true;
}
catch (const Exception &e)
{
qWarning() << "Unable to parse response:" << e.cause();
return false;
}
}
private: /* data */
QByteArray data;
Meta::BaseEntity *m_entity;
QByteArray data;
Meta::BaseEntity *m_entity;
};
Meta::BaseEntity::~BaseEntity()
@ -74,89 +74,89 @@ Meta::BaseEntity::~BaseEntity()
QUrl Meta::BaseEntity::url() const
{
return QUrl("https://v1.meta.multimc.org").resolved(localFilename());
return QUrl("https://v1.meta.multimc.org").resolved(localFilename());
}
bool Meta::BaseEntity::loadLocalFile()
{
const QString fname = QDir("meta").absoluteFilePath(localFilename());
if (!QFile::exists(fname))
{
return false;
}
// TODO: check if the file has the expected checksum
try
{
parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
return true;
}
catch (const Exception &e)
{
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
// just make sure it's gone and we never consider it again.
QFile::remove(fname);
return false;
}
const QString fname = QDir("meta").absoluteFilePath(localFilename());
if (!QFile::exists(fname))
{
return false;
}
// TODO: check if the file has the expected checksum
try
{
parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
return true;
}
catch (const Exception &e)
{
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
// just make sure it's gone and we never consider it again.
QFile::remove(fname);
return false;
}
}
void Meta::BaseEntity::load(Net::Mode loadType)
{
// load local file if nothing is loaded yet
if(!isLoaded())
{
if(loadLocalFile())
{
m_loadStatus = LoadStatus::Local;
}
}
// if we need remote update, run the update task
if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate())
{
return;
}
NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()));
auto url = this->url();
auto entry = ENV.metacache()->resolveEntry("meta", localFilename());
entry->setStale(true);
auto dl = Net::Download::makeCached(url, entry);
/*
* The validator parses the file and loads it into the object.
* If that fails, the file is not written to storage.
*/
dl->addValidator(new ParsingValidator(this));
job->addNetAction(dl);
m_updateStatus = UpdateStatus::InProgress;
m_updateTask.reset(job);
QObject::connect(job, &NetJob::succeeded, [&]()
{
m_loadStatus = LoadStatus::Remote;
m_updateStatus = UpdateStatus::Succeeded;
m_updateTask.reset();
});
QObject::connect(job, &NetJob::failed, [&]()
{
m_updateStatus = UpdateStatus::Failed;
m_updateTask.reset();
});
m_updateTask->start();
// load local file if nothing is loaded yet
if(!isLoaded())
{
if(loadLocalFile())
{
m_loadStatus = LoadStatus::Local;
}
}
// if we need remote update, run the update task
if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate())
{
return;
}
NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()));
auto url = this->url();
auto entry = ENV.metacache()->resolveEntry("meta", localFilename());
entry->setStale(true);
auto dl = Net::Download::makeCached(url, entry);
/*
* The validator parses the file and loads it into the object.
* If that fails, the file is not written to storage.
*/
dl->addValidator(new ParsingValidator(this));
job->addNetAction(dl);
m_updateStatus = UpdateStatus::InProgress;
m_updateTask.reset(job);
QObject::connect(job, &NetJob::succeeded, [&]()
{
m_loadStatus = LoadStatus::Remote;
m_updateStatus = UpdateStatus::Succeeded;
m_updateTask.reset();
});
QObject::connect(job, &NetJob::failed, [&]()
{
m_updateStatus = UpdateStatus::Failed;
m_updateTask.reset();
});
m_updateTask->start();
}
bool Meta::BaseEntity::isLoaded() const
{
return m_loadStatus > LoadStatus::NotLoaded;
return m_loadStatus > LoadStatus::NotLoaded;
}
bool Meta::BaseEntity::shouldStartRemoteUpdate() const
{
// TODO: version-locks and offline mode?
return m_updateStatus != UpdateStatus::InProgress;
// TODO: version-locks and offline mode?
return m_updateStatus != UpdateStatus::InProgress;
}
shared_qobject_ptr<Task> Meta::BaseEntity::getCurrentTask()
{
if(m_updateStatus == UpdateStatus::InProgress)
{
return m_updateTask;
}
return nullptr;
if(m_updateStatus == UpdateStatus::InProgress)
{
return m_updateTask;
}
return nullptr;
}

View File

@ -28,41 +28,41 @@ namespace Meta
class MULTIMC_LOGIC_EXPORT BaseEntity
{
public: /* types */
using Ptr = std::shared_ptr<BaseEntity>;
enum class LoadStatus
{
NotLoaded,
Local,
Remote
};
enum class UpdateStatus
{
NotDone,
InProgress,
Failed,
Succeeded
};
using Ptr = std::shared_ptr<BaseEntity>;
enum class LoadStatus
{
NotLoaded,
Local,
Remote
};
enum class UpdateStatus
{
NotDone,
InProgress,
Failed,
Succeeded
};
public:
virtual ~BaseEntity();
virtual ~BaseEntity();
virtual void parse(const QJsonObject &obj) = 0;
virtual void parse(const QJsonObject &obj) = 0;
virtual QString localFilename() const = 0;
virtual QUrl url() const;
virtual QString localFilename() const = 0;
virtual QUrl url() const;
bool isLoaded() const;
bool shouldStartRemoteUpdate() const;
bool isLoaded() const;
bool shouldStartRemoteUpdate() const;
void load(Net::Mode loadType);
shared_qobject_ptr<Task> getCurrentTask();
void load(Net::Mode loadType);
shared_qobject_ptr<Task> getCurrentTask();
protected: /* methods */
bool loadLocalFile();
bool loadLocalFile();
private:
LoadStatus m_loadStatus = LoadStatus::NotLoaded;
UpdateStatus m_updateStatus = UpdateStatus::NotDone;
shared_qobject_ptr<Task> m_updateTask;
LoadStatus m_loadStatus = LoadStatus::NotLoaded;
UpdateStatus m_updateStatus = UpdateStatus::NotDone;
shared_qobject_ptr<Task> m_updateTask;
};
}

View File

@ -21,128 +21,128 @@
namespace Meta
{
Index::Index(QObject *parent)
: QAbstractListModel(parent)
: QAbstractListModel(parent)
{
}
Index::Index(const QVector<VersionListPtr> &lists, QObject *parent)
: QAbstractListModel(parent), m_lists(lists)
: QAbstractListModel(parent), m_lists(lists)
{
for (int i = 0; i < m_lists.size(); ++i)
{
m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i));
connectVersionList(i, m_lists.at(i));
}
for (int i = 0; i < m_lists.size(); ++i)
{
m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i));
connectVersionList(i, m_lists.at(i));
}
}
QVariant Index::data(const QModelIndex &index, int role) const
{
if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size())
{
return QVariant();
}
if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size())
{
return QVariant();
}
VersionListPtr list = m_lists.at(index.row());
switch (role)
{
case Qt::DisplayRole:
switch (index.column())
{
case 0: return list->humanReadable();
default: break;
}
case UidRole: return list->uid();
case NameRole: return list->name();
case ListPtrRole: return QVariant::fromValue(list);
}
return QVariant();
VersionListPtr list = m_lists.at(index.row());
switch (role)
{
case Qt::DisplayRole:
switch (index.column())
{
case 0: return list->humanReadable();
default: break;
}
case UidRole: return list->uid();
case NameRole: return list->name();
case ListPtrRole: return QVariant::fromValue(list);
}
return QVariant();
}
int Index::rowCount(const QModelIndex &parent) const
{
return m_lists.size();
return m_lists.size();
}
int Index::columnCount(const QModelIndex &parent) const
{
return 1;
return 1;
}
QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0)
{
return tr("Name");
}
else
{
return QVariant();
}
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0)
{
return tr("Name");
}
else
{
return QVariant();
}
}
bool Index::hasUid(const QString &uid) const
{
return m_uids.contains(uid);
return m_uids.contains(uid);
}
VersionListPtr Index::get(const QString &uid)
{
VersionListPtr out = m_uids.value(uid, nullptr);
if(!out)
{
out = std::make_shared<VersionList>(uid);
m_uids[uid] = out;
}
return out;
VersionListPtr out = m_uids.value(uid, nullptr);
if(!out)
{
out = std::make_shared<VersionList>(uid);
m_uids[uid] = out;
}
return out;
}
VersionPtr Index::get(const QString &uid, const QString &version)
{
auto list = get(uid);
return list->getVersion(version);
auto list = get(uid);
return list->getVersion(version);
}
void Index::parse(const QJsonObject& obj)
{
parseIndex(obj, this);
parseIndex(obj, this);
}
void Index::merge(const std::shared_ptr<Index> &other)
{
const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
// initial load, no need to merge
if (m_lists.isEmpty())
{
beginResetModel();
m_lists = lists;
for (int i = 0; i < lists.size(); ++i)
{
m_uids.insert(lists.at(i)->uid(), lists.at(i));
connectVersionList(i, lists.at(i));
}
endResetModel();
}
else
{
for (const VersionListPtr &list : lists)
{
if (m_uids.contains(list->uid()))
{
m_uids[list->uid()]->mergeFromIndex(list);
}
else
{
beginInsertRows(QModelIndex(), m_lists.size(), m_lists.size());
connectVersionList(m_lists.size(), list);
m_lists.append(list);
m_uids.insert(list->uid(), list);
endInsertRows();
}
}
}
const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
// initial load, no need to merge
if (m_lists.isEmpty())
{
beginResetModel();
m_lists = lists;
for (int i = 0; i < lists.size(); ++i)
{
m_uids.insert(lists.at(i)->uid(), lists.at(i));
connectVersionList(i, lists.at(i));
}
endResetModel();
}
else
{
for (const VersionListPtr &list : lists)
{
if (m_uids.contains(list->uid()))
{
m_uids[list->uid()]->mergeFromIndex(list);
}
else
{
beginInsertRows(QModelIndex(), m_lists.size(), m_lists.size());
connectVersionList(m_lists.size(), list);
m_lists.append(list);
m_uids.insert(list->uid(), list);
endInsertRows();
}
}
}
}
void Index::connectVersionList(const int row, const VersionListPtr &list)
{
connect(list.get(), &VersionList::nameChanged, this, [this, row]()
{
emit dataChanged(index(row), index(row), QVector<int>() << Qt::DisplayRole);
});
connect(list.get(), &VersionList::nameChanged, this, [this, row]()
{
emit dataChanged(index(row), index(row), QVector<int>() << Qt::DisplayRole);
});
}
}

View File

@ -31,41 +31,41 @@ using VersionPtr = std::shared_ptr<class Version>;
class MULTIMC_LOGIC_EXPORT Index : public QAbstractListModel, public BaseEntity
{
Q_OBJECT
Q_OBJECT
public:
explicit Index(QObject *parent = nullptr);
explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
explicit Index(QObject *parent = nullptr);
explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
enum
{
UidRole = Qt::UserRole,
NameRole,
ListPtrRole
};
enum
{
UidRole = Qt::UserRole,
NameRole,
ListPtrRole
};
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QString localFilename() const override { return "index.json"; }
QString localFilename() const override { return "index.json"; }
// queries
VersionListPtr get(const QString &uid);
VersionPtr get(const QString &uid, const QString &version);
bool hasUid(const QString &uid) const;
// queries
VersionListPtr get(const QString &uid);
VersionPtr get(const QString &uid, const QString &version);
bool hasUid(const QString &uid) const;
QVector<VersionListPtr> lists() const { return m_lists; }
QVector<VersionListPtr> lists() const { return m_lists; }
public: // for usage by parsers only
void merge(const std::shared_ptr<Index> &other);
void parse(const QJsonObject &obj) override;
void merge(const std::shared_ptr<Index> &other);
void parse(const QJsonObject &obj) override;
private:
QVector<VersionListPtr> m_lists;
QHash<QString, VersionListPtr> m_uids;
QVector<VersionListPtr> m_lists;
QHash<QString, VersionListPtr> m_uids;
void connectVersionList(const int row, const VersionListPtr &list);
void connectVersionList(const int row, const VersionListPtr &list);
};
}

View File

@ -7,36 +7,36 @@
class IndexTest : public QObject
{
Q_OBJECT
Q_OBJECT
private
slots:
void test_isProvidedByEnv()
{
QVERIFY(ENV.metadataIndex());
QCOMPARE(ENV.metadataIndex(), ENV.metadataIndex());
}
void test_isProvidedByEnv()
{
QVERIFY(ENV.metadataIndex());
QCOMPARE(ENV.metadataIndex(), ENV.metadataIndex());
}
void test_hasUid_and_getList()
{
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
QVERIFY(windex.hasUid("list1"));
QVERIFY(!windex.hasUid("asdf"));
QVERIFY(windex.get("list2") != nullptr);
QCOMPARE(windex.get("list2")->uid(), QString("list2"));
QVERIFY(windex.get("adsf") != nullptr);
}
void test_hasUid_and_getList()
{
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
QVERIFY(windex.hasUid("list1"));
QVERIFY(!windex.hasUid("asdf"));
QVERIFY(windex.get("list2") != nullptr);
QCOMPARE(windex.get("list2")->uid(), QString("list2"));
QVERIFY(windex.get("adsf") != nullptr);
}
void test_merge()
{
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")})));
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list4"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list5")})));
QCOMPARE(windex.lists().size(), 5);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list6")})));
QCOMPARE(windex.lists().size(), 6);
}
void test_merge()
{
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")})));
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list4"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list5")})));
QCOMPARE(windex.lists().size(), 5);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list6")})));
QCOMPARE(windex.lists().size(), 6);
}
};
QTEST_GUILESS_MAIN(IndexTest)

View File

@ -30,141 +30,141 @@ namespace Meta
MetadataVersion currentFormatVersion()
{
return MetadataVersion::InitialRelease;
return MetadataVersion::InitialRelease;
}
// Index
static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj)
{
const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
QVector<VersionListPtr> lists;
lists.reserve(objects.size());
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
{
VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid"));
list->setName(ensureString(obj, "name", QString()));
return list;
});
return std::make_shared<Index>(lists);
const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
QVector<VersionListPtr> lists;
lists.reserve(objects.size());
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
{
VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid"));
list->setName(ensureString(obj, "name", QString()));
return list;
});
return std::make_shared<Index>(lists);
}
// Version
static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)
{
VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version"));
version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
version->setType(ensureString(obj, "type", QString()));
version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
RequireSet requires, conflicts;
parseRequires(obj, &requires, "requires");
parseRequires(obj, &conflicts, "conflicts");
version->setRequires(requires, conflicts);
return version;
VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version"));
version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
version->setType(ensureString(obj, "type", QString()));
version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
RequireSet requires, conflicts;
parseRequires(obj, &requires, "requires");
parseRequires(obj, &conflicts, "conflicts");
version->setRequires(requires, conflicts);
return version;
}
static std::shared_ptr<Version> parseVersionInternal(const QJsonObject &obj)
{
VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
QString("%1/%2.json").arg(version->uid(), version->version()),
obj.contains("order")));
return version;
version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
QString("%1/%2.json").arg(version->uid(), version->version()),
obj.contains("order")));
return version;
}
// Version list / package
static std::shared_ptr<VersionList> parseVersionListInternal(const QJsonObject &obj)
{
const QString uid = requireString(obj, "uid");
const QString uid = requireString(obj, "uid");
const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
QVector<VersionPtr> versions;
versions.reserve(versionsRaw.size());
std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)
{
auto version = parseCommonVersion(uid, vObj);
version->setProvidesRecommendations();
return version;
});
const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
QVector<VersionPtr> versions;
versions.reserve(versionsRaw.size());
std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)
{
auto version = parseCommonVersion(uid, vObj);
version->setProvidesRecommendations();
return version;
});
VersionListPtr list = std::make_shared<VersionList>(uid);
list->setName(ensureString(obj, "name", QString()));
list->setVersions(versions);
return list;
VersionListPtr list = std::make_shared<VersionList>(uid);
list->setName(ensureString(obj, "name", QString()));
list->setVersions(versions);
return list;
}
MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required)
{
if (!obj.contains("formatVersion"))
{
if(required)
{
return MetadataVersion::Invalid;
}
return MetadataVersion::InitialRelease;
}
if (!obj.value("formatVersion").isDouble())
{
return MetadataVersion::Invalid;
}
switch(obj.value("formatVersion").toInt())
{
case 0:
case 1:
return MetadataVersion::InitialRelease;
default:
return MetadataVersion::Invalid;
}
if (!obj.contains("formatVersion"))
{
if(required)
{
return MetadataVersion::Invalid;
}
return MetadataVersion::InitialRelease;
}
if (!obj.value("formatVersion").isDouble())
{
return MetadataVersion::Invalid;
}
switch(obj.value("formatVersion").toInt())
{
case 0:
case 1:
return MetadataVersion::InitialRelease;
default:
return MetadataVersion::Invalid;
}
}
void serializeFormatVersion(QJsonObject& obj, Meta::MetadataVersion version)
{
if(version == MetadataVersion::Invalid)
{
return;
}
obj.insert("formatVersion", int(version));
if(version == MetadataVersion::Invalid)
{
return;
}
obj.insert("formatVersion", int(version));
}
void parseIndex(const QJsonObject &obj, Index *ptr)
{
const MetadataVersion version = parseFormatVersion(obj);
switch (version)
{
case MetadataVersion::InitialRelease:
ptr->merge(parseIndexInternal(obj));
break;
case MetadataVersion::Invalid:
throw ParseException(QObject::tr("Unknown format version!"));
}
const MetadataVersion version = parseFormatVersion(obj);
switch (version)
{
case MetadataVersion::InitialRelease:
ptr->merge(parseIndexInternal(obj));
break;
case MetadataVersion::Invalid:
throw ParseException(QObject::tr("Unknown format version!"));
}
}
void parseVersionList(const QJsonObject &obj, VersionList *ptr)
{
const MetadataVersion version = parseFormatVersion(obj);
switch (version)
{
case MetadataVersion::InitialRelease:
ptr->merge(parseVersionListInternal(obj));
break;
case MetadataVersion::Invalid:
throw ParseException(QObject::tr("Unknown format version!"));
}
const MetadataVersion version = parseFormatVersion(obj);
switch (version)
{
case MetadataVersion::InitialRelease:
ptr->merge(parseVersionListInternal(obj));
break;
case MetadataVersion::Invalid:
throw ParseException(QObject::tr("Unknown format version!"));
}
}
void parseVersion(const QJsonObject &obj, Version *ptr)
{
const MetadataVersion version = parseFormatVersion(obj);
switch (version)
{
case MetadataVersion::InitialRelease:
ptr->merge(parseVersionInternal(obj));
break;
case MetadataVersion::Invalid:
throw ParseException(QObject::tr("Unknown format version!"));
}
const MetadataVersion version = parseFormatVersion(obj);
switch (version)
{
case MetadataVersion::InitialRelease:
ptr->merge(parseVersionInternal(obj));
break;
case MetadataVersion::Invalid:
throw ParseException(QObject::tr("Unknown format version!"));
}
}
/*
@ -174,44 +174,44 @@ void parseVersion(const QJsonObject &obj, Version *ptr)
*/
void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char * keyName)
{
if(obj.contains(keyName))
{
QSet<QString> requires;
auto reqArray = requireArray(obj, keyName);
auto iter = reqArray.begin();
while(iter != reqArray.end())
{
auto reqObject = requireObject(*iter);
auto uid = requireString(reqObject, "uid");
auto equals = ensureString(reqObject, "equals", QString());
auto suggests = ensureString(reqObject, "suggests", QString());
ptr->insert({uid, equals, suggests});
iter++;
}
}
if(obj.contains(keyName))
{
QSet<QString> requires;
auto reqArray = requireArray(obj, keyName);
auto iter = reqArray.begin();
while(iter != reqArray.end())
{
auto reqObject = requireObject(*iter);
auto uid = requireString(reqObject, "uid");
auto equals = ensureString(reqObject, "equals", QString());
auto suggests = ensureString(reqObject, "suggests", QString());
ptr->insert({uid, equals, suggests});
iter++;
}
}
}
void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char * keyName)
{
if(!ptr || ptr->empty())
{
return;
}
QJsonArray arrOut;
for(auto &iter: *ptr)
{
QJsonObject reqOut;
reqOut.insert("uid", iter.uid);
if(!iter.equalsVersion.isEmpty())
{
reqOut.insert("equals", iter.equalsVersion);
}
if(!iter.suggests.isEmpty())
{
reqOut.insert("suggests", iter.suggests);
}
arrOut.append(reqOut);
}
obj.insert(keyName, arrOut);
if(!ptr || ptr->empty())
{
return;
}
QJsonArray arrOut;
for(auto &iter: *ptr)
{
QJsonObject reqOut;
reqOut.insert("uid", iter.uid);
if(!iter.equalsVersion.isEmpty())
{
reqOut.insert("equals", iter.equalsVersion);
}
if(!iter.suggests.isEmpty())
{
reqOut.insert("suggests", iter.suggests);
}
arrOut.append(reqOut);
}
obj.insert(keyName, arrOut);
}
}

View File

@ -30,39 +30,39 @@ class VersionList;
enum class MetadataVersion
{
Invalid = -1,
InitialRelease = 1
Invalid = -1,
InitialRelease = 1
};
class ParseException : public Exception
{
public:
using Exception::Exception;
using Exception::Exception;
};
struct Require
{
bool operator==(const Require & rhs) const
{
return uid == rhs.uid;
}
bool operator<(const Require & rhs) const
{
return uid < rhs.uid;
}
bool deepEquals(const Require & rhs) const
{
return uid == rhs.uid
&& equalsVersion == rhs.equalsVersion
&& suggests == rhs.suggests;
}
QString uid;
QString equalsVersion;
QString suggests;
bool operator==(const Require & rhs) const
{
return uid == rhs.uid;
}
bool operator<(const Require & rhs) const
{
return uid < rhs.uid;
}
bool deepEquals(const Require & rhs) const
{
return uid == rhs.uid
&& equalsVersion == rhs.equalsVersion
&& suggests == rhs.suggests;
}
QString uid;
QString equalsVersion;
QString suggests;
};
inline Q_DECL_PURE_FUNCTION uint qHash(const Require &key, uint seed = 0) Q_DECL_NOTHROW
{
return qHash(key.uid, seed);
return qHash(key.uid, seed);
}
using RequireSet = std::set<Require>;

View File

@ -21,7 +21,7 @@
#include "minecraft/ComponentList.h"
Meta::Version::Version(const QString &uid, const QString &version)
: BaseVersion(), m_uid(uid), m_version(version)
: BaseVersion(), m_uid(uid), m_version(version)
{
}
@ -31,110 +31,110 @@ Meta::Version::~Version()
QString Meta::Version::descriptor()
{
return m_version;
return m_version;
}
QString Meta::Version::name()
{
if(m_data)
return m_data->name;
return m_uid;
if(m_data)
return m_data->name;
return m_uid;
}
QString Meta::Version::typeString() const
{
return m_type;
return m_type;
}
QDateTime Meta::Version::time() const
{
return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
}
void Meta::Version::parse(const QJsonObject& obj)
{
parseVersion(obj, this);
parseVersion(obj, this);
}
void Meta::Version::mergeFromList(const Meta::VersionPtr& other)
{
if(other->m_providesRecommendations)
{
if(m_recommended != other->m_recommended)
{
setRecommended(other->m_recommended);
}
}
if (m_type != other->m_type)
{
setType(other->m_type);
}
if (m_time != other->m_time)
{
setTime(other->m_time);
}
if (m_requires != other->m_requires)
{
m_requires = other->m_requires;
}
if (m_conflicts != other->m_conflicts)
{
m_conflicts = other->m_conflicts;
}
if(m_volatile != other->m_volatile)
{
setVolatile(other->m_volatile);
}
if(other->m_providesRecommendations)
{
if(m_recommended != other->m_recommended)
{
setRecommended(other->m_recommended);
}
}
if (m_type != other->m_type)
{
setType(other->m_type);
}
if (m_time != other->m_time)
{
setTime(other->m_time);
}
if (m_requires != other->m_requires)
{
m_requires = other->m_requires;
}
if (m_conflicts != other->m_conflicts)
{
m_conflicts = other->m_conflicts;
}
if(m_volatile != other->m_volatile)
{
setVolatile(other->m_volatile);
}
}
void Meta::Version::merge(const VersionPtr &other)
{
mergeFromList(other);
if(other->m_data)
{
setData(other->m_data);
}
mergeFromList(other);
if(other->m_data)
{
setData(other->m_data);
}
}
QString Meta::Version::localFilename() const
{
return m_uid + '/' + m_version + ".json";
return m_uid + '/' + m_version + ".json";
}
void Meta::Version::setType(const QString &type)
{
m_type = type;
emit typeChanged();
m_type = type;
emit typeChanged();
}
void Meta::Version::setTime(const qint64 time)
{
m_time = time;
emit timeChanged();
m_time = time;
emit timeChanged();
}
void Meta::Version::setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts)
{
m_requires = requires;
m_conflicts = conflicts;
emit requiresChanged();
m_requires = requires;
m_conflicts = conflicts;
emit requiresChanged();
}
void Meta::Version::setVolatile(bool volatile_)
{
m_volatile = volatile_;
m_volatile = volatile_;
}
void Meta::Version::setData(const VersionFilePtr &data)
{
m_data = data;
m_data = data;
}
void Meta::Version::setProvidesRecommendations()
{
m_providesRecommendations = true;
m_providesRecommendations = true;
}
void Meta::Version::setRecommended(bool recommended)
{
m_recommended = recommended;
m_recommended = recommended;
}

View File

@ -36,82 +36,82 @@ using VersionPtr = std::shared_ptr<class Version>;
class MULTIMC_LOGIC_EXPORT Version : public QObject, public BaseVersion, public BaseEntity
{
Q_OBJECT
Q_OBJECT
public: /* con/des */
explicit Version(const QString &uid, const QString &version);
virtual ~Version();
explicit Version(const QString &uid, const QString &version);
virtual ~Version();
QString descriptor() override;
QString name() override;
QString typeString() const override;
QString descriptor() override;
QString name() override;
QString typeString() const override;
QString uid() const
{
return m_uid;
}
QString version() const
{
return m_version;
}
QString type() const
{
return m_type;
}
QDateTime time() const;
qint64 rawTime() const
{
return m_time;
}
const Meta::RequireSet &requires() const
{
return m_requires;
}
VersionFilePtr data() const
{
return m_data;
}
bool isRecommended() const
{
return m_recommended;
}
bool isLoaded() const
{
return m_data != nullptr;
}
QString uid() const
{
return m_uid;
}
QString version() const
{
return m_version;
}
QString type() const
{
return m_type;
}
QDateTime time() const;
qint64 rawTime() const
{
return m_time;
}
const Meta::RequireSet &requires() const
{
return m_requires;
}
VersionFilePtr data() const
{
return m_data;
}
bool isRecommended() const
{
return m_recommended;
}
bool isLoaded() const
{
return m_data != nullptr;
}
void merge(const VersionPtr &other);
void mergeFromList(const VersionPtr &other);
void parse(const QJsonObject &obj) override;
void merge(const VersionPtr &other);
void mergeFromList(const VersionPtr &other);
void parse(const QJsonObject &obj) override;
QString localFilename() const override;
QString localFilename() const override;
public: // for usage by format parsers only
void setType(const QString &type);
void setTime(const qint64 time);
void setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts);
void setVolatile(bool volatile_);
void setRecommended(bool recommended);
void setProvidesRecommendations();
void setData(const VersionFilePtr &data);
void setType(const QString &type);
void setTime(const qint64 time);
void setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts);
void setVolatile(bool volatile_);
void setRecommended(bool recommended);
void setProvidesRecommendations();
void setData(const VersionFilePtr &data);
signals:
void typeChanged();
void timeChanged();
void requiresChanged();
void typeChanged();
void timeChanged();
void requiresChanged();
private:
bool m_providesRecommendations = false;
bool m_recommended = false;
QString m_name;
QString m_uid;
QString m_version;
QString m_type;
qint64 m_time = 0;
Meta::RequireSet m_requires;
Meta::RequireSet m_conflicts;
bool m_volatile = false;
VersionFilePtr m_data;
bool m_providesRecommendations = false;
bool m_recommended = false;
QString m_name;
QString m_uid;
QString m_version;
QString m_type;
qint64 m_time = 0;
Meta::RequireSet m_requires;
Meta::RequireSet m_conflicts;
bool m_volatile = false;
VersionFilePtr m_data;
};
}

View File

@ -24,222 +24,222 @@
namespace Meta
{
VersionList::VersionList(const QString &uid, QObject *parent)
: BaseVersionList(parent), m_uid(uid)
: BaseVersionList(parent), m_uid(uid)
{
setObjectName("Version list: " + uid);
setObjectName("Version list: " + uid);
}
shared_qobject_ptr<Task> VersionList::getLoadTask()
{
load(Net::Mode::Online);
return getCurrentTask();
load(Net::Mode::Online);
return getCurrentTask();
}
bool VersionList::isLoaded()
{
return BaseEntity::isLoaded();
return BaseEntity::isLoaded();
}
const BaseVersionPtr VersionList::at(int i) const
{
return m_versions.at(i);
return m_versions.at(i);
}
int VersionList::count() const
{
return m_versions.size();
return m_versions.size();
}
void VersionList::sortVersions()
{
beginResetModel();
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
{
return *a.get() < *b.get();
});
endResetModel();
beginResetModel();
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
{
return *a.get() < *b.get();
});
endResetModel();
}
QVariant VersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid())
{
return QVariant();
}
if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid())
{
return QVariant();
}
VersionPtr version = m_versions.at(index.row());
VersionPtr version = m_versions.at(index.row());
switch (role)
{
case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast<BaseVersion>(version));
case VersionRole:
case VersionIdRole:
return version->version();
case ParentVersionRole:
{
// FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent uid'.
auto & reqs = version->requires();
auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require & req)
{
return req.uid == "net.minecraft";
});
if (iter != reqs.end())
{
return (*iter).equalsVersion;
}
return QVariant();
}
case TypeRole: return version->type();
switch (role)
{
case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast<BaseVersion>(version));
case VersionRole:
case VersionIdRole:
return version->version();
case ParentVersionRole:
{
// FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent uid'.
auto & reqs = version->requires();
auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require & req)
{
return req.uid == "net.minecraft";
});
if (iter != reqs.end())
{
return (*iter).equalsVersion;
}
return QVariant();
}
case TypeRole: return version->type();
case UidRole: return version->uid();
case TimeRole: return version->time();
case RequiresRole: return QVariant::fromValue(version->requires());
case SortRole: return version->rawTime();
case VersionPtrRole: return QVariant::fromValue(version);
case RecommendedRole: return version->isRecommended();
// FIXME: this should be determined in whatever view/proxy is used...
// case LatestRole: return version == getLatestStable();
default: return QVariant();
}
case UidRole: return version->uid();
case TimeRole: return version->time();
case RequiresRole: return QVariant::fromValue(version->requires());
case SortRole: return version->rawTime();
case VersionPtrRole: return QVariant::fromValue(version);
case RecommendedRole: return version->isRecommended();
// FIXME: this should be determined in whatever view/proxy is used...
// case LatestRole: return version == getLatestStable();
default: return QVariant();
}
}
BaseVersionList::RoleList VersionList::providesRoles() const
{
return {VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole,
TypeRole, UidRole, TimeRole, RequiresRole, SortRole,
RecommendedRole, LatestRole, VersionPtrRole};
return {VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole,
TypeRole, UidRole, TimeRole, RequiresRole, SortRole,
RecommendedRole, LatestRole, VersionPtrRole};
}
QHash<int, QByteArray> VersionList::roleNames() const
{
QHash<int, QByteArray> roles = BaseVersionList::roleNames();
roles.insert(UidRole, "uid");
roles.insert(TimeRole, "time");
roles.insert(SortRole, "sort");
roles.insert(RequiresRole, "requires");
return roles;
QHash<int, QByteArray> roles = BaseVersionList::roleNames();
roles.insert(UidRole, "uid");
roles.insert(TimeRole, "time");
roles.insert(SortRole, "sort");
roles.insert(RequiresRole, "requires");
return roles;
}
QString VersionList::localFilename() const
{
return m_uid + "/index.json";
return m_uid + "/index.json";
}
QString VersionList::humanReadable() const
{
return m_name.isEmpty() ? m_uid : m_name;
return m_name.isEmpty() ? m_uid : m_name;
}
VersionPtr VersionList::getVersion(const QString &version)
{
VersionPtr out = m_lookup.value(version, nullptr);
if(!out)
{
out = std::make_shared<Version>(m_uid, version);
m_lookup[version] = out;
}
return out;
VersionPtr out = m_lookup.value(version, nullptr);
if(!out)
{
out = std::make_shared<Version>(m_uid, version);
m_lookup[version] = out;
}
return out;
}
void VersionList::setName(const QString &name)
{
m_name = name;
emit nameChanged(name);
m_name = name;
emit nameChanged(name);
}
void VersionList::setVersions(const QVector<VersionPtr> &versions)
{
beginResetModel();
m_versions = versions;
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
{
return a->rawTime() > b->rawTime();
});
for (int i = 0; i < m_versions.size(); ++i)
{
m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i));
setupAddedVersion(i, m_versions.at(i));
}
beginResetModel();
m_versions = versions;
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
{
return a->rawTime() > b->rawTime();
});
for (int i = 0; i < m_versions.size(); ++i)
{
m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i));
setupAddedVersion(i, m_versions.at(i));
}
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; });
m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
endResetModel();
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; });
m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
endResetModel();
}
void VersionList::parse(const QJsonObject& obj)
{
parseVersionList(obj, this);
parseVersionList(obj, this);
}
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
static const Meta::VersionPtr &getBetterVersion(const Meta::VersionPtr &a, const Meta::VersionPtr &b)
{
if(!a)
return b;
if(!b)
return a;
if(a->type() == b->type())
{
// newer of same type wins
return (a->rawTime() > b->rawTime() ? a : b);
}
// 'release' type wins
return (a->type() == "release" ? a : b);
if(!a)
return b;
if(!b)
return a;
if(a->type() == b->type())
{
// newer of same type wins
return (a->rawTime() > b->rawTime() ? a : b);
}
// 'release' type wins
return (a->type() == "release" ? a : b);
}
void VersionList::mergeFromIndex(const VersionListPtr &other)
{
if (m_name != other->m_name)
{
setName(other->m_name);
}
if (m_name != other->m_name)
{
setName(other->m_name);
}
}
void VersionList::merge(const VersionListPtr &other)
{
if (m_name != other->m_name)
{
setName(other->m_name);
}
if (m_name != other->m_name)
{
setName(other->m_name);
}
// TODO: do not reset the whole model. maybe?
beginResetModel();
m_versions.clear();
if(other->m_versions.isEmpty())
{
qWarning() << "Empty list loaded ...";
}
for (const VersionPtr &version : other->m_versions)
{
// we already have the version. merge the contents
if (m_lookup.contains(version->version()))
{
m_lookup.value(version->version())->mergeFromList(version);
}
else
{
m_lookup.insert(version->uid(), version);
}
// connect it.
setupAddedVersion(m_versions.size(), version);
m_versions.append(version);
m_recommended = getBetterVersion(m_recommended, version);
}
endResetModel();
// TODO: do not reset the whole model. maybe?
beginResetModel();
m_versions.clear();
if(other->m_versions.isEmpty())
{
qWarning() << "Empty list loaded ...";
}
for (const VersionPtr &version : other->m_versions)
{
// we already have the version. merge the contents
if (m_lookup.contains(version->version()))
{
m_lookup.value(version->version())->mergeFromList(version);
}
else
{
m_lookup.insert(version->uid(), version);
}
// connect it.
setupAddedVersion(m_versions.size(), version);
m_versions.append(version);
m_recommended = getBetterVersion(m_recommended, version);
}
endResetModel();
}
void VersionList::setupAddedVersion(const int row, const VersionPtr &version)
{
// FIXME: do not disconnect from everythin, disconnect only the lambdas here
version->disconnect();
connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
// FIXME: do not disconnect from everythin, disconnect only the lambdas here
version->disconnect();
connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
}
BaseVersionPtr VersionList::getRecommended() const
{
return m_recommended;
return m_recommended;
}
}

View File

@ -27,75 +27,75 @@ using VersionListPtr = std::shared_ptr<class VersionList>;
class MULTIMC_LOGIC_EXPORT VersionList : public BaseVersionList, public BaseEntity
{
Q_OBJECT
Q_PROPERTY(QString uid READ uid CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_OBJECT
Q_PROPERTY(QString uid READ uid CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
public:
explicit VersionList(const QString &uid, QObject *parent = nullptr);
explicit VersionList(const QString &uid, QObject *parent = nullptr);
enum Roles
{
UidRole = Qt::UserRole + 100,
TimeRole,
RequiresRole,
VersionPtrRole
};
enum Roles
{
UidRole = Qt::UserRole + 100,
TimeRole,
RequiresRole,
VersionPtrRole
};
shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
void sortVersions() override;
shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
void sortVersions() override;
BaseVersionPtr getRecommended() const override;
BaseVersionPtr getRecommended() const override;
QVariant data(const QModelIndex &index, int role) const override;
RoleList providesRoles() const override;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role) const override;
RoleList providesRoles() const override;
QHash<int, QByteArray> roleNames() const override;
QString localFilename() const override;
QString localFilename() const override;
QString uid() const
{
return m_uid;
}
QString name() const
{
return m_name;
}
QString humanReadable() const;
QString uid() const
{
return m_uid;
}
QString name() const
{
return m_name;
}
QString humanReadable() const;
VersionPtr getVersion(const QString &version);
VersionPtr getVersion(const QString &version);
QVector<VersionPtr> versions() const
{
return m_versions;
}
QVector<VersionPtr> versions() const
{
return m_versions;
}
public: // for usage only by parsers
void setName(const QString &name);
void setVersions(const QVector<VersionPtr> &versions);
void merge(const VersionListPtr &other);
void mergeFromIndex(const VersionListPtr &other);
void parse(const QJsonObject &obj) override;
void setName(const QString &name);
void setVersions(const QVector<VersionPtr> &versions);
void merge(const VersionListPtr &other);
void mergeFromIndex(const VersionListPtr &other);
void parse(const QJsonObject &obj) override;
signals:
void nameChanged(const QString &name);
void nameChanged(const QString &name);
protected slots:
void updateListData(QList<BaseVersionPtr>) override
{
}
void updateListData(QList<BaseVersionPtr>) override
{
}
private:
QVector<VersionPtr> m_versions;
QHash<QString, VersionPtr> m_lookup;
QString m_uid;
QString m_name;
QVector<VersionPtr> m_versions;
QHash<QString, VersionPtr> m_lookup;
QString m_uid;
QString m_name;
VersionPtr m_recommended;
VersionPtr m_recommended;
void setupAddedVersion(const int row, const VersionPtr &version);
void setupAddedVersion(const int row, const VersionPtr &version);
};
}
Q_DECLARE_METATYPE(Meta::VersionListPtr)

View File

@ -38,200 +38,200 @@ namespace AssetsUtils
*/
bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
{
/*
{
"objects": {
"icons/icon_16x16.png": {
"hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a",
"size": 3665
},
...
}
}
}
*/
/*
{
"objects": {
"icons/icon_16x16.png": {
"hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a",
"size": 3665
},
...
}
}
}
*/
QFile file(path);
QFile file(path);
// Try to open the file and fail if we can't.
// TODO: We should probably report this error to the user.
if (!file.open(QIODevice::ReadOnly))
{
qCritical() << "Failed to read assets index file" << path;
return false;
}
index->id = assetsId;
// Try to open the file and fail if we can't.
// TODO: We should probably report this error to the user.
if (!file.open(QIODevice::ReadOnly))
{
qCritical() << "Failed to read assets index file" << path;
return false;
}
index->id = assetsId;
// Read the file and close it.
QByteArray jsonData = file.readAll();
file.close();
// Read the file and close it.
QByteArray jsonData = file.readAll();
file.close();
QJsonParseError parseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
QJsonParseError parseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
// Fail if the JSON is invalid.
if (parseError.error != QJsonParseError::NoError)
{
qCritical() << "Failed to parse assets index file:" << parseError.errorString()
<< "at offset " << QString::number(parseError.offset);
return false;
}
// Fail if the JSON is invalid.
if (parseError.error != QJsonParseError::NoError)
{
qCritical() << "Failed to parse assets index file:" << parseError.errorString()
<< "at offset " << QString::number(parseError.offset);
return false;
}
// Make sure the root is an object.
if (!jsonDoc.isObject())
{
qCritical() << "Invalid assets index JSON: Root should be an array.";
return false;
}
// Make sure the root is an object.
if (!jsonDoc.isObject())
{
qCritical() << "Invalid assets index JSON: Root should be an array.";
return false;
}
QJsonObject root = jsonDoc.object();
QJsonObject root = jsonDoc.object();
QJsonValue isVirtual = root.value("virtual");
if (!isVirtual.isUndefined())
{
index->isVirtual = isVirtual.toBool(false);
}
QJsonValue isVirtual = root.value("virtual");
if (!isVirtual.isUndefined())
{
index->isVirtual = isVirtual.toBool(false);
}
QJsonValue objects = root.value("objects");
QVariantMap map = objects.toVariant().toMap();
QJsonValue objects = root.value("objects");
QVariantMap map = objects.toVariant().toMap();
for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
{
// qDebug() << iter.key();
for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
{
// qDebug() << iter.key();
QVariant variant = iter.value();
QVariantMap nested_objects = variant.toMap();
QVariant variant = iter.value();
QVariantMap nested_objects = variant.toMap();
AssetObject object;
AssetObject object;
for (QVariantMap::const_iterator nested_iter = nested_objects.begin();
nested_iter != nested_objects.end(); ++nested_iter)
{
// qDebug() << nested_iter.key() << nested_iter.value().toString();
QString key = nested_iter.key();
QVariant value = nested_iter.value();
for (QVariantMap::const_iterator nested_iter = nested_objects.begin();
nested_iter != nested_objects.end(); ++nested_iter)
{
// qDebug() << nested_iter.key() << nested_iter.value().toString();
QString key = nested_iter.key();
QVariant value = nested_iter.value();
if (key == "hash")
{
object.hash = value.toString();
}
else if (key == "size")
{
object.size = value.toDouble();
}
}
if (key == "hash")
{
object.hash = value.toString();
}
else if (key == "size")
{
object.size = value.toDouble();
}
}
index->objects.insert(iter.key(), object);
}
index->objects.insert(iter.key(), object);
}
return true;
return true;
}
QDir reconstructAssets(QString assetsId)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects"));
QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual"));
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects"));
QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual"));
QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json");
QFile indexFile(indexPath);
QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId));
QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json");
QFile indexFile(indexPath);
QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId));
if (!indexFile.exists())
{
qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets";
return virtualRoot;
}
if (!indexFile.exists())
{
qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets";
return virtualRoot;
}
qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path()
<< objectDir.path() << virtualDir.path() << virtualRoot.path();
qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path()
<< objectDir.path() << virtualDir.path() << virtualRoot.path();
AssetsIndex index;
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index);
AssetsIndex index;
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index);
if (loadAssetsIndex && index.isVirtual)
{
qDebug() << "Reconstructing virtual assets folder at" << virtualRoot.path();
if (loadAssetsIndex && index.isVirtual)
{
qDebug() << "Reconstructing virtual assets folder at" << virtualRoot.path();
for (QString map : index.objects.keys())
{
AssetObject asset_object = index.objects.value(map);
QString target_path = FS::PathCombine(virtualRoot.path(), map);
QFile target(target_path);
for (QString map : index.objects.keys())
{
AssetObject asset_object = index.objects.value(map);
QString target_path = FS::PathCombine(virtualRoot.path(), map);
QFile target(target_path);
QString tlk = asset_object.hash.left(2);
QString tlk = asset_object.hash.left(2);
QString original_path = FS::PathCombine(objectDir.path(), tlk, asset_object.hash);
QFile original(original_path);
if (!original.exists())
continue;
if (!target.exists())
{
QFileInfo info(target_path);
QDir target_dir = info.dir();
// qDebug() << target_dir;
if (!target_dir.exists())
QDir("").mkpath(target_dir.path());
QString original_path = FS::PathCombine(objectDir.path(), tlk, asset_object.hash);
QFile original(original_path);
if (!original.exists())
continue;
if (!target.exists())
{
QFileInfo info(target_path);
QDir target_dir = info.dir();
// qDebug() << target_dir;
if (!target_dir.exists())
QDir("").mkpath(target_dir.path());
bool couldCopy = original.copy(target_path);
qDebug() << " Copying" << original_path << "to" << target_path
<< QString::number(couldCopy); // << original.errorString();
}
}
bool couldCopy = original.copy(target_path);
qDebug() << " Copying" << original_path << "to" << target_path
<< QString::number(couldCopy); // << original.errorString();
}
}
// TODO: Write last used time to virtualRoot/.lastused
}
// TODO: Write last used time to virtualRoot/.lastused
}
return virtualRoot;
return virtualRoot;
}
}
NetActionPtr AssetObject::getDownloadAction()
{
QFileInfo objectFile(getLocalPath());
if ((!objectFile.isFile()) || (objectFile.size() != size))
{
auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
if(hash.size())
{
auto rawHash = QByteArray::fromHex(hash.toLatin1());
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
}
objectDL->m_total_progress = size;
return objectDL;
}
return nullptr;
QFileInfo objectFile(getLocalPath());
if ((!objectFile.isFile()) || (objectFile.size() != size))
{
auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath());
if(hash.size())
{
auto rawHash = QByteArray::fromHex(hash.toLatin1());
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
}
objectDL->m_total_progress = size;
return objectDL;
}
return nullptr;
}
QString AssetObject::getLocalPath()
{
return "assets/objects/" + getRelPath();
return "assets/objects/" + getRelPath();
}
QUrl AssetObject::getUrl()
{
return QUrl("http://resources.download.minecraft.net/" + getRelPath());
return QUrl("http://resources.download.minecraft.net/" + getRelPath());
}
QString AssetObject::getRelPath()
{
return hash.left(2) + "/" + hash;
return hash.left(2) + "/" + hash;
}
NetJobPtr AssetsIndex::getDownloadJob()
{
auto job = new NetJob(QObject::tr("Assets for %1").arg(id));
for (auto &object : objects.values())
{
auto dl = object.getDownloadAction();
if(dl)
{
job->addNetAction(dl);
}
}
if(job->size())
return job;
return nullptr;
auto job = new NetJob(QObject::tr("Assets for %1").arg(id));
for (auto &object : objects.values())
{
auto dl = object.getDownloadAction();
if(dl)
{
job->addNetAction(dl);
}
}
if(job->size())
return job;
return nullptr;
}

View File

@ -22,22 +22,22 @@
struct AssetObject
{
QString getRelPath();
QUrl getUrl();
QString getLocalPath();
NetActionPtr getDownloadAction();
QString getRelPath();
QUrl getUrl();
QString getLocalPath();
NetActionPtr getDownloadAction();
QString hash;
qint64 size;
QString hash;
qint64 size;
};
struct AssetsIndex
{
NetJobPtr getDownloadJob();
NetJobPtr getDownloadJob();
QString id;
QMap<QString, AssetObject> objects;
bool isVirtual = false;
QString id;
QMap<QString, AssetObject> objects;
bool isVirtual = false;
};
namespace AssetsUtils

View File

@ -13,356 +13,356 @@
Component::Component(ComponentList * parent, const QString& uid)
{
assert(parent);
m_parent = parent;
assert(parent);
m_parent = parent;
m_uid = uid;
m_uid = uid;
}
Component::Component(ComponentList * parent, std::shared_ptr<Meta::Version> version)
{
assert(parent);
m_parent = parent;
assert(parent);
m_parent = parent;
m_metaVersion = version;
m_uid = version->uid();
m_version = m_cachedVersion = version->version();
m_cachedName = version->name();
m_loaded = version->isLoaded();
m_metaVersion = version;
m_uid = version->uid();
m_version = m_cachedVersion = version->version();
m_cachedName = version->name();
m_loaded = version->isLoaded();
}
Component::Component(ComponentList * parent, const QString& uid, std::shared_ptr<VersionFile> file)
{
assert(parent);
m_parent = parent;
assert(parent);
m_parent = parent;
m_file = file;
m_uid = uid;
m_cachedVersion = m_file->version;
m_cachedName = m_file->name;
m_loaded = true;
m_file = file;
m_uid = uid;
m_cachedVersion = m_file->version;
m_cachedName = m_file->name;
m_loaded = true;
}
std::shared_ptr<Meta::Version> Component::getMeta()
{
return m_metaVersion;
return m_metaVersion;
}
void Component::applyTo(LaunchProfile* profile)
{
// do not apply disabled components
if(!isEnabled())
{
return;
}
auto vfile = getVersionFile();
if(vfile)
{
vfile->applyTo(profile);
}
else
{
profile->applyProblemSeverity(getProblemSeverity());
}
// do not apply disabled components
if(!isEnabled())
{
return;
}
auto vfile = getVersionFile();
if(vfile)
{
vfile->applyTo(profile);
}
else
{
profile->applyProblemSeverity(getProblemSeverity());
}
}
std::shared_ptr<class VersionFile> Component::getVersionFile() const
{
if(m_metaVersion)
{
if(!m_metaVersion->isLoaded())
{
m_metaVersion->load(Net::Mode::Online);
}
return m_metaVersion->data();
}
else
{
return m_file;
}
if(m_metaVersion)
{
if(!m_metaVersion->isLoaded())
{
m_metaVersion->load(Net::Mode::Online);
}
return m_metaVersion->data();
}
else
{
return m_file;
}
}
std::shared_ptr<class Meta::VersionList> Component::getVersionList() const
{
// FIXME: what if the metadata index isn't loaded yet?
if(ENV.metadataIndex()->hasUid(m_uid))
{
return ENV.metadataIndex()->get(m_uid);
}
return nullptr;
// FIXME: what if the metadata index isn't loaded yet?
if(ENV.metadataIndex()->hasUid(m_uid))
{
return ENV.metadataIndex()->get(m_uid);
}
return nullptr;
}
int Component::getOrder()
{
if(m_orderOverride)
return m_order;
if(m_orderOverride)
return m_order;
auto vfile = getVersionFile();
if(vfile)
{
return vfile->order;
}
return 0;
auto vfile = getVersionFile();
if(vfile)
{
return vfile->order;
}
return 0;
}
void Component::setOrder(int order)
{
m_orderOverride = true;
m_order = order;
m_orderOverride = true;
m_order = order;
}
QString Component::getID()
{
return m_uid;
return m_uid;
}
QString Component::getName()
{
if (!m_cachedName.isEmpty())
return m_cachedName;
return m_uid;
if (!m_cachedName.isEmpty())
return m_cachedName;
return m_uid;
}
QString Component::getVersion()
{
return m_cachedVersion;
return m_cachedVersion;
}
QString Component::getFilename()
{
return m_parent->patchFilePathForUid(m_uid);
return m_parent->patchFilePathForUid(m_uid);
}
QDateTime Component::getReleaseDateTime()
{
if(m_metaVersion)
{
return m_metaVersion->time();
}
auto vfile = getVersionFile();
if(vfile)
{
return vfile->releaseTime;
}
// FIXME: fake
return QDateTime::currentDateTime();
if(m_metaVersion)
{
return m_metaVersion->time();
}
auto vfile = getVersionFile();
if(vfile)
{
return vfile->releaseTime;
}
// FIXME: fake
return QDateTime::currentDateTime();
}
bool Component::isEnabled()
{
return !canBeDisabled() || !m_disabled;
return !canBeDisabled() || !m_disabled;
}
bool Component::canBeDisabled()
{
return isRemovable() && !m_dependencyOnly;
return isRemovable() && !m_dependencyOnly;
}
bool Component::setEnabled(bool state)
{
bool intendedDisabled = !state;
if (!canBeDisabled())
{
intendedDisabled = false;
}
if(intendedDisabled != m_disabled)
{
m_disabled = intendedDisabled;
emit dataChanged();
return true;
}
return false;
bool intendedDisabled = !state;
if (!canBeDisabled())
{
intendedDisabled = false;
}
if(intendedDisabled != m_disabled)
{
m_disabled = intendedDisabled;
emit dataChanged();
return true;
}
return false;
}
bool Component::isCustom()
{
return m_file != nullptr;
return m_file != nullptr;
}
bool Component::isCustomizable()
{
if(m_metaVersion)
{
if(getVersionFile())
{
return true;
}
}
return false;
if(m_metaVersion)
{
if(getVersionFile())
{
return true;
}
}
return false;
}
bool Component::isRemovable()
{
return !m_important;
return !m_important;
}
bool Component::isRevertible()
{
if (isCustom())
{
if(ENV.metadataIndex()->hasUid(m_uid))
{
return true;
}
}
return false;
if (isCustom())
{
if(ENV.metadataIndex()->hasUid(m_uid))
{
return true;
}
}
return false;
}
bool Component::isMoveable()
{
// HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
return true;
// HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
return true;
}
bool Component::isVersionChangeable()
{
auto list = getVersionList();
if(list)
{
if(!list->isLoaded())
{
list->load(Net::Mode::Online);
}
return list->count() != 0;
}
return false;
auto list = getVersionList();
if(list)
{
if(!list->isLoaded())
{
list->load(Net::Mode::Online);
}
return list->count() != 0;
}
return false;
}
void Component::setImportant(bool state)
{
if(m_important != state)
{
m_important = state;
emit dataChanged();
}
if(m_important != state)
{
m_important = state;
emit dataChanged();
}
}
ProblemSeverity Component::getProblemSeverity() const
{
auto file = getVersionFile();
if(file)
{
return file->getProblemSeverity();
}
return ProblemSeverity::Error;
auto file = getVersionFile();
if(file)
{
return file->getProblemSeverity();
}
return ProblemSeverity::Error;
}
const QList<PatchProblem> Component::getProblems() const
{
auto file = getVersionFile();
if(file)
{
return file->getProblems();
}
return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
auto file = getVersionFile();
if(file)
{
return file->getProblems();
}
return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
}
void Component::setVersion(const QString& version)
{
if(version == m_version)
{
return;
}
m_version = version;
if(m_loaded)
{
// we are loaded and potentially have state to invalidate
if(m_file)
{
// we have a file... explicit version has been changed and there is nothing else to do.
}
else
{
// we don't have a file, therefore we are loaded with metadata
m_cachedVersion = version;
// see if the meta version is loaded
auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
if(metaVersion->isLoaded())
{
// if yes, we can continue with that.
m_metaVersion = metaVersion;
}
else
{
// if not, we need loading
m_metaVersion.reset();
m_loaded = false;
}
updateCachedData();
}
}
else
{
// not loaded... assume it will be sorted out later by the update task
}
emit dataChanged();
if(version == m_version)
{
return;
}
m_version = version;
if(m_loaded)
{
// we are loaded and potentially have state to invalidate
if(m_file)
{
// we have a file... explicit version has been changed and there is nothing else to do.
}
else
{
// we don't have a file, therefore we are loaded with metadata
m_cachedVersion = version;
// see if the meta version is loaded
auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
if(metaVersion->isLoaded())
{
// if yes, we can continue with that.
m_metaVersion = metaVersion;
}
else
{
// if not, we need loading
m_metaVersion.reset();
m_loaded = false;
}
updateCachedData();
}
}
else
{
// not loaded... assume it will be sorted out later by the update task
}
emit dataChanged();
}
bool Component::customize()
{
if(isCustom())
{
return false;
}
if(isCustom())
{
return false;
}
auto filename = getFilename();
if(!FS::ensureFilePathExists(filename))
{
return false;
}
// FIXME: get rid of this try-catch.
try
{
QSaveFile jsonFile(filename);
if(!jsonFile.open(QIODevice::WriteOnly))
{
return false;
}
auto vfile = getVersionFile();
if(!vfile)
{
return false;
}
auto document = OneSixVersionFormat::versionFileToJson(vfile);
jsonFile.write(document.toJson());
if(!jsonFile.commit())
{
return false;
}
m_file = vfile;
m_metaVersion.reset();
emit dataChanged();
}
catch (const Exception &error)
{
qWarning() << "Version could not be loaded:" << error.cause();
}
return true;
auto filename = getFilename();
if(!FS::ensureFilePathExists(filename))
{
return false;
}
// FIXME: get rid of this try-catch.
try
{
QSaveFile jsonFile(filename);
if(!jsonFile.open(QIODevice::WriteOnly))
{
return false;
}
auto vfile = getVersionFile();
if(!vfile)
{
return false;
}
auto document = OneSixVersionFormat::versionFileToJson(vfile);
jsonFile.write(document.toJson());
if(!jsonFile.commit())
{
return false;
}
m_file = vfile;
m_metaVersion.reset();
emit dataChanged();
}
catch (const Exception &error)
{
qWarning() << "Version could not be loaded:" << error.cause();
}
return true;
}
bool Component::revert()
{
if(!isCustom())
{
// already not custom
return true;
}
auto filename = getFilename();
bool result = true;
// just kill the file and reload
if(QFile::exists(filename))
{
result = QFile::remove(filename);
}
if(result)
{
// file gone...
m_file.reset();
if(!isCustom())
{
// already not custom
return true;
}
auto filename = getFilename();
bool result = true;
// just kill the file and reload
if(QFile::exists(filename))
{
result = QFile::remove(filename);
}
if(result)
{
// file gone...
m_file.reset();
// check local cache for metadata...
auto version = ENV.metadataIndex()->get(m_uid, m_version);
if(version->isLoaded())
{
m_metaVersion = version;
}
else
{
m_metaVersion.reset();
m_loaded = false;
}
emit dataChanged();
}
return result;
// check local cache for metadata...
auto version = ENV.metadataIndex()->get(m_uid, m_version);
if(version->isLoaded())
{
m_metaVersion = version;
}
else
{
m_metaVersion.reset();
m_loaded = false;
}
emit dataChanged();
}
return result;
}
/**
@ -372,68 +372,68 @@ bool Component::revert()
*/
static bool deepCompare(const std::set<Meta::Require> & a, const std::set<Meta::Require> & b)
{
// NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
if(a.size() != b.size())
{
return false;
}
for(const auto & reqA :a)
{
const auto &iter2 = b.find(reqA);
if(iter2 == b.cend())
{
return false;
}
const auto & reqB = *iter2;
if(!reqA.deepEquals(reqB))
{
return false;
}
}
return true;
// NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
if(a.size() != b.size())
{
return false;
}
for(const auto & reqA :a)
{
const auto &iter2 = b.find(reqA);
if(iter2 == b.cend())
{
return false;
}
const auto & reqB = *iter2;
if(!reqA.deepEquals(reqB))
{
return false;
}
}
return true;
}
void Component::updateCachedData()
{
auto file = getVersionFile();
if(file)
{
bool changed = false;
if(m_cachedName != file->name)
{
m_cachedName = file->name;
changed = true;
}
if(m_cachedVersion != file->version)
{
m_cachedVersion = file->version;
changed = true;
}
if(m_cachedVolatile != file->m_volatile)
{
m_cachedVolatile = file->m_volatile;
changed = true;
}
if(!deepCompare(m_cachedRequires, file->requires))
{
m_cachedRequires = file->requires;
changed = true;
}
if(!deepCompare(m_cachedConflicts, file->conflicts))
{
m_cachedConflicts = file->conflicts;
changed = true;
}
if(changed)
{
emit dataChanged();
}
}
else
{
// in case we removed all the metadata
m_cachedRequires.clear();
m_cachedConflicts.clear();
emit dataChanged();
}
auto file = getVersionFile();
if(file)
{
bool changed = false;
if(m_cachedName != file->name)
{
m_cachedName = file->name;
changed = true;
}
if(m_cachedVersion != file->version)
{
m_cachedVersion = file->version;
changed = true;
}
if(m_cachedVolatile != file->m_volatile)
{
m_cachedVolatile = file->m_volatile;
changed = true;
}
if(!deepCompare(m_cachedRequires, file->requires))
{
m_cachedRequires = file->requires;
changed = true;
}
if(!deepCompare(m_cachedConflicts, file->conflicts))
{
m_cachedConflicts = file->conflicts;
changed = true;
}
if(changed)
{
emit dataChanged();
}
}
else
{
// in case we removed all the metadata
m_cachedRequires.clear();
m_cachedConflicts.clear();
emit dataChanged();
}
}

View File

@ -13,8 +13,8 @@ class ComponentList;
class LaunchProfile;
namespace Meta
{
class Version;
class VersionList;
class Version;
class VersionList;
}
class VersionFile;
@ -22,90 +22,90 @@ class MULTIMC_LOGIC_EXPORT Component : public QObject, public ProblemProvider
{
Q_OBJECT
public:
Component(ComponentList * parent, const QString &uid);
Component(ComponentList * parent, const QString &uid);
// DEPRECATED: remove these constructors?
Component(ComponentList * parent, std::shared_ptr<Meta::Version> version);
Component(ComponentList * parent, const QString & uid, std::shared_ptr<VersionFile> file);
// DEPRECATED: remove these constructors?
Component(ComponentList * parent, std::shared_ptr<Meta::Version> version);
Component(ComponentList * parent, const QString & uid, std::shared_ptr<VersionFile> file);
virtual ~Component(){};
void applyTo(LaunchProfile *profile);
virtual ~Component(){};
void applyTo(LaunchProfile *profile);
bool isEnabled();
bool setEnabled (bool state);
bool canBeDisabled();
bool isEnabled();
bool setEnabled (bool state);
bool canBeDisabled();
bool isMoveable();
bool isCustomizable();
bool isRevertible();
bool isRemovable();
bool isCustom();
bool isVersionChangeable();
bool isMoveable();
bool isCustomizable();
bool isRevertible();
bool isRemovable();
bool isCustom();
bool isVersionChangeable();
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
void setOrder(int order);
int getOrder();
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
void setOrder(int order);
int getOrder();
QString getID();
QString getName();
QString getVersion();
std::shared_ptr<Meta::Version> getMeta();
QDateTime getReleaseDateTime();
QString getID();
QString getName();
QString getVersion();
std::shared_ptr<Meta::Version> getMeta();
QDateTime getReleaseDateTime();
QString getFilename();
QString getFilename();
std::shared_ptr<class VersionFile> getVersionFile() const;
std::shared_ptr<class Meta::VersionList> getVersionList() const;
std::shared_ptr<class VersionFile> getVersionFile() const;
std::shared_ptr<class Meta::VersionList> getVersionList() const;
void setImportant (bool state);
void setImportant (bool state);
const QList<PatchProblem> getProblems() const override;
ProblemSeverity getProblemSeverity() const override;
const QList<PatchProblem> getProblems() const override;
ProblemSeverity getProblemSeverity() const override;
void setVersion(const QString & version);
bool customize();
bool revert();
void setVersion(const QString & version);
bool customize();
bool revert();
void updateCachedData();
void updateCachedData();
signals:
void dataChanged();
void dataChanged();
public: /* data */
ComponentList * m_parent;
ComponentList * m_parent;
// BEGIN: persistent component list properties
/// ID of the component
QString m_uid;
/// version of the component - when there's a custom json override, this is also the version the component reverts to
QString m_version;
/// if true, this has been added automatically to satisfy dependencies and may be automatically removed
bool m_dependencyOnly = false;
/// if true, the component is either the main component of the instance, or otherwise important and cannot be removed.
bool m_important = false;
/// if true, the component is disabled
bool m_disabled = false;
// BEGIN: persistent component list properties
/// ID of the component
QString m_uid;
/// version of the component - when there's a custom json override, this is also the version the component reverts to
QString m_version;
/// if true, this has been added automatically to satisfy dependencies and may be automatically removed
bool m_dependencyOnly = false;
/// if true, the component is either the main component of the instance, or otherwise important and cannot be removed.
bool m_important = false;
/// if true, the component is disabled
bool m_disabled = false;
/// cached name for display purposes, taken from the version file (meta or local override)
QString m_cachedName;
/// cached version for display AND other purposes, taken from the version file (meta or local override)
QString m_cachedVersion;
/// cached set of requirements, taken from the version file (meta or local override)
Meta::RequireSet m_cachedRequires;
Meta::RequireSet m_cachedConflicts;
/// if true, the component is volatile and may be automatically removed when no longer needed
bool m_cachedVolatile = false;
// END: persistent component list properties
/// cached name for display purposes, taken from the version file (meta or local override)
QString m_cachedName;
/// cached version for display AND other purposes, taken from the version file (meta or local override)
QString m_cachedVersion;
/// cached set of requirements, taken from the version file (meta or local override)
Meta::RequireSet m_cachedRequires;
Meta::RequireSet m_cachedConflicts;
/// if true, the component is volatile and may be automatically removed when no longer needed
bool m_cachedVolatile = false;
// END: persistent component list properties
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
bool m_orderOverride = false;
int m_order = 0;
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
bool m_orderOverride = false;
int m_order = 0;
// load state
std::shared_ptr<Meta::Version> m_metaVersion;
std::shared_ptr<VersionFile> m_file;
bool m_loaded = false;
// load state
std::shared_ptr<Meta::Version> m_metaVersion;
std::shared_ptr<VersionFile> m_file;
bool m_loaded = false;
};
typedef shared_qobject_ptr<Component> ComponentPtr;

Some files were not shown because too many files have changed in this diff Show More