Compare commits

...
Author SHA1 Message Date
isanae 08e0d85d85 added DownloadInfo for headers and user agent 2021-03-02 19:14:14 -05:00
isanae 2094d7d15f added IPluginRepository to container
downloads now have an IRepositoryDownload to manage the actual downloads
curl downloader uses IDownload and IDownloader
download stats, allow downloading to buffer
2021-03-02 17:15:52 -05:00
isanae 25a69bccd3 split queue back into manager 2021-02-17 14:16:55 -05:00
isanae a39a80ec4c log wrapper, verbose log
log curl version
2021-02-17 11:18:28 -05:00
isanae 42d432e5aa moved to curldownloader 2021-02-16 04:53:49 -05:00
isanae acb55d431c fixed output file staying opened when moving to queue
refactored removing from queue
2021-02-16 04:43:26 -05:00
isanae 95a2cc93b9 queue management 2021-02-16 04:23:20 -05:00
isanae 35a0474ac1 initial new download manager 2021-02-16 02:33:20 -05:00
Mikaël Capelle bd93864ed2 Merge pull request #1409 from isanae/portable-loop
Portable instance loop.
2021-02-13 18:25:39 +01:00
isanae 406d6f28c0 force instance selection when the current instance cannot be used
the problem is that an empty instance name means the portable instance, but MO clears the name when it needs instance selection, which can create a loop if the portable instance can't be used
2021-02-13 07:54:05 -05:00
Mikaël Capelle ee4c364ccc Bump version to 2.4.0rc2 and update translations. 2021-02-12 18:18:19 +01:00
Mikaël Capelle 607fafa364 Merge pull request #1408 from isanae/portable-shortcuts
Fixes for portable shortcuts
2021-02-12 18:09:15 +01:00
Mikaël Capelle 7dc28b10d9 Merge pull request #1407 from isanae/processes-in-job
Fixes for processesInJob()
2021-02-12 18:09:05 +01:00
Mikaël Capelle 9f0c8c31a6 Merge pull request #1405 from LostDragonist/update_fixes
Fixes for the update dialog window
2021-02-12 18:08:54 +01:00
Mikaël Capelle 2fe627c261 Merge pull request #1404 from Al12rs/fix_update_filter
Hopefully fix update filter not showing new mod updates.
2021-02-12 18:08:41 +01:00
Mikaël Capelle 879c37984e Merge pull request #1403 from Holt59/fix-steamappid-env-error
Do not return previous value in env::set and env::setPath.
2021-02-12 18:08:25 +01:00
Mikaël Capelle 1cf8a2bfee Merge pull request #1401 from ModOrganizer2/Al12rs-appveyorFix
Appveyor: artifact only the exe
2021-02-12 18:08:07 +01:00
Mikaël Capelle 7531a860cf Merge pull request #1400 from Holt59/fix-markers-conflicts-refresh
Better refresh of override markers and conflicts.
2021-02-12 18:07:56 +01:00
Chris Bessent 31c8415e88 Use release name for changelogs 2021-02-12 01:31:41 -07:00
isanae 48274f3357 - renamed instance::name() to displayName() to reduce confusion with portable instances
- better errors when running incorrect shortcuts
- fixed shortcuts being created with moshortcut://Portable:exe instead of an empty instance name for portable instances
2021-02-12 03:22:52 -05:00
Chris Bessent f486e6fa4e Updates to text, title and buttons 2021-02-11 18:39:06 -07:00
Chris Bessent 9e5a64b545 Update CMakeLists.txt 2021-02-11 18:09:19 -07:00
Chris Bessent 3dd05ff869 Create a new dialog for the update window
The idea here is to be able to resize the window and better handle
markdown formatting.  Everything was stolen from the LOOT dialog.
2021-02-11 17:58:10 -07:00
AL eba98e8469 Remove unused variable 2021-02-11 16:34:43 +01:00
AL 73418ec616 Hopefully fix update filter not showing new mod updates.
Notify all the mods that have had their info checked, not just the ones that have been found with an update. This is because Mo2 also shows mods that have had their files deleted or moved to Old as updateAvailable, in which case there is no update but the filter still needs to be notified to include the mod.
2021-02-11 16:29:32 +01:00
Mikaël Capelle 7283e6c3ba Do not return previous value in env::set and env::setPath (avoid error log when not previously set). 2021-02-10 19:17:21 +01:00
isanae d179ad01c8 maxIds was local to the loop, not doubled
handle ERROR_MORE_DATA, it seems to sometimes happen, not sure why
added more logging, a user actually got that error
2021-02-10 10:01:39 -05:00
Al 04d80bb567 Appveyor: artifact only the exe
The entire build is too big and we get over the limit too fast
2021-02-09 22:01:24 +01:00
Mikaël Capelle 595f6eccf6 Better refresh of override markers and conflicts. 2021-02-09 20:23:42 +01:00
Chris Bessent f058125e66 Fix concatenating update logs 2021-02-08 06:09:06 -07:00
Mikaël Capelle 7d36bc20d0 Bump version to 2.4.0rc1.1. 2021-02-07 18:47:00 +01:00
40 changed files with 2576 additions and 360 deletions
+3 -3
View File
@@ -52,10 +52,10 @@ build_script:
if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode ) }
test: off
artifacts:
- path: '\modorganizer-build\install\bin'
- path: '\modorganizer-build\install\bin\ModOrganizer.exe'
name: Mod.Organizer-$(APPVEYOR_BUILD_VERSION)
- path: '\modorganizer-build\install\pdb'
name: PDBs-Mod.Organizer-$(APPVEYOR_BUILD_VERSION)
- path: '\modorganizer-build\install\pdb\ModOrganizer.pdb'
name: PDB-Mod.Organizer-$(APPVEYOR_BUILD_VERSION)
deploy: off
on_success:
- ps: >-
+2
View File
@@ -18,6 +18,7 @@ add_filter(NAME src/application GROUPS
multiprocess
sanitychecks
selfupdater
updatedialog
)
add_filter(NAME src/browser GROUPS
@@ -64,6 +65,7 @@ add_filter(NAME src/downloads GROUPS
downloadlist
downloadlistview
downloadmanager
downloadmanager2
)
add_filter(NAME src/env GROUPS
+5 -6
View File
@@ -251,7 +251,7 @@ std::optional<int> CommandLine::runPostApplication(MOApplication& a)
env::Console c;
if (auto i=InstanceManager::singleton().currentInstance()) {
std::cout << i->name().toStdString() << "\n";
std::cout << i->displayName().toStdString() << "\n";
} else {
std::cout << "no instance configured\n";
}
@@ -289,9 +289,8 @@ std::optional<int> CommandLine::runPostOrganizer(OrganizerCore& core)
return 0;
}
catch (const std::exception &e) {
reportError(
QObject::tr("failed to start shortcut: %1").arg(e.what()));
catch (std::exception&) {
// user was already warned
return 1;
}
}
@@ -451,7 +450,7 @@ std::optional<QString> CommandLine::instance() const
// note that moshortcut:// overrides -i
if (m_shortcut.isValid() && m_shortcut.hasInstance()) {
return m_shortcut.instance();
return m_shortcut.instanceName();
} else if (m_vm.count("instance")) {
return QString::fromStdString(m_vm["instance"].as<std::string>());
}
@@ -817,7 +816,7 @@ std::optional<int> RunCommand::runPostOrganizer(OrganizerCore& core)
reportError(
QObject::tr("Executable '%1' not found in instance '%2'.")
.arg(program)
.arg(InstanceManager::singleton().currentInstance()->name()));
.arg(InstanceManager::singleton().currentInstance()->displayName()));
return 1;
}
File diff suppressed because it is too large Load Diff
+212
View File
@@ -0,0 +1,212 @@
#ifndef MODORGANIZER_CURLDOWNLOADER_INCLUDED
#define MODORGANIZER_CURLDOWNLOADER_INCLUDED
#include <ipluginrepository.h>
namespace dm::curl
{
namespace fs = std::filesystem;
using hr_clock = std::chrono::high_resolution_clock;
struct defer_t {};
extern const defer_t defer;
class GlobalHandle
{
public:
GlobalHandle();
~GlobalHandle();
GlobalHandle(const GlobalHandle&) = delete;
GlobalHandle& operator=(const GlobalHandle) = delete;
};
class EasyHandle
{
public:
EasyHandle();
EasyHandle(defer_t);
~EasyHandle();
EasyHandle(const EasyHandle&) = delete;
EasyHandle& operator=(const EasyHandle&) = delete;
bool create();
CURL* get() const;
private:
CURL* m_handle;
};
class MultiHandle
{
public:
MultiHandle();
MultiHandle(defer_t);
~MultiHandle();
MultiHandle(const MultiHandle&) = delete;
MultiHandle& operator=(const MultiHandle&) = delete;
bool create();
CURLM* get() const;
private:
CURLM* m_handle;
};
class FileHandle
{
public:
FileHandle();
~FileHandle();
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
bool opened() const;
std::size_t open(fs::path p, bool append);
void close();
bool write(std::string_view sv);
private:
HANDLE m_handle;
fs::path m_path;
bool doOpen(bool append);
};
struct SListDeleter
{
void operator()(curl_slist* p)
{
if (p) {
curl_slist_free_all(p);
}
}
};
using SList = std::unique_ptr<curl_slist, SListDeleter>;
class Download : public MOBase::IDownload
{
public:
Download(std::string url, Info info);
CURL* setup(curl_off_t maxSpeed);
CURL* handle() const;
States state() const override;
Stats stats() const override;
std::string stealBuffer();
QByteArray buffer() const override;
int httpCode() const override;
std::string error() const;
std::string debugName() const;
void start();
void stop() override;
bool finish(CURLcode code);
bool xfer(
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
bool header(std::string_view sv);
bool write(std::string_view data);
void debug(curl_infotype t, std::string_view data);
private:
std::string m_url;
Info m_info;
EasyHandle m_handle;
FileHandle m_out;
SList m_headers;
std::string m_buffer;
States m_state;
std::string m_error;
hr_clock::time_point m_lastCheck;
std::size_t m_bytes;
std::atomic<double> m_bytesPerSecond;
std::atomic<double> m_progress;
std::size_t resumeFrom();
bool rename();
fs::path outputFile() const;
static int s_xfer(
void* p,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
static size_t s_header(char* data, size_t size, size_t n, void* p);
static size_t s_write(char* data, size_t size, size_t n, void* p);
static int s_debug(CURL* h, curl_infotype t, char* data, size_t n, void *p);
};
class Downloader : public MOBase::IDownloader
{
public:
static const std::size_t NoLimit =
std::numeric_limits<std::size_t>::max();
Downloader();
~Downloader();
void cancel();
void stop();
void join();
void maxSpeed(std::size_t bytesPerSecond);
bool finished() const;
std::shared_ptr<MOBase::IDownload> add(
const QUrl& url, const MOBase::IDownload::Info& info={}) override;
private:
using DownloadList = std::list<std::shared_ptr<Download>>;
std::shared_ptr<GlobalHandle> m_global;
MultiHandle m_handle;
std::vector<std::shared_ptr<Download>> m_temp;
std::mutex m_tempMutex;
std::thread m_thread;
std::atomic<bool> m_cancel, m_stop, m_finished;
std::condition_variable m_cv;
DownloadList m_list;
std::map<CURL*, DownloadList::iterator> m_map;
std::atomic<std::size_t> m_maxSpeed;
void run();
void checkTemp();
void perform();
void poll();
bool start(std::shared_ptr<Download> d);
void setLimits();
void checkCancel();
void checkQueue();
bool cleanupActive();
DownloadList::iterator removeFromActive(DownloadList::iterator itor);
void stopOverMax(std::size_t max);
bool addFromQueue(std::size_t max);
curl_off_t maxSpeedPer() const;
};
} // namespace
#endif // MODORGANIZER_CURLDOWNLOADER_INCLUDED
File diff suppressed because it is too large Load Diff
+114
View File
@@ -0,0 +1,114 @@
#ifndef MODORGANIZER_DOWNLOADEDMANAGER2_INCLUDED
#define MODORGANIZER_DOWNLOADEDMANAGER2_INCLUDED
#include <ipluginrepository.h>
class PluginContainer;
namespace dm::curl { class Downloader; class Download; }
namespace dm
{
namespace fs = std::filesystem;
class DownloadManager2;
class Download
{
public:
enum States
{
None = 0,
Queueing,
Queued,
Finished,
Errored,
Running,
Pausing,
Paused,
Cancelling,
Cancelled
};
Download(DownloadManager2& dm, MOBase::IPluginRepository& repo, QString what);
const QString& what() const;
const QString& error() const;
States state() const;
bool start();
void cancel();
void pause();
void queue();
void tick();
QString debugName() const;
private:
DownloadManager2& m_dm;
MOBase::IPluginRepository& m_repo;
std::unique_ptr<MOBase::IRepositoryDownload> m_download;
QString m_what;
States m_state;
QString m_error;
void setState(States s);
void next();
};
class DownloadManager2
{
public:
static const std::size_t NoLimit =
std::numeric_limits<std::size_t>::max();
DownloadManager2(PluginContainer& pc);
~DownloadManager2();
void add(QString what);
void maxActive(std::size_t n);
void maxSpeed(std::size_t bytesPerSecond);
bool hasActive() const;
curl::Downloader& downloader();
private:
using DownloadList = std::list<std::shared_ptr<Download>>;
PluginContainer& m_pc;
std::thread m_thread;
std::atomic<bool> m_stop;
std::unique_ptr<curl::Downloader> m_downloader;
std::vector<std::shared_ptr<Download>> m_temp;
std::mutex m_tempMutex;
std::condition_variable m_cv;
DownloadList m_queued, m_active, m_inactive;
std::atomic<std::size_t> m_maxActive, m_maxSpeed;
std::atomic<bool> m_hasActive;
void run();
void checkTemp();
void checkQueue();
void cleanupActive();
void stopOverMax(std::size_t max);
void addFromQueue(std::size_t max);
};
} // namespace
#endif // MODORGANIZER_DOWNLOADEDMANAGER2_INCLUDED
+3 -5
View File
@@ -418,9 +418,9 @@ QString prependToPath(const QString& s)
return old;
}
QString setPath(const QString& s)
void setPath(const QString& s)
{
return set("PATH", s);
set("PATH", s);
}
QString get(const QString& name)
@@ -457,11 +457,9 @@ QString get(const QString& name)
return QString::fromWCharArray(buffer.get(), realSize);
}
QString set(const QString& n, const QString& v)
void set(const QString& n, const QString& v)
{
auto old = get(n);
::SetEnvironmentVariableW(n.toStdWString().c_str(), v.toStdWString().c_str());
return old;
}
+2 -2
View File
@@ -233,12 +233,12 @@ private:
// environment variables
//
QString get(const QString& name);
QString set(const QString& name, const QString& value);
void set(const QString& name, const QString& value);
QString path();
QString appendToPath(const QString& s);
QString prependToPath(const QString& s);
QString setPath(const QString& s);
void setPath(const QString& s);
class Service
+21 -5
View File
@@ -603,9 +603,16 @@ Process getProcessTreeFromProcess(HANDLE h)
std::vector<DWORD> processesInJob(HANDLE h)
{
for (int tries=0; tries<5; ++tries) {
DWORD maxIds = 100;
const int MaxTries = 5;
// doubled MaxTries times on failure
DWORD maxIds = 100;
// for logging
DWORD lastCount=0, lastAssigned=0;
for (int tries=0; tries<MaxTries; ++tries) {
const DWORD idsSize = sizeof(ULONG_PTR) * maxIds;
const DWORD bufferSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + idsSize;
@@ -617,8 +624,10 @@ std::vector<DWORD> processesInJob(HANDLE h)
if (!r) {
const auto e = GetLastError();
log::error("failed to get process ids in job, {}", formatSystemMessage(e));
return {};
if (e != ERROR_MORE_DATA) {
log::error("failed to get process ids in job, {}", formatSystemMessage(e));
return {};
}
}
if (ids->NumberOfProcessIdsInList >= ids->NumberOfAssignedProcesses) {
@@ -632,9 +641,16 @@ std::vector<DWORD> processesInJob(HANDLE h)
// try again with a larger buffer
maxIds *= 2;
// for logging
lastCount = ids->NumberOfProcessIdsInList;
lastAssigned = ids->NumberOfAssignedProcesses;
}
log::error("failed to get processes in job, can't get a buffer large enough");
log::error(
"failed to get processes in job, can't get a buffer large enough, "
"{}/{} ids", lastCount, lastAssigned);
return {};
}
+3 -1
View File
@@ -145,11 +145,13 @@ Shortcut::Shortcut()
Shortcut::Shortcut(const Executable& exe)
: Shortcut()
{
const auto i = *InstanceManager::singleton().currentInstance();
m_name = exe.title();
m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
m_arguments = QString("\"moshortcut://%1:%2\"")
.arg(InstanceManager::singleton().currentInstance()->name())
.arg(i.isPortable() ? "" : i.displayName())
.arg(exe.title());
m_description = QString("Run %1 with ModOrganizer").arg(exe.title());
+8 -8
View File
@@ -48,7 +48,7 @@ Instance::Instance(QString dir, bool portable, QString profileName) :
{
}
QString Instance::name() const
QString Instance::displayName() const
{
if (isPortable())
return QObject::tr("Portable");
@@ -105,7 +105,7 @@ bool Instance::isActive() const
if (m_portable) {
return i->isPortable();
} else {
return (i->name() == name());
return (i->displayName() == displayName());
}
}
@@ -822,7 +822,7 @@ SetupInstanceResults selectGame(Instance& instance, PluginContainer& pc)
CreateInstanceDialog dlg(pc, nullptr);
// only show the game page
dlg.setSinglePage<cid::GamePage>(instance.name());
dlg.setSinglePage<cid::GamePage>(instance.displayName());
dlg.show();
dlg.activateWindow();
@@ -860,7 +860,7 @@ SetupInstanceResults selectVariant(Instance& instance, PluginContainer& pc)
instance.gamePlugin(), instance.gameDirectory());
// only show the variant page
dlg.setSinglePage<cid::VariantsPage>(instance.name());
dlg.setSinglePage<cid::VariantsPage>(instance.displayName());
dlg.show();
dlg.activateWindow();
@@ -897,7 +897,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
reportError(
QObject::tr("Cannot open instance '%1', failed to read INI file %2.")
.arg(instance.name()).arg(instance.iniPath()));
.arg(instance.displayName()).arg(instance.iniPath()));
return SetupInstanceResults::SelectAnother;
}
@@ -914,7 +914,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
QObject::tr(
"Cannot open instance '%1', the managed game was not found in the INI "
"file %2. Select the game managed by this instance.")
.arg(instance.name()).arg(instance.iniPath()));
.arg(instance.displayName()).arg(instance.iniPath()));
return selectGame(instance, pc);
}
@@ -928,7 +928,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
QObject::tr(
"Cannot open instance '%1', the game plugin '%2' doesn't exist. It "
"may have been deleted by an antivirus. Select another instance.")
.arg(instance.name()).arg(instance.gameName()));
.arg(instance.displayName()).arg(instance.gameName()));
return SetupInstanceResults::SelectAnother;
}
@@ -943,7 +943,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc)
"Cannot open instance '%1', the game directory '%2' doesn't exist or "
"the game plugin '%3' doesn't recognize it. Select the game managed "
"by this instance.")
.arg(instance.name())
.arg(instance.displayName())
.arg(instance.gameDirectory())
.arg(instance.gameName()));
+6 -1
View File
@@ -132,9 +132,14 @@ public:
// returns the instance name; this is the directory name or "Portable" for
// portable instances
//
// be careful when using this function to check whether two instances are the
// same, some parts of MO use an empty string to represent portable instances,
// but this function will return "Portable" for them; it's safer to check
// for isPortable() first
//
// can be called without setup()
//
QString name() const;
QString displayName() const;
// returns either:
// 1) the game name from the INI, if readFromIni() was called;
+8 -8
View File
@@ -221,7 +221,7 @@ void InstanceManagerDialog::updateInstances()
// sort first, prepend portable after so it's always on top
std::sort(m_instances.begin(), m_instances.end(), [](auto&& a, auto&& b) {
return (MOBase::naturalCompare(a->name(), b->name()) < 0);
return (MOBase::naturalCompare(a->displayName(), b->displayName()) < 0);
});
if (m.portableInstanceExists()) {
@@ -249,7 +249,7 @@ void InstanceManagerDialog::updateList()
for (std::size_t i=0; i<m_instances.size(); ++i) {
const auto& ii = *m_instances[i];
auto* item = new QStandardItem(ii.name());
auto* item = new QStandardItem(ii.displayName());
item->setIcon(instanceIcon(m_pc, ii));
m_model->appendRow(item);
@@ -295,7 +295,7 @@ void InstanceManagerDialog::select(std::size_t i)
void InstanceManagerDialog::select(const QString& name)
{
for (std::size_t i=0; i<m_instances.size(); ++i) {
if (m_instances[i]->name() == name) {
if (m_instances[i]->displayName() == name) {
select(i);
return;
}
@@ -310,7 +310,7 @@ void InstanceManagerDialog::selectActiveInstance()
if (active) {
for (std::size_t i=0; i<m_instances.size(); ++i) {
if (m_instances[i]->name() == active->name()) {
if (m_instances[i]->displayName() == active->displayName()) {
select(i);
ui->list->scrollTo(
@@ -340,7 +340,7 @@ void InstanceManagerDialog::openSelectedInstance()
if (to.isPortable()) {
InstanceManager::singleton().setCurrentInstance("");
} else {
InstanceManager::singleton().setCurrentInstance(to.name());
InstanceManager::singleton().setCurrentInstance(to.displayName());
}
if (m_restartOnSelect) {
@@ -372,7 +372,7 @@ bool InstanceManagerDialog::confirmSwitch(const Instance& to)
const auto r = dlg
.title(tr("Switching instances"))
.main(tr("Mod Organizer must restart to manage the instance '%1'.")
.arg(to.name()))
.arg(to.displayName()))
.content(tr("This confirmation can be disabled in the settings."))
.icon(QMessageBox::Question)
.button({tr("Restart Mod Organizer"), QMessageBox::Ok})
@@ -401,7 +401,7 @@ void InstanceManagerDialog::rename()
// getting new name
const auto newName = getInstanceName(
this, tr("Rename instance"), "", tr("Instance name"), i->name());
this, tr("Rename instance"), "", tr("Instance name"), i->displayName());
if (newName.isEmpty()) {
return;
@@ -661,7 +661,7 @@ const Instance* InstanceManagerDialog::singleSelection() const
void InstanceManagerDialog::fillData(const Instance& ii)
{
ui->name->setText(ii.name());
ui->name->setText(ii.displayName());
ui->location->setText(ii.directory());
ui->baseDirectory->setText(ii.baseDirectory());
ui->gameName->setText(ii.gameName());
+8 -2
View File
@@ -7,6 +7,7 @@
#include "instancemanager.h"
#include "thread_utils.h"
#include "shared/util.h"
#include "downloadmanager2.h"
#include <report.h>
#include <log.h>
@@ -80,7 +81,8 @@ int run(int argc, char *argv[])
// stuff that's done only once, even if MO restarts in the loop below
app.firstTimeSetup(multiProcess);
// force the "Select instance" dialog on startup (only for first loop)
// force the "Select instance" dialog on startup, only for first loop or when
// the current instance cannot be used
bool pick = cl.pick();
// MO runs in a loop because it can be restarted in several ways, such as
@@ -105,13 +107,17 @@ int run(int argc, char *argv[])
const auto r = app.setup(multiProcess, pick);
pick = false;
if (r == RestartExitCode) {
if (r == RestartExitCode || r == ReselectExitCode) {
// resets things when MO is "restarted"
app.resetForRestart();
// don't reprocess command line
cl.clear();
if (r == ReselectExitCode) {
pick = true;
}
continue;
} else if (r != 0) {
// something failed, quit
+1 -5
View File
@@ -2951,7 +2951,6 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
}
std::vector<ModInfo::Ptr> modsList = ModInfo::getByModID(gameNameReal, modID);
for (auto mod : modsList) {
bool foundUpdate = false;
QDateTime now = QDateTime::currentDateTimeUtc();
QDateTime updateTarget = mod->getExpires();
if (now >= updateTarget) {
@@ -2959,7 +2958,6 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
// with an older version than the main mod version.
if (mod->getNexusFileStatus() != 3 && mod->getNexusFileStatus() != 5) {
mod->setNewestVersion(result["version"].toString());
foundUpdate = true;
}
// update the LastNexusUpdate time in any case since we did perform the check.
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
@@ -2979,9 +2977,7 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC));
mod->saveMeta();
if (foundUpdate) {
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
}
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
}
}
+11 -7
View File
@@ -392,10 +392,14 @@ void MOApplication::externalMessage(const QString& message)
if (moshortcut.isValid()) {
if(moshortcut.hasExecutable()) {
m_core->processRunner()
.setFromShortcut(moshortcut)
.setWaitForCompletion(ProcessRunner::TriggerRefresh)
.run();
try {
m_core->processRunner()
.setFromShortcut(moshortcut)
.setWaitForCompletion(ProcessRunner::TriggerRefresh)
.run();
} catch(std::exception&) {
// user was already warned
}
}
} else if (isNxmLink(message)) {
MessageDialog::showMessage(tr("Download started"), qApp->activeWindow(), false);
@@ -414,11 +418,11 @@ void MOApplication::externalMessage(const QString& message)
if (auto i=cl.instance()) {
const auto ci = InstanceManager::singleton().currentInstance();
if (*i != ci->name()) {
if (*i != ci->displayName()) {
reportError(tr(
"This shortcut or command line is for instance '%1', but the current "
"instance is '%2'.")
.arg(*i).arg(ci->name()));
.arg(*i).arg(ci->displayName()));
return;
}
@@ -488,7 +492,7 @@ std::optional<int> MOApplication::setupInstanceLoop(
continue;
} else if (setupResult == SetupInstanceResults::SelectAnother) {
InstanceManager::singleton().clearCurrentInstance();
return RestartExitCode;
return ReselectExitCode;
} else {
return 1;
}
-5
View File
@@ -897,11 +897,6 @@ public: // Conflicts
//
virtual const std::set<unsigned int>& getModArchiveLooseOverwritten() const { return s_EmptySet; }
/**
* @brief Update conflict information.
*/
virtual void doConflictCheck() const {}
public slots:
/**
+39 -68
View File
@@ -19,11 +19,11 @@ ModInfoWithConflictInfo::ModInfoWithConflictInfo(OrganizerCore& core) :
m_FileTree([this]() { return QDirFileTree::makeTree(absolutePath()); }),
m_Valid([this]() { return doIsValid(); }),
m_Contents([this]() { return doGetContents(); }),
m_HasLooseOverwrite(false), m_HasHiddenFiles(false) {}
m_Conflicts([this]() { return doConflictCheck(); }) { }
void ModInfoWithConflictInfo::clearCaches()
{
m_LastConflictCheck = QTime();
m_Conflicts.invalidate();
}
std::vector<ModInfo::EFlag> ModInfoWithConflictInfo::getFlags() const
@@ -82,14 +82,9 @@ std::vector<ModInfo::EConflictFlag> ModInfoWithConflictInfo::getConflictFlags()
}
void ModInfoWithConflictInfo::doConflictCheck() const
ModInfoWithConflictInfo::Conflicts ModInfoWithConflictInfo::doConflictCheck() const
{
m_OverwriteList.clear();
m_OverwrittenList.clear();
m_ArchiveOverwriteList.clear();
m_ArchiveOverwrittenList.clear();
m_ArchiveLooseOverwriteList.clear();
m_ArchiveLooseOverwrittenList.clear();
Conflicts conflicts;
bool providesAnything = false;
bool hasHiddenFiles = false;
@@ -102,10 +97,6 @@ void ModInfoWithConflictInfo::doConflictCheck() const
std::wstring name = ToWString(this->name());
const std::wstring hideExt = ToWString(ModInfo::s_HiddenExt);
m_CurrentConflictState = CONFLICT_NONE;
m_ArchiveConflictState = CONFLICT_NONE;
m_ArchiveConflictLooseState = CONFLICT_NONE;
if (m_Core.directoryStructure()->originExists(name)) {
FilesOrigin &origin = m_Core.directoryStructure()->getOriginByName(name);
std::vector<FileEntryPtr> files = origin.getFiles();
@@ -168,12 +159,12 @@ void ModInfoWithConflictInfo::doConflictCheck() const
unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName()));
if (!file->isFromArchive()) {
if (!archiveData.isValid())
m_OverwrittenList.insert(altIndex);
conflicts.m_OverwrittenList.insert(altIndex);
else
m_ArchiveLooseOverwrittenList.insert(altIndex);
conflicts.m_ArchiveLooseOverwrittenList.insert(altIndex);
}
else {
m_ArchiveOverwrittenList.insert(altIndex);
conflicts.m_ArchiveOverwrittenList.insert(altIndex);
}
} else {
providesAnything = true;
@@ -188,21 +179,21 @@ void ModInfoWithConflictInfo::doConflictCheck() const
if (!altInfo.isFromArchive()) {
if (!archiveData.isValid()) {
if (origin.getPriority() > altOrigin.getPriority()) {
m_OverwriteList.insert(altIndex);
conflicts.m_OverwriteList.insert(altIndex);
} else {
m_OverwrittenList.insert(altIndex);
conflicts.m_OverwrittenList.insert(altIndex);
}
} else {
m_ArchiveLooseOverwrittenList.insert(altIndex);
conflicts.m_ArchiveLooseOverwrittenList.insert(altIndex);
}
} else {
if (!archiveData.isValid()) {
m_ArchiveLooseOverwriteList.insert(altIndex);
conflicts.m_ArchiveLooseOverwriteList.insert(altIndex);
} else {
if (archiveData.order() > altInfo.archive().order()) {
m_ArchiveOverwriteList.insert(altIndex);
conflicts.m_ArchiveOverwriteList.insert(altIndex);
} else if (archiveData.order() < altInfo.archive().order()) {
m_ArchiveOverwrittenList.insert(altIndex);
conflicts.m_ArchiveOverwrittenList.insert(altIndex);
}
}
}
@@ -210,66 +201,51 @@ void ModInfoWithConflictInfo::doConflictCheck() const
}
}
}
m_LastConflictCheck = QTime::currentTime();
if (files.size() != 0) {
if (!providesAnything)
m_CurrentConflictState = CONFLICT_REDUNDANT;
else if (!m_OverwriteList.empty() && !m_OverwrittenList.empty())
m_CurrentConflictState = CONFLICT_MIXED;
else if (!m_OverwriteList.empty())
m_CurrentConflictState = CONFLICT_OVERWRITE;
else if (!m_OverwrittenList.empty())
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
conflicts.m_CurrentConflictState = CONFLICT_REDUNDANT;
else if (!conflicts.m_OverwriteList.empty() && !conflicts.m_OverwrittenList.empty())
conflicts.m_CurrentConflictState = CONFLICT_MIXED;
else if (!conflicts.m_OverwriteList.empty())
conflicts.m_CurrentConflictState = CONFLICT_OVERWRITE;
else if (!conflicts.m_OverwrittenList.empty())
conflicts.m_CurrentConflictState = CONFLICT_OVERWRITTEN;
if (!m_ArchiveOverwriteList.empty() && !m_ArchiveOverwrittenList.empty())
m_ArchiveConflictState = CONFLICT_MIXED;
else if (!m_ArchiveOverwriteList.empty())
m_ArchiveConflictState = CONFLICT_OVERWRITE;
else if (!m_ArchiveOverwrittenList.empty())
m_ArchiveConflictState = CONFLICT_OVERWRITTEN;
if (!conflicts.m_ArchiveOverwriteList.empty() && !conflicts.m_ArchiveOverwrittenList.empty())
conflicts.m_ArchiveConflictState = CONFLICT_MIXED;
else if (!conflicts.m_ArchiveOverwriteList.empty())
conflicts.m_ArchiveConflictState = CONFLICT_OVERWRITE;
else if (!conflicts.m_ArchiveOverwrittenList.empty())
conflicts.m_ArchiveConflictState = CONFLICT_OVERWRITTEN;
if (!m_ArchiveLooseOverwrittenList.empty() && !m_ArchiveLooseOverwriteList.empty())
m_ArchiveConflictLooseState = CONFLICT_MIXED;
else if (!m_ArchiveLooseOverwrittenList.empty())
m_ArchiveConflictLooseState = CONFLICT_OVERWRITTEN;
else if (!m_ArchiveLooseOverwriteList.empty())
m_ArchiveConflictLooseState = CONFLICT_OVERWRITE;
if (!conflicts.m_ArchiveLooseOverwrittenList.empty() && !conflicts.m_ArchiveLooseOverwriteList.empty())
conflicts.m_ArchiveConflictLooseState = CONFLICT_MIXED;
else if (!conflicts.m_ArchiveLooseOverwrittenList.empty())
conflicts.m_ArchiveConflictLooseState = CONFLICT_OVERWRITTEN;
else if (!conflicts.m_ArchiveLooseOverwriteList.empty())
conflicts.m_ArchiveConflictLooseState = CONFLICT_OVERWRITE;
m_HasHiddenFiles = hasHiddenFiles;
conflicts.m_HasHiddenFiles = hasHiddenFiles;
}
}
return conflicts;
}
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const
{
// this is costy so cache the result
QTime now = QTime::currentTime();
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
doConflictCheck();
}
return m_CurrentConflictState;
return m_Conflicts.value().m_CurrentConflictState;
}
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isArchiveConflicted() const
{
QTime now = QTime::currentTime();
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
doConflictCheck();
}
return m_ArchiveConflictState;
return m_Conflicts.value().m_ArchiveConflictState;
}
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isLooseArchiveConflicted() const
{
QTime now = QTime::currentTime();
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
doConflictCheck();
}
return m_ArchiveConflictLooseState;
return m_Conflicts.value().m_ArchiveConflictLooseState;
}
@@ -294,12 +270,7 @@ bool ModInfoWithConflictInfo::isRedundant() const
bool ModInfoWithConflictInfo::hasHiddenFiles() const
{
QTime now = QTime::currentTime();
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
doConflictCheck();
}
return m_HasHiddenFiles;
return m_Conflicts.value().m_HasHiddenFiles;
}
void ModInfoWithConflictInfo::diskContentModified() {
+26 -27
View File
@@ -54,14 +54,12 @@ public:
*/
void clearCaches() override;
const std::set<unsigned int>& getModOverwrite() const override { return m_OverwriteList; }
const std::set<unsigned int>& getModOverwritten() const override { return m_OverwrittenList; }
const std::set<unsigned int>& getModArchiveOverwrite() const override { return m_ArchiveOverwriteList; }
const std::set<unsigned int>& getModArchiveOverwritten() const override { return m_ArchiveOverwrittenList; }
const std::set<unsigned int>& getModArchiveLooseOverwrite() const override { return m_ArchiveLooseOverwriteList; }
const std::set<unsigned int>& getModArchiveLooseOverwritten() const override { return m_ArchiveLooseOverwrittenList; }
void doConflictCheck() const override;
const std::set<unsigned int>& getModOverwrite() const override { return m_Conflicts.value().m_OverwriteList; }
const std::set<unsigned int>& getModOverwritten() const override { return m_Conflicts.value().m_OverwrittenList; }
const std::set<unsigned int>& getModArchiveOverwrite() const override { return m_Conflicts.value().m_ArchiveOverwriteList; }
const std::set<unsigned int>& getModArchiveOverwritten() const override { return m_Conflicts.value().m_ArchiveOverwrittenList; }
const std::set<unsigned int>& getModArchiveLooseOverwrite() const override { return m_Conflicts.value().m_ArchiveLooseOverwriteList; }
const std::set<unsigned int>& getModArchiveLooseOverwritten() const override { return m_Conflicts.value().m_ArchiveLooseOverwrittenList; }
public slots:
@@ -72,11 +70,8 @@ public slots:
protected:
/**
* @brief Check if the content of this mod is valid.
*
* @return true if the content is valid, false otherwise.
**/
// check if the content of this mod is valid
//
virtual bool doIsValid() const;
/**
@@ -136,23 +131,27 @@ protected:
private:
struct Conflicts {
EConflictType m_CurrentConflictState = CONFLICT_NONE;
EConflictType m_ArchiveConflictState = CONFLICT_NONE;
EConflictType m_ArchiveConflictLooseState = CONFLICT_NONE;
bool m_HasLooseOverwrite = false;
bool m_HasHiddenFiles = false;
std::set<unsigned int> m_OverwriteList; // indices of mods overritten by this mod
std::set<unsigned int> m_OverwrittenList; // indices of mods overwriting this mod
std::set<unsigned int> m_ArchiveOverwriteList; // indices of mods with archive files overritten by this mod
std::set<unsigned int> m_ArchiveOverwrittenList; // indices of mods with archive files overwriting this mod
std::set<unsigned int> m_ArchiveLooseOverwriteList; // indices of mods with archives being overwritten by this mod's loose files
std::set<unsigned int> m_ArchiveLooseOverwrittenList; // indices of mods with loose files overwriting this mod's archive files
};
Conflicts doConflictCheck() const;
MOBase::MemoizedLocked<std::shared_ptr<const MOBase::IFileTree>> m_FileTree;
MOBase::MemoizedLocked<bool> m_Valid;
MOBase::MemoizedLocked<std::set<int>> m_Contents;
mutable EConflictType m_CurrentConflictState;
mutable EConflictType m_ArchiveConflictState;
mutable EConflictType m_ArchiveConflictLooseState;
mutable bool m_HasLooseOverwrite;
mutable bool m_HasHiddenFiles;
mutable QTime m_LastConflictCheck;
mutable std::set<unsigned int> m_OverwriteList; // indices of mods overritten by this mod
mutable std::set<unsigned int> m_OverwrittenList; // indices of mods overwriting this mod
mutable std::set<unsigned int> m_ArchiveOverwriteList; // indices of mods with archive files overritten by this mod
mutable std::set<unsigned int> m_ArchiveOverwrittenList; // indices of mods with archive files overwriting this mod
mutable std::set<unsigned int> m_ArchiveLooseOverwriteList; // indices of mods with archives being overwritten by this mod's loose files
mutable std::set<unsigned int> m_ArchiveLooseOverwrittenList; // indices of mods with loose files overwriting this mod's archive files
MOBase::MemoizedLocked<Conflicts> m_Conflicts;
};

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