Compare commits

..
Author SHA1 Message Date
Tannin e158038cfc - 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
2013-09-21 19:25:33 +02:00
Tannin d4c424911a - bugfix: testing for missing masters at the wrong time seems to have caused crashes
- bugfix: mod list is now written to a temporary file first. Only on success is the original file overwritten
- bugfix: moving a mod priority to just above the overwrite could cause a crash or error message
- bugfix: versions with a release candidate number weren't sorted correctly (woops)
- bugfix: staging script didn't include archive.dll and dlls.manifest
- installation time on overwrite no longer updates constantly
2013-09-16 22:32:42 +02:00
Tannin b9c2a18f86 updated versions 2013-09-05 21:14:08 +02:00
Tannin 224627515a - bugfix: automatically removes a file from old NCC release that was interfering with the current version
- bugfix: fomod installer didn't find fomod files in nested folder
- bugfix: python proxy will now not even try to initialize python if python_dir contains no python.
This is necessary because the python interpreter crashes the application if the path is invalid
2013-09-05 21:04:20 +02:00
7 changed files with 133 additions and 30 deletions
+4
View File
@@ -1,3 +1,7 @@
#include "resource.h"
#ifdef _DEBUG
IDR_LOADER_DLL BINARY MOVEABLE PURE "..\\..\\pythonRunner\\debug\\pythonRunner.dll"
#else // _DEBUG
IDR_LOADER_DLL BINARY MOVEABLE PURE "..\\..\\pythonRunner\\release\\pythonRunner.dll"
#endif // _DEBUG
+2 -3
View File
@@ -14,7 +14,6 @@ contains(QT_VERSION, "^5.*") {
CONFIG += plugins
CONFIG += dll
CONFIG += warn_on
CONFIG(release, debug|release) {
QMAKE_CXXFLAGS += /Zi
@@ -46,5 +45,5 @@ WINPWD ~= s,/,$$QMAKE_DIR_SEP,g
QMAKE_POST_LINK += copy $$(PYTHONPATH)\\lib\\site-packages\\sip.pyd $$quote($$DSTDIR)\\plugins\\data\\ $$escape_expand(\\n)
QMAKE_POST_LINK += copy $$(PYTHONPATH)\\lib\\site-packages\\PyQt4\\QtCore.pyd $$quote($$DSTDIR)\\plugins\\data\\ $$escape_expand(\\n)
QMAKE_POST_LINK += copy $$(PYTHONPATH)\\lib\\site-packages\\PyQt4\\QtGui.pyd $$quote($$DSTDIR)\\plugins\\data\\ $$escape_expand(\\n)
QMAKE_POST_LINK += copy $$(PYTHONPATH)\\lib\\site-packages\\PyQt4\\QtCore.pyd $$quote($$DSTDIR)\\plugins\\data\\PyQt4\\ $$escape_expand(\\n)
QMAKE_POST_LINK += copy $$(PYTHONPATH)\\lib\\site-packages\\PyQt4\\QtGui.pyd $$quote($$DSTDIR)\\plugins\\data\\PyQt4\\ $$escape_expand(\\n)
+89 -21
View File
@@ -11,6 +11,7 @@
#include <QtPlugin>
#include <QDirIterator>
#include <QWidget>
#include <QMessageBox>
#include "resource.h"
@@ -85,9 +86,20 @@ 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;
QString pythonPath = m_MOInfo->pluginSetting(name(), "python_dir").toString();
if (!pythonPath.isEmpty() && !QFile::exists(pythonPath + "/python.exe")) {
m_LoadFailure = FAIL_WRONGPYTHONPATH;
return true;
}
m_TempRunnerFile = ExtractResource(IDR_LOADER_DLL, "__pythonRunner.dll");
m_RunnerLib = ::LoadLibraryW(ToWString(m_TempRunnerFile).c_str());
if (m_RunnerLib != NULL) {
@@ -95,8 +107,33 @@ bool ProxyPython::init(IOrganizer *moInfo)
if (CreatePythonRunner == NULL) {
throw MyException("embedded dll is invalid: " + windowsErrorString(::GetLastError()));
}
m_Runner = CreatePythonRunner(moInfo, m_MOInfo->pluginSetting(name(), "python_dir").toString());
m_LoadFailure = FAIL_NONE;
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 {
m_LoadFailure = FAIL_INITFAIL;
}
return true;
} else {
DWORD error = ::GetLastError();
@@ -125,7 +162,7 @@ QString ProxyPython::description() const
VersionInfo ProxyPython::version() const
{
return VersionInfo(1, 1, 0, VersionInfo::RELEASE_FINAL);
return VersionInfo(1, 2, 0, VersionInfo::RELEASE_FINAL);
}
bool ProxyPython::isActive() const
@@ -137,6 +174,7 @@ QList<PluginSetting> ProxyPython::settings() const
{
QList<PluginSetting> 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;
}
@@ -156,7 +194,8 @@ QStringList ProxyPython::pluginList(const QString &pluginPath) const
QObject *ProxyPython::instantiate(const QString &pluginName)
{
if (m_Runner != NULL) {
return m_Runner->instantiate(pluginName);
QObject *result = m_Runner->instantiate(pluginName);
return result;
} else {
return NULL;
}
@@ -168,6 +207,12 @@ std::vector<unsigned int> ProxyPython::activeProblems() const
std::vector<unsigned int> result;
if (m_LoadFailure == FAIL_MISSINGDEPENDENCIES) {
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) {
if (!m_Runner->isPythonInstalled()) {
// don't know how this could happen but wth
@@ -185,11 +230,20 @@ QString ProxyPython::shortDescription(unsigned int key) const
{
switch (key) {
case PROBLEM_PYTHONMISSING: {
return tr("Python not installed or not found");
} break;
return tr("Python not installed or not found");
} break;
case PROBLEM_PYTHONWRONGVERSION: {
return tr("Python version is incompatible");
} break;
return tr("Python version is incompatible");
} break;
case PROBLEM_WRONGPYTHONPATH: {
return tr("Invalid python path");
} break;
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));
}
@@ -200,29 +254,43 @@ QString ProxyPython::fullDescription(unsigned int key) const
{
switch (key) {
case PROBLEM_PYTHONMISSING: {
return tr("Some plugins require the python interpreter to be installed. "
"These plugins will not even show up in settings-&gt;plugins.<br>"
"If you want to use those plugins, please install the 32-bit version of Python 2.7.x from <a href=\"%1\">%1</a>.").arg(s_DownloadPythonURL);
} break;
return tr("Some MO plugins require the python interpreter to be installed. "
"These plugins will not even show up in settings-&gt;plugins.<br>"
"If you want to use those plugins, please install the 32-bit version of Python 2.7.x from <a href=\"%1\">%1</a>.<br>"
"This is only required to use some extended functionality in MO, you do not need Python to play the game.").arg(s_DownloadPythonURL);
} break;
case PROBLEM_PYTHONWRONGVERSION: {
return tr("Your installed python version has a different version than 2.7. "
"Some plugins may not work.<br>"
"If you have multiple versions of python installed you may have to configure the path to 2.7 "
"in the settings dialog.");
} break;
return tr("Your installed python version has a different version than 2.7. "
"Some MO plugins may not work.<br>"
"If you have multiple versions of python installed you may have to configure the path to 2.7 (32 bit) "
"in the settings dialog.<br>"
"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 (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.");
} break;
default:
throw MyException(tr("invalid problem key %1").arg(key));
}
}
bool ProxyPython::hasGuidedFix(unsigned int) const
bool ProxyPython::hasGuidedFix(unsigned int key) const
{
return true;
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);
}
}
+7 -1
View File
@@ -50,9 +50,12 @@ private:
static const unsigned int PROBLEM_PYTHONMISSING = 1;
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;
@@ -61,6 +64,9 @@ private:
FAIL_NONE,
FAIL_NOTINIT,
FAIL_MISSINGDEPENDENCIES,
FAIL_INITFAIL,
FAIL_WRONGPYTHONPATH,
FAIL_PYTHONDETECTION,
FAIL_OTHER
} m_LoadFailure;
+19 -4
View File
@@ -1,5 +1,7 @@
#include "pythonrunner.h"
#pragma warning( disable : 4100 )
#pragma warning( disable : 4996 )
#include <boost/python.hpp>
#include <iplugininstaller.h>
@@ -8,6 +10,7 @@
#include "proxypluginwrappers.h"
#include <Windows.h>
#include <utility.h>
#include <QFile>
// sip and qt slots seems to conflict
#include <sip.h>
@@ -16,6 +19,7 @@
#include <boost/python.hpp>
#endif
MOBase::IOrganizer *s_Organizer = NULL;
@@ -44,8 +48,12 @@ private:
IPythonRunner *CreatePythonRunner(const MOBase::IOrganizer *moInfo, const QString &pythonDir)
{
PythonRunner *result = new PythonRunner(moInfo);
result->initPython(pythonDir);
return result;
if (result->initPython(pythonDir)) {
return result;
} else {
delete result;
return NULL;
}
}
@@ -564,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<bpy::reference_existing_object>());
@@ -624,7 +635,9 @@ static char* argv0 = "ModOrganizer.exe";
bool PythonRunner::initPython(const QString &pythonPath)
{
try {
//QString pythonPath = m_MOInfo->pluginSetting(name(), "python_dir").toString();
if (!pythonPath.isEmpty() && !QFile::exists(pythonPath + "/python.exe")) {
return false;
}
strncpy(m_PythonHome, pythonPath.toUtf8().constData(), MAX_PATH);
if (!pythonPath.isEmpty()) {
Py_SetPythonHome(m_PythonHome);
@@ -632,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__");
+9 -1
View File
@@ -15,7 +15,15 @@ public:
virtual bool isPythonVersionSupported() const = 0;
};
extern "C" QDLLEXPORT IPythonRunner *CreatePythonRunner(const MOBase::IOrganizer *moInfo, const QString &pythonDir);
#ifdef PYTHONRUNNER_LIBRARY
#define PYDLLEXPORT Q_DECL_EXPORT
#else // PYTHONRUNNER_LIBRARY
#define PYDLLEXPORT Q_DECL_IMPORT
#endif // PYTHONRUNNER_LIBRARY
extern "C" PYDLLEXPORT IPythonRunner *CreatePythonRunner(const MOBase::IOrganizer *moInfo, const QString &pythonDir);
#endif // PYTHONRUNNER_H
+3
View File
@@ -133,6 +133,9 @@ struct IOrganizerWrapper: MOBase::IOrganizer, boost::python::wrapper<MOBase::IOr
virtual bool removeMod(MOBase::IModInterface *mod) { return this->get_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")(); }