From e158038cfc8784ae46704b8289affb6778b2c78a Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 21 Sep 2013 19:25:33 +0200 Subject: [PATCH] - esp reader now handles invalid files more gracefully - files moved will now also be treated as "deleted" in the old location so a newly created file with that same name is not created in overwrite - introduced a mechanism by which MO can recognize if it crashed before when attempting to load a plugin. That plugin can be blacklisted so it doesn't get loaded again - plugins can now programaticaly change their settings - plugins can now store data persistently without exposing that data as settings - requesting an unset-setting from a plugin is no longer treated as a bug - clarified warning message for when files are in overwrite directory - the proxyPython plugin will now discover if python initialization crashed MO on a previous session and give the user a chance to fix it or disable the plugin - bugfix: GetModuleFileName modified the buffer past the zero termination. While this doesn't violate the API documentation it is different from the regular windows implementation - bugfix: proxy plugins couldn't access the parent widget - bugfix: when moving a file from overwrite to a mod the in-memory file structure wasn't updated - bugfix: name input dialog for profiles allowed names that weren't valid directory names - bugfix: profile dialog wasn't able to delete profiles if the name started or ended in whitespaces - bugfix: The name-cells for plugin settings could be changed (without effect) - removed a few obsolete files from the repository --- src/proxy/proxypython.cpp | 49 ++++++++++++++++++++++++++++++++----- src/proxy/proxypython.h | 4 ++- src/runner/pythonrunner.cpp | 7 +++++- src/runner/uibasewrappers.h | 3 +++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/proxy/proxypython.cpp b/src/proxy/proxypython.cpp index de2c693..fe2f4c7 100644 --- a/src/proxy/proxypython.cpp +++ b/src/proxy/proxypython.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "resource.h" @@ -85,6 +86,10 @@ typedef IPythonRunner* (*CreatePythonRunner_func)(const MOBase::IOrganizer *moIn bool ProxyPython::init(IOrganizer *moInfo) { m_MOInfo = moInfo; + if (!m_MOInfo->pluginSetting(name(), "enabled").toBool()) { + m_LoadFailure = FAIL_NONE; + return false; + } m_LoadFailure = FAIL_OTHER; @@ -95,7 +100,6 @@ bool ProxyPython::init(IOrganizer *moInfo) return true; } - m_TempRunnerFile = ExtractResource(IDR_LOADER_DLL, "__pythonRunner.dll"); m_RunnerLib = ::LoadLibraryW(ToWString(m_TempRunnerFile).c_str()); if (m_RunnerLib != NULL) { @@ -103,7 +107,28 @@ bool ProxyPython::init(IOrganizer *moInfo) if (CreatePythonRunner == NULL) { throw MyException("embedded dll is invalid: " + windowsErrorString(::GetLastError())); } + if (m_MOInfo->persistent(name(), "tryInit", false).toBool()) { + if (pythonPath.isEmpty()) { + m_LoadFailure = FAIL_PYTHONDETECTION; + } else { + m_LoadFailure = FAIL_WRONGPYTHONPATH; + } + if (QMessageBox::question(parentWidget(), tr("Python Initialization failed"), + tr("On a previous start the Python Plugin failed to initialize.\n" + "Either the value in Settings->Plugins->ProxyPython->plugin_dir is set incorrectly or it is empty and auto-detection doesn't work " + "for whatever reason.\n" + "Do you want to try initializing python again (at the risk of another crash)?\n" + "Suggestion: Select \"no\", and click the warning sign for further help. Afterwards you have to re-enable the python plugin."), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) { + m_MOInfo->setPluginSetting(name(), "enabled", false); + return true; + } + } + + m_MOInfo->setPersistent(name(), "tryInit", true); m_Runner = CreatePythonRunner(moInfo, pythonPath); + m_MOInfo->setPersistent(name(), "tryInit", false); + if (m_Runner != NULL) { m_LoadFailure = FAIL_NONE; } else { @@ -137,7 +162,7 @@ QString ProxyPython::description() const VersionInfo ProxyPython::version() const { - return VersionInfo(1, 1, 1, VersionInfo::RELEASE_FINAL); + return VersionInfo(1, 2, 0, VersionInfo::RELEASE_FINAL); } bool ProxyPython::isActive() const @@ -149,6 +174,7 @@ QList ProxyPython::settings() const { QList result; result.push_back(PluginSetting("python_dir", "Path to your python installation. Leave empty for auto-detection", "")); + result.push_back(PluginSetting("enabled", "Set to true to enable support for python plugins", true)); return result; } @@ -183,6 +209,8 @@ std::vector ProxyPython::activeProblems() const result.push_back(PROBLEM_PYTHONMISSING); } else if (m_LoadFailure == FAIL_WRONGPYTHONPATH) { result.push_back(PROBLEM_WRONGPYTHONPATH); + } else if (m_LoadFailure == FAIL_PYTHONDETECTION) { + result.push_back(PROBLEM_PYTHONDETECTION); } else if (m_LoadFailure == FAIL_INITFAIL) { result.push_back(PROBLEM_INITFAIL); } else if (m_Runner != NULL) { @@ -213,6 +241,9 @@ QString ProxyPython::shortDescription(unsigned int key) const case PROBLEM_INITFAIL: { return tr("Initializing Python failed"); } break; + case PROBLEM_PYTHONDETECTION: { + return tr("Python auto-detection failed"); + } break; default: throw MyException(tr("invalid problem key %1").arg(key)); } @@ -231,12 +262,16 @@ QString ProxyPython::fullDescription(unsigned int key) const case PROBLEM_PYTHONWRONGVERSION: { return tr("Your installed python version has a different version than 2.7. " "Some MO plugins may not work.
" - "If you have multiple versions of python installed you may have to configure the path to 2.7 " + "If you have multiple versions of python installed you may have to configure the path to 2.7 (32 bit) " "in the settings dialog.
" "This is only required to use some extended functionality in MO, you do not need Python to play the game."); } break; case PROBLEM_WRONGPYTHONPATH: { - return tr("Please set python_dir in Settings->Plugins->ProxyPython to the path of your python 2.7 installation."); + return tr("Please set python_dir in Settings->Plugins->ProxyPython to the path of your python 2.7 (32 bit) installation."); + } break; + case PROBLEM_PYTHONDETECTION: { + return tr("The auto-detection of the python path failed. I don't know why this would happen but you can try to fix it " + "by setting python_dir in Settings->Plugins->ProxyPython to the path of your python 2.7 (32 bit) installation."); } break; case PROBLEM_INITFAIL: { return tr("Sorry, I don't know any details. Most likely your python installation is not supported."); @@ -251,9 +286,11 @@ bool ProxyPython::hasGuidedFix(unsigned int key) const return (key == PROBLEM_PYTHONMISSING) || (key == PROBLEM_PYTHONWRONGVERSION); } -void ProxyPython::startGuidedFix(unsigned int) const +void ProxyPython::startGuidedFix(unsigned int key) const { - ::ShellExecuteA(NULL, "open", s_DownloadPythonURL, NULL, NULL, SW_SHOWNORMAL); + if ((key == PROBLEM_PYTHONMISSING) || (key == PROBLEM_PYTHONWRONGVERSION)) { + ::ShellExecuteA(NULL, "open", s_DownloadPythonURL, NULL, NULL, SW_SHOWNORMAL); + } } diff --git a/src/proxy/proxypython.h b/src/proxy/proxypython.h index f2d15c6..31e808d 100644 --- a/src/proxy/proxypython.h +++ b/src/proxy/proxypython.h @@ -52,9 +52,10 @@ private: static const unsigned int PROBLEM_PYTHONWRONGVERSION = 2; static const unsigned int PROBLEM_WRONGPYTHONPATH = 3; static const unsigned int PROBLEM_INITFAIL = 4; + static const unsigned int PROBLEM_PYTHONDETECTION = 5; static const char *s_DownloadPythonURL; - const MOBase::IOrganizer *m_MOInfo; + MOBase::IOrganizer *m_MOInfo; QString m_TempRunnerFile; HMODULE m_RunnerLib; IPythonRunner *m_Runner; @@ -65,6 +66,7 @@ private: FAIL_MISSINGDEPENDENCIES, FAIL_INITFAIL, FAIL_WRONGPYTHONPATH, + FAIL_PYTHONDETECTION, FAIL_OTHER } m_LoadFailure; diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 7b50dbb..842cf07 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -572,6 +572,9 @@ BOOST_PYTHON_MODULE(mobase) .def("removeMod", bpy::pure_virtual(&MOBase::IOrganizer::removeMod)) .def("modDataChanged", bpy::pure_virtual(&MOBase::IOrganizer::modDataChanged)) .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) + .def("setPluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) + .def("persistent", bpy::pure_virtual(&IOrganizer::persistent)) + .def("setPersistent", bpy::pure_virtual(&IOrganizer::setPersistent)) .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) .def("installMod", bpy::pure_virtual(&IOrganizer::installMod)) .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()); @@ -642,10 +645,12 @@ bool PythonRunner::initPython(const QString &pythonPath) Py_SetProgramName(argv0); PyImport_AppendInittab("mobase", &initmobase); - Py_Initialize(); + + Py_InitializeEx(0); if (!Py_IsInitialized()) { return false; } + PySys_SetArgv(0, &argv0); bpy::object main_module = bpy::import("__main__"); diff --git a/src/runner/uibasewrappers.h b/src/runner/uibasewrappers.h index 828a3a5..275931d 100644 --- a/src/runner/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -133,6 +133,9 @@ struct IOrganizerWrapper: MOBase::IOrganizer, boost::python::wrapperget_override("removeMod")(mod); } virtual void modDataChanged(MOBase::IModInterface *mod) { this->get_override("modDataChanged")(mod); } virtual QVariant pluginSetting(const QString &pluginName, const QString &key) const { return this->get_override("pluginSetting")(pluginName, key); } + virtual void setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value) { this->get_override("setPluginSetting")(pluginName, key, value); } + virtual QVariant persistent(const QString &pluginName, const QString &key, const QVariant &def = QVariant()) const { return this->get_override("persistent")(pluginName, key, def); } + virtual void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync = true) { this->get_override("setPersistent")(pluginName, key, value, sync); } virtual QString pluginDataPath() const { return this->get_override("pluginDataPath")(); } virtual void installMod(const QString &fileName) { this->get_override("installMod")(fileName); } virtual MOBase::IDownloadManager *downloadManager() { return this->get_override("downloadManager")(); }