Compare commits

...
291 Commits
Author SHA1 Message Date
Chris Bessent 7c54a45f63 Merge pull request #99 from Holt59/no-more-external-python
Remove support for external Python.
2022-03-19 21:15:05 -07:00
Chris Bessent b090d1341b Merge pull request #100 from Qudix/isX
Add hasMasterExtension, hasLightExtension, isMasterFlagged, and isLightFlagged
2022-03-19 21:10:02 -07:00
Qudix f3b43a5375 Change isXFile to hasXExtension 2022-03-19 19:47:35 -05:00
Qudix 0423ccb9ca Rename isX to isXFile and deprecate isMaster 2022-02-24 20:00:56 -06:00
Qudix d4d958018d Add isMasterFlagged isLight isLightFlagged 2022-02-24 17:57:23 -06:00
Mikaël Capelle b1c51ada08 Remove support for external Python. 2022-02-13 17:33:53 +01:00
Mikaël Capelle 5f56aa99ca Merge pull request #98 from Holt59/fix-sharedptr-deletion
Fix shared_ptr in createFile().
2022-02-13 17:32:39 +01:00
Mikaël Capelle 0c5622ea69 Fix shared_ptr in createFile(). 2022-01-11 22:36:16 +01:00
Mikaël Capelle 071498f305 Merge pull request #96 from Holt59/remove-boost-uibase
Update after removal of boost in uibase.
2021-05-27 15:43:22 +02:00
Mikaël Capelle f134881da6 Update after removal of boost in uibase. 2021-05-26 19:47:15 +02:00
Mikaël Capelle 931996347b Merge pull request #94 from Holt59/fix-fileinfo
Fix FileInfo attribute access.
2021-05-20 20:21:07 +02:00
Mikaël Capelle 4775bafc33 Merge pull request #95 from Holt59/iorganizer-virtualfiletree
Add IOrganizer::virtualFileTree().
2021-05-20 20:20:47 +02:00
Mikaël Capelle f5a8446230 Add IOrganizer::virtualFileTree(). 2021-05-14 14:40:36 +02:00
Mikaël Capelle 120c13170c Fix FileInfo attribute access. 2021-05-14 13:54:06 +02:00
Mikaël Capelle ae975e52d7 Merge pull request #93 from Holt59/fix-startup-logs
Import mobase after installing the exception wrapper.
2021-03-04 20:29:24 +01:00
Mikaël Capelle e5ac21442f Import mobase after installing the exception wrapper. 2021-03-03 20:44:20 +01:00
Mikaël Capelle 40e6924714 Merge pull request #92 from LostDragonist/disabled_plugins
Add enabledByDefault to IPlugin definition
2021-03-02 19:27:44 +01:00
Mikaël Capelle e4ff66b727 Merge pull request #91 from Holt59/fix-ifiletree-sharedptr
Fix deletion of shared_ptr created from C++.
2021-03-02 19:27:32 +01:00
Chris Bessent a9a3d49efa Add enabledByDefault to IPlugin definition 2021-03-02 02:45:47 -07:00
Mikaël Capelle fce4f39dc8 Fix deleting of shared_ptr create from C++. 2021-02-28 19:58:15 +01:00
Mikaël Capelle 57441bd3c5 Update translation file. 2021-02-02 20:39:14 +01:00
Mikaël Capelle bab25d25ca Merge pull request #89 from Holt59/fix-iplugininstaller-install
Update IInstallationManager::installArchive() to handle string directly.
2021-01-02 15:30:16 +01:00
Mikaël Capelle ca052389b5 Merge pull request #90 from Holt59/update-imodinterface
Remove setName and remove from mod interface.
2021-01-02 15:25:35 +01:00
Mikaël Capelle 5f86de5d9e Remove setName and remove from mod interface. 2021-01-02 15:07:31 +01:00
Mikaël Capelle 91c55c909f Update IInstallationManager::installArchive() to handle string directly. 2021-01-01 19:58:19 +01:00
Mikaël Capelle d2d31f993d Merge pull request #88 from Holt59/fix-iorganizer-findfileinfos
Add missing converters to use IOrganizer::findFileInfos.
2020-12-30 21:06:13 +01:00
Mikaël Capelle 56dbb1f40e Add missing converters to use IOrganizer::findFileInfos. 2020-12-30 20:42:19 +01:00
Al cde7f79bc3 Merge pull request #87 from Holt59/imodinterface-isX
Add method to get the type of IModInterface.
2020-12-30 15:25:18 +01:00
Mikaël Capelle 96a7bc3898 Add method to get the type of IModInterface. 2020-12-26 16:55:07 +01:00
Mikaël Capelle f4ae1c94a0 Merge pull request #86 from Holt59/add-taskdialog
Add bindings for TaskDialog.
2020-12-12 13:00:37 +01:00
Mikaël Capelle 87c91b8259 Add bindings for TaskDialog. 2020-12-08 19:44:10 +01:00
Mikaël Capelle 2ed4b901c0 Merge pull request #85 from Holt59/moexception
Rename MyException to MOException
2020-12-04 13:30:30 +01:00
Mikaël Capelle cd9b16dcc2 MOException -> Exception. 2020-12-04 12:04:45 +01:00
Mikaël Capelle 5cad8a9ff8 Rename MyException to MOException. 2020-12-04 11:49:44 +01:00
Mikaël Capelle e961d21f24 Merge pull request #84 from Holt59/ipluginproxy-unload
Add IPluginProxy::unload() and rename instantiate() to load().
2020-12-02 16:26:56 +01:00
Mikaël Capelle 7f0848c9d2 Improve comments. 2020-12-02 09:20:13 +01:00
Mikaël Capelle bc05ec6477 Add IPluginProxy::unload() and rename instantiate() to load(). 2020-12-01 21:18:06 +01:00
Al 631fc2b31b Merge pull request #83 from ModOrganizer2/Al12rs-refresh-waitForApplication
Add refresh parameter for waitForApplication
2020-11-28 10:55:38 -08:00
AL f17ce3d2e6 add default value for refresh 2020-11-28 18:17:23 +01:00
AL 73d7771703 Add refresh parameter for waitForApplication 2020-11-28 18:02:39 +01:00
Al e737f0a5a8 Merge pull request #79 from ModOrganizer2/iplugingame-saves
IPluginGame::listSaves
2020-11-28 06:37:40 -08:00
isanae a75e94d46f Merge pull request #82 from ModOrganizer2/revert-73-master
Revert "Moving things around"
2020-11-27 07:47:30 -05:00
isanae b7855ca7d8 Merge pull request #81 from ModOrganizer2/revert-80-dll_directory
Revert "Fix libffi not found"
2020-11-27 07:47:24 -05:00
isanae f6cf0e8b33 Revert "Moving things around" 2020-11-27 07:36:39 -05:00
isanae 4fdbabdf76 Revert "Fix libffi not found" 2020-11-27 07:36:28 -05:00
Mikaël Capelle efc7d76a0a Revert translation file. 2020-11-25 22:47:10 +01:00
Mikaël Capelle 832aced4d3 Fix default binding for IPlugin::master(). 2020-11-21 14:11:36 +01:00
Mikaël Capelle a335adf928 Add argument name to IPluginRequirement::check(). 2020-11-21 13:45:30 +01:00
Mikaël Capelle ccef4be741 Replace ScriptExtender saveGameAttachmentExtensions with savegameExtension. 2020-11-21 12:25:59 +01:00
Mikaël Capelle aacf3654d8 Add default argument to GuessedString constructor to match C++ implementation. 2020-11-20 21:42:07 +01:00
Mikaël Capelle 234586823d Return shared_ptr in IPlugin::requirements(). 2020-11-19 21:21:58 +01:00
Mikaël Capelle 5b84588732 Add comment to reference Boost.Python issue. 2020-11-19 20:41:24 +01:00
Mikaël Capelle 7dbc139b10 Fix crash when destructing Python save games due to the GIL lock. 2020-11-19 20:40:05 +01:00
Mikaël Capelle 31e4f724ed Clean listSaves() bindings. 2020-11-19 20:33:24 +01:00
Mikaël Capelle 6e088406c8 Update following removal of IPlugin::isActive(). 2020-11-19 20:33:24 +01:00
Mikaël Capelle 9784881921 Update following addition of IPluginGame::listSaves(). 2020-11-19 20:33:00 +01:00
Mikaël Capelle aa8caee65c Merge pull request #80 from Qudix/dll_directory
Fix libffi not found
2020-11-19 09:36:34 +01:00
Qudix 9a10c01cc0 lamb-da 2020-11-18 14:30:40 -06:00
Qudix d9243bb617 Better 2020-11-18 13:37:54 -06:00
Qudix adf5888faf Fix libffi not found 2020-11-18 13:02:15 -06:00
Mikaël Capelle 7bec9237e6 Merge pull request #78 from Holt59/iplugin-isactive-refactoring
IPlugin::isActive refactoring
2020-11-18 10:01:30 +01:00
Mikaël Capelle 0846ffd177 Merge pull request #77 from Holt59/logging-module
Add handler for Python logging module.
2020-11-16 09:13:28 +01:00
isanae f3601e5bfe Merge pull request #73 from Qudix/master
Moving things around
2020-11-14 06:54:31 -05:00
Mikaël Capelle b37d3a9c17 Add callbacks for plugin enabled/disabled. 2020-11-12 21:49:37 +01:00
Mikaël Capelle cbb911f6a0 Rename the wrapper declaration and definition macros. 2020-11-11 22:02:40 +01:00
Mikaël Capelle 27c34af2aa Update authors. 2020-11-11 21:54:54 +01:00
Mikaël Capelle b20c015643 master() returns the name of the plugin. 2020-11-11 21:00:47 +01:00
Mikaël Capelle d89eae1646 Allow IPluginRequirement to be properly extended from Python. 2020-11-11 17:25:55 +01:00
Mikaël Capelle de5fde0f28 Cleaner IPluginRequirement. 2020-11-11 16:14:28 +01:00
Mikaël Capelle e84777d550 Remove automatic plugin dependency for proxy (moved to MO2 core). 2020-11-11 13:38:42 +01:00
Mikaël Capelle 1ca6fee64d Add plugin requirements. 2020-11-11 11:23:15 +01:00
Mikaël Capelle 57c774103d Better handling of sequence conversions. 2020-11-11 11:22:50 +01:00
Mikaël Capelle ead8352c93 Remove isActive() for the Python proxy plugin. 2020-11-11 11:22:10 +01:00
Mikaël Capelle 04e1af60a0 Do not set basicConfig() but provide a default logger. 2020-11-09 20:08:29 +01:00
Mikaël Capelle ebb566f773 Expose LogHandler in mobase. 2020-11-09 19:17:57 +01:00
Mikaël Capelle c45bfb1e35 Cast the record message to str() before extracting. 2020-11-09 18:28:58 +01:00
Mikaël Capelle 5f8e1803c0 Add handler for Python logging module. 2020-11-09 18:23:57 +01:00
Seth Riley 9bd22cfceb Merge branch 'master' of https://github.com/ModOrganizer2/modorganizer-plugin_python into master 2020-11-06 13:24:00 -06:00
Mikaël Capelle c2c63e0624 Merge pull request #76 from Holt59/iplugingame-detectgame
Replace IPlugin::registered() by IPluginGame::detectGame().
2020-11-06 20:17:45 +01:00
Seth Riley 240622565c Fix merge 2020-11-06 12:36:43 -06:00
Seth Riley ba7ba616ea Merge branch 'master' of https://github.com/ModOrganizer2/modorganizer-plugin_python into master 2020-11-06 12:27:51 -06:00
Mikaël Capelle 93dcf8d5b3 Replace IPlugin::registered() by IPluginGame::detectGame(). 2020-11-06 18:56:00 +01:00
isanae 7d0e97ed2b Merge pull request #75 from isanae/command-line
Command line and instance dialogs
2020-11-06 10:50:25 -05:00
isanae 86297b1170 removed unused IOrganizer from PythonRunner 2020-11-06 09:25:01 -05:00
isanae 78e4e5c548 binding for getPluginDataPath() 2020-11-06 09:22:23 -05:00
isanae 6a73c23a4b added registered() python wrapper
null checks for the IOrganizer, replaced pluginDataPath() by the static version getPluginDataPath()
2020-11-06 09:06:55 -05:00
Mikaël Capelle 7afade8642 Merge pull request #74 from Holt59/imodinterface-filetree
Add IModInterface::fileTree().
2020-11-05 10:43:55 +01:00
isanae 6445f7f47f handle empty variants returned from pluginSetting()
can happen when init() is called when MO has no instance set up
2020-11-04 22:52:56 -05:00
Mikaël Capelle c6e89dead6 Add IModInterface::fileTree(). 2020-11-04 21:48:22 +01:00
Seth Riley 20c2e5a14d Moving things around 2020-11-02 13:46:02 -06:00
Mikaël Capelle 83dec58e4a Merge pull request #72 from Holt59/imodlist-updates
IModList updates
2020-10-26 21:37:39 +01:00
Mikaël Capelle f4b8cbcd0b Merge pull request #70 from Holt59/iorganizer-profile-callbacks
Add IOrganizer::onProfileXXX callbacks.
2020-10-26 21:36:51 +01:00
Mikaël Capelle 96d15ae762 Fix default nullptr argument for allModsByProfilePriority. 2020-10-25 18:37:53 +01:00
Mikaël Capelle 7668783d3c Move IOrganizer::getMod() to IModList::getMod(). 2020-10-25 17:46:00 +01:00
Mikaël Capelle 40b7cd8e86 Move IOrganizer::removeMod() to IModList::removeMod(). 2020-10-25 17:15:22 +01:00
Mikaël Capelle 2aa65ec5bb Move IOrganizer::modsSortedByProfilePriority() to IModList::allModsByProfilePriority(). 2020-10-25 16:52:28 +01:00
Mikaël Capelle b1e554526a Fix for onModInstalled() modification. 2020-10-25 16:52:20 +01:00
Mikaël Capelle 8640890f8c Rename IOrganizer::refresh(). Move IOrganizer::onModInstalled. Add IModList::onModRemoved(). 2020-10-25 16:18:55 +01:00
Mikaël Capelle 2526b75ca1 Merge pull request #71 from Holt59/fix-ifiletree-const
Fix implicit conversion from non-const IFileTree to const IFileTree.
2020-10-25 13:00:50 +01:00
Mikaël Capelle 138de736ef Fix implicit conversion from non-const IFileTree to const IFileTree. 2020-10-25 12:58:37 +01:00
Mikaël Capelle 9e4cfaa0f0 Add IOrganizer::onProfileXXX callbacks. 2020-10-24 20:52:52 +02:00
Mikaël Capelle 8b703f2e4f Merge pull request #68 from Holt59/iplugin-setactive-fix
Switch from Qt signals to boost::signals for DownloadManager.
2020-10-21 20:07:55 +02:00
Mikaël Capelle 984a307a04 Merge pull request #69 from Holt59/add-mapping-constructor
Add Mapping constructor.
2020-10-18 12:56:34 +02:00
Mikaël Capelle 2c30cd25cd Add Mapping constructor. 2020-10-18 10:59:57 +02:00
Mikaël Capelle 9ba91b1ffa Switch from Qt signals to boost::signals for DownloadManager. 2020-10-14 18:48:14 +02:00
Mikaël Capelle d53e8de91c Merge pull request #67 from Holt59/imodinterface-seturl
Add IModInterface::setUrl and remove IInstallationManager::setURL.
2020-10-14 18:44:41 +02:00
Mikaël Capelle 702dc6c66f Add IModInterface::setUrl and remove IInstallationManager::setURL. 2020-10-09 20:32:07 +02:00
Mikaël Capelle 9c9d1ee574 Merge pull request #66 from Holt59/add-iprofile-inifilepath
Add IProfile::iniFilePath().
2020-09-27 21:22:34 +02:00
Mikaël Capelle 7a83a472b8 Rename iniFilePath() -> absoluteIniFilePath(). 2020-09-27 20:56:41 +02:00
Mikaël Capelle e4b367e909 Add IProfile::iniFilePath(). 2020-09-27 20:33:30 +02:00
Mikaël Capelle eb5232c1eb Merge pull request #60 from Holt59/fix-ipluginlist-callbacks
Make onPluginStateChanged take a list of plugins.
2020-09-27 14:55:47 +02:00
Jeremy Rimpo eb033c3e9e [skip ci] Use PR branch for umbrella repo (if it exists) 2020-09-25 00:34:18 -05:00
Jeremy Rimpo 28704a8a2e Merge branch 'master' of https://github.com/ModOrganizer2/modorganizer-plugin_python into master 2020-09-24 21:44:34 -05:00
Mikaël Capelle 5a15a374bf Merge pull request #64 from Holt59/plugin-installer-callbacks
Add IPluginInstaller onInstallationStart and onInstallationEnd callbacks
2020-09-23 18:40:24 +02:00
Mikaël Capelle f4478dcf66 Merge remote-tracking branch 'origin/master' into plugin-installer-callbacks 2020-09-23 18:38:25 +02:00
Mikaël Capelle df663f6e4f Merge pull request #63 from Holt59/expose-modinterface-meta
Add getters for most meta-information in mod.
2020-09-23 18:34:24 +02:00
Mikaël Capelle bb522b9126 Add 'reinstallation' parameter to onInstallationStart(). 2020-09-22 22:02:27 +02:00
Mikaël Capelle d549a99867 Change modId() to nexusId(). 2020-09-22 21:13:23 +02:00
Al 44553b4621 Merge pull request #65 from Holt59/imodinterface-clearpluginsettings
Add IModInterface::clearPluginSettings().
2020-09-21 14:42:42 -07:00
Mikaël Capelle 1d51b3c457 Add IModInterface::clearPluginSettings(). 2020-09-21 23:37:30 +02:00
Al 2c0f1006cf Merge pull request #61 from Holt59/iplugin-interface-improvements
Add interfaces for IPlugin::localizedName() and IPlugin::master().
2020-09-21 13:08:54 -07:00
Mikaël Capelle 58d5100dfc Add IPluginInstaller onInstallationStart and onInstallationEnd callbacks. 2020-09-21 21:34:24 +02:00
Jeremy Rimpo e545aec65a Merge branch 'test' into master 2020-09-21 13:26:04 -05:00
Jeremy Rimpo f10194a4bb Revert "Test GH integration"
This reverts commit afe671d4
2020-09-21 10:42:05 -05:00
Jeremy Rimpo afe671d484 Test GH integration 2020-09-21 07:51:33 -05:00
Jeremy Rimpo bfb85087a0 Force exit PS with last exit code after umbrella build error 2020-09-21 05:53:44 -05:00
Jeremy Rimpo b4ae13314c Needs quotes 2020-09-21 05:37:42 -05:00
Jeremy Rimpo c5be2c3605 PowerShell build should fail on error 2020-09-21 05:17:22 -05:00
Jeremy Rimpo 0f33ee2176 Finalize appveyor script update 2020-09-20 22:34:32 -05:00
Jeremy Rimpo 1d2fa7f269 Convert scripts to PowerShell 2020-09-20 19:56:37 -05:00
Jeremy Rimpo c0828f2553 Merge remote-tracking branch 'origin/master' into master 2020-09-20 19:16:23 -05:00
Jeremy Rimpo 847e73a0db Need extra space for second command 2020-09-20 19:11:50 -05:00
Al 1d111cdd8f Merge pull request #62 from Holt59/mod-specific-plugin-settings
Add pluginSetting[s]() and setPluginSetting() to IModInterface.
2020-09-20 12:53:07 -07:00
Mikaël Capelle 5b06bb75a9 Add pluginSetting[s]() and setPluginSetting() to IModInterface. 2020-09-20 21:30:01 +02:00
Mikaël Capelle 04a54c7566 Add getters for most meta-information in mod. 2020-09-20 15:20:22 +02:00
Jeremy Rimpo b96e006e7d Add /bigobj flag to compile options 2020-09-09 19:38:50 -05:00
Mikaël Capelle bfa5c28285 Remove debug logs. 2020-09-06 11:54:55 +02:00
Mikaël Capelle cf6679292f Add interfaces for IPlugin::localizedName() and IPlugin::master(). 2020-09-06 11:44:50 +02:00
Mikaël Capelle 65e73aa537 Fix typo onModStateChanged -> onPluginStateChanged. 2020-09-05 14:49:19 +02:00
Mikaël Capelle 194bba9a9b Make onPluginStateChanged take a list of plugins. 2020-09-05 13:49:11 +02:00
Al 64fd574c3b Merge pull request #59 from Holt59/ipluginlist-setpriority
Add IPluginList::setPriority binding.
2020-09-01 03:59:56 -07:00
Al 93fee5bd6c Merge pull request #58 from Holt59/modlist-setactive-bulk
Fix bindings of IModList::setActive.
2020-09-01 03:59:32 -07:00
Mikaël Capelle accb270353 Add IPluginList::setPriority binding. 2020-08-30 14:03:23 +02:00
Mikaël Capelle d8657946e9 Fix depreciation -> deprecation. 2020-08-30 11:15:31 +02:00
Mikaël Capelle 3780f157e4 Move the storage for deprecated lines in show_depreciation_warnings(). 2020-08-30 10:25:11 +02:00
Mikaël Capelle 7a9cd34878 Update following onModStateChanged + Depreciation warning. 2020-08-29 19:55:06 +02:00
Mikaël Capelle 829ac65cea Fix bindings of IModList::setActive. 2020-08-29 12:59:38 +02:00
isanae 5c34d11e79 Merge pull request #57 from Holt59/fix-imodlist-ipluginlist-callbacks
Add missing onPluginStateChanged binding and missing functor converters.
2020-08-27 12:38:01 -04:00
Mikaël Capelle 6001d88f40 Add missing onPluginStateChanged binding and missing functor converters. 2020-08-27 18:30:22 +02:00
isanae 139666705a Merge pull request #55 from Holt59/fix-ifiletree-copy
Fix IFileTree::copy() bindings.
2020-08-06 09:00:28 -04:00
Mikaël Capelle fed261e9eb Fix IFileTree::copy() bindings. 2020-08-06 11:10:26 +02:00
isanae f531905e4a Merge pull request #54 from Holt59/add-missing-iplugin-bases
Add missing bpy::bases<IPluginX> for some plugin types.
2020-07-28 21:26:13 -04:00
Mikaël Capelle e25fa00285 Add missing bpy::bases<IPlugin> for some plugin types. 2020-07-22 21:23:47 +02:00
isanae 5c63d62a1e Merge pull request #53 from Holt59/add-executableforcedloadsetting-converter
Add executableforcedloadsetting converter
2020-07-20 15:18:42 -04:00
Mikaël Capelle 624c5257bd Add missing converter for QList<ExecutableForcedLoadSetting>. 2020-07-20 19:44:51 +02:00
Mikaël Capelle 480eb3f30e Fix name of default argument in persistent(). 2020-07-20 19:36:58 +02:00
isanae 4909b6cff0 Merge pull request #52 from Holt59/add-arg-names
Add arg names
2020-07-14 12:23:02 -04:00
Mikaël Capelle 331416d1e7 Add binding for IInstallationManager::getSupportedExtensions(). 2020-07-14 16:39:45 +02:00
Mikaël Capelle c955b4e8de Fix argument name for getSaveGameWidget. 2020-07-14 15:00:26 +02:00
Mikaël Capelle 727a55e1a8 Add missing argument names. 2020-07-14 14:36:11 +02:00
Mikaël Capelle 6c1f961d83 Add missing arg names for GuessedString constructor. 2020-07-14 13:33:48 +02:00
Mikaël Capelle a3e9916400 Fix order of overloads for VersionInfo constructors. 2020-07-14 13:21:30 +02:00
Mikaël Capelle 0fbb88b3c1 Add argument names to bindings. 2020-07-14 13:14:53 +02:00
isanae 5023a13aa2 Merge pull request #51 from isanae/ts
updated translations
2020-07-10 12:21:48 -04:00
isanae fe8a0b849c updated translations
disabled translations for plugin_python, identical to pythonrunner
2020-07-10 12:17:45 -04:00
isanae e1a1b70121 Merge pull request #50 from Holt59/add-movariant
Add module variable to fake MoVariant.
2020-06-17 18:08:03 -04:00
Mikaël Capelle 671776f306 Clarify comment for MoVariant export. 2020-06-16 22:12:25 +02:00
Mikaël Capelle 95ed8ae14a Add module variable to fake MoVariant. 2020-06-16 21:57:31 +02:00
isanae e6ca79c694 Merge pull request #49 from Holt59/new-iorganizer-callbacks
Update for new organizer callbacks.
2020-06-16 15:40:04 -04:00
Mikaël Capelle c521178769 Update for new organizer callbacks. 2020-06-15 22:48:21 +02:00
Al 1cbfde96a0 Merge pull request #48 from Holt59/fix-game-plugins-getloadorder
Change getLoadOrder() to return the load order in python.
2020-06-14 15:59:29 -07:00
Mikaël Capelle e7ae48f633 Update getLoadOrder() bindings following game_features change. 2020-06-14 15:31:21 +02:00
Mikaël Capelle d0fd776648 Change getLoadOrder() to return the load order in python. 2020-06-14 14:51:52 +02:00
Al cbdf8fee14 Merge pull request #46 from Holt59/allow-plugins-to-fix-filetree
Update following ModDataChecker change.
2020-06-14 05:48:54 -07:00
Al 5fe9c44f26 Merge pull request #47 from Holt59/executable-forced-load-settings-bindings
Add missing bindings for executableForcedLoads and fix ExecutableInfo.
2020-06-14 05:48:21 -07:00
Mikaël Capelle 3f02f975aa Add missing bindings for executableForcedLoads and fix ExecutableInfo. 2020-06-14 10:52:45 +02:00
Mikaël Capelle b2b0b6f5c5 Missing includes and fix names in bindings. 2020-06-14 00:06:41 +02:00
Mikaël Capelle 6ee58c6bf9 Update following ModDataChecker change. 2020-06-13 23:51:52 +02:00
isanae 2efc6d4af6 Merge pull request #45 from Holt59/fix-exception-transfer-at-startup
Fix Python -> C++ error transfer during plugin creation.
2020-06-13 13:10:04 -04:00
Mikaël Capelle 760cca172f Fix Python -> C++ error transfer during plugin creation. 2020-06-13 19:04:58 +02:00
Al 26c0e628b8 Merge pull request #44 from Holt59/add-ifiletree-copy
Add bindings for IFileTree::copy().
2020-06-13 02:52:10 -07:00
Mikaël Capelle ab4a067042 Add bindings for IFileTree::copy(). 2020-06-12 20:54:52 +02:00
Al 38188e0b17 Merge pull request #43 from Holt59/add-utility-functions
Add bindings for utility functions from uibase.
2020-06-10 04:00:05 -07:00
Mikaël Capelle 863893cc31 Add bindings for utility functions from uibase. 2020-06-09 23:31:25 +02:00
Al 06b11c86ba Merge pull request #40 from Holt59/fix-exceptions-plugin-preview
Fix genFilePreview issue with exceptions not being properly translated to C++.
2020-06-09 05:53:15 -07:00
Al d4a6a57f14 Merge pull request #42 from Holt59/fix-iprofile-invalidationactive
Fix IProfile::invalidationActive
2020-06-08 13:10:17 -07:00
Mikaël Capelle 2ea33f4faa Return a tuple<bool, bool> for IProfile::invalidationActive() to replace the bool* parameter. 2020-06-08 21:45:01 +02:00
Al b4c23ad733 Merge pull request #41 from Holt59/iorganizer-findfiles-glob
Add findFiles overload with glob patterns.
2020-06-08 12:05:26 -07:00
Mikaël Capelle 71db9d81d4 Add findFiles overload with glob patterns. 2020-06-08 18:37:59 +02:00
Mikaël Capelle e712448ca0 Fix genFilePreview issue with exceptions not being properly translated to C++. 2020-06-08 17:54:30 +02:00
Al 98b8d92350 Merge pull request #39 from Holt59/qclass-converter-fix
Fix issue with QClass converter for overloaded functions.
2020-06-08 08:40:46 -07:00
Mikaël Capelle 7cc0f974f5 Use a sequence converter for QStringList instead of a QClass converter. 2020-06-07 17:56:58 +02:00
Mikaël Capelle 48c35a8f92 Switch 0 for nullptr. 2020-06-07 17:01:46 +02:00
Mikaël Capelle 4b9fc2d32a Fix issue with QClass converter for overloaded functions. 2020-06-07 16:54:53 +02:00
Al be63c1d9de Merge pull request #38 from Holt59/set-plugin-setting-signal
Add IOrganizer signal when a plugin setting is changed.
2020-06-07 03:45:50 -07:00
Mikaël Capelle 100a2d03f0 Add IOrganizer signal when a plugin setting is changed. 2020-06-05 16:48:42 +02:00
Al fcd1177acd Merge pull request #37 from Holt59/install-manager-improvements
Installation manager improvements
2020-06-01 13:56:14 -07:00
Mikaël Capelle 5eeb02403f Update bindings after uibase update. 2020-05-30 14:58:50 +02:00
isanae eca495c849 Merge pull request #36 from Holt59/mod-data-content
Add ModDataContent to the python interface.
2020-05-28 23:43:02 -04:00
Mikaël Capelle 024a333194 Update to match game features. 2020-05-27 22:44:32 +02:00
Mikaël Capelle ead37d1dbe Add ModDataContent to the python interface. 2020-05-25 21:24:50 +02:00
Al 65003ef4ad Merge pull request #35 from Holt59/moddatachecker
Minor updates following changes in uibase.
2020-05-23 14:07:30 -07:00
Mikaël Capelle a3b593f714 Remove time() method from FileTreeEntry. 2020-05-23 21:45:05 +02:00
Mikaël Capelle ab4168fd93 Minor updates following changes in uibase. 2020-05-23 17:32:01 +02:00
Al bfad575467 Merge pull request #34 from Holt59/cleaning
Cleaning and improvements
2020-05-22 12:30:48 +02:00
Mikaël Capelle d5f44f424d Add comments for basicWrapperFunction functions. 2020-05-21 23:15:41 +02:00
Mikaël Capelle da679b1d6d Common code for basicWrapperFunction functions. 2020-05-21 23:11:56 +02:00
Mikaël Capelle 751e67e86c Fix issue with Python errors not being retrieved correctly in C++. 2020-05-21 22:08:20 +02:00
Mikaël Capelle c96fe2d21a Add missing qflags converter for ModState. 2020-05-21 17:47:08 +02:00
Mikaël Capelle 4b4c36df7a Add possibility to create multiple plugins object from a single .py file or python module. 2020-05-20 20:52:31 +02:00
Mikaël Capelle 5f9d2b296d Remove usage of getDataFolderName() for ModDataChecker. 2020-05-20 19:53:49 +02:00
Mikaël Capelle 05e79cc13b Merge remote-tracking branch 'origin/master' into cleaning 2020-05-20 18:32:22 +02:00
Al 3f5aa49dee Merge pull request #33 from Holt59/python-module
Allow implementation of python plugin as whole module.
2020-05-20 13:15:46 +02:00
Al 3e9209a191 Merge pull request #32 from Holt59/new-filetree
Update python bindings for UI base change and new filetree implementation
2020-05-20 13:03:39 +02:00
Mikaël Capelle d70942efa1 Rollback to setGameName instead of setGamePlugin for mod interface. 2020-05-19 17:58:44 +02:00
Mikaël Capelle d9393a2d2b Add WalkReturn enumeration. 2020-05-18 16:16:26 +02:00
Mikaël Capelle 7c1389d479 Add ModDataChecker game feature and clean some game-feature related code. 2020-05-16 20:47:41 +02:00
Mikaël Capelle 7333bde006 Add makeTree in moprivate. 2020-05-16 17:37:57 +02:00
Mikaël Capelle fae9f1b52f Allow None value from python in functor converter. 2020-05-16 17:37:41 +02:00
Mikaël Capelle 23c6f3fad9 Better arity check for functor converter. 2020-05-16 17:37:22 +02:00
Mikaël Capelle f12bb05d61 Update python version to 3.8. 2020-05-14 23:24:38 +02:00
Mikaël Capelle d931d71a43 Translate failure in exceptions for some IFileTree methods. Add IFileTree::walk. 2020-05-14 23:18:23 +02:00
Mikaël Capelle aa75c3b18e Add .feature() method to the Python interface of IPluginGame. 2020-05-13 17:35:33 +02:00
Mikaël Capelle eb29202b2f Use UPPER_CASE for enumeration values (kept non-uppercase for backward compatibility). 2020-05-13 15:55:05 +02:00
Mikaël Capelle fe0a75b819 Add to-python converter for QVariant holding QStringList. 2020-05-13 15:35:50 +02:00
Mikaël Capelle 90a99b37c9 Add missing override annotation and use using to bring invalidate in public scope. 2020-05-13 14:34:17 +02:00
Mikaël Capelle 7c474c58ca Add small comments. 2020-05-13 14:33:15 +02:00
Mikaël Capelle 288f9f9a95 Rename IModRepositoryBridge to be consistent with other interfaces. 2020-05-12 19:25:57 +02:00
Mikaël Capelle a05006a0b3 Allow implementation of python plugin as whole module. 2020-05-11 23:51:58 +02:00
Mikaël Capelle d6b8c66911 Move include directive where they should be. 2020-05-11 23:40:06 +02:00
Mikaël Capelle 0c52c2417c Remove unused global variable. 2020-05-11 23:39:55 +02:00
Mikaël Capelle 24205bba66 Remove useless wrappers. 2020-05-11 23:36:35 +02:00
Mikaël Capelle f0f949c4ef Remove ModRepositoryBridgeWrapper since it is not needed anymore. 2020-05-11 23:26:29 +02:00
Mikaël Capelle 6b88cee9d8 Remove useless includes. 2020-05-11 23:26:06 +02:00
Mikaël Capelle 1825c58666 Remove comment. 2020-05-11 23:18:44 +02:00
Mikaël Capelle 83a89ca595 Move utils::register_ at the top of the module definition. 2020-05-11 23:16:51 +02:00
Mikaël Capelle e9fdfd40aa Add comment for Q_DELEGATE. 2020-05-11 23:02:08 +02:00
Mikaël Capelle fcf1aa8b42 Move converter to a dedicate file. Add Q_DELEGATE for QObject-derived wrappers. 2020-05-11 23:00:29 +02:00
Mikaël Capelle d404a37fed Add/fix bindings for ISaveGame and ISaveGameInfoWidget. 2020-05-10 16:01:14 +02:00
Mikaël Capelle b360cae555 Add missing include in error.h. 2020-05-10 15:57:46 +02:00
Mikaël Capelle 039e33f5fa Minor clean and first try for ISaveGameInfoWidget. 2020-05-09 23:30:10 +02:00
Mikaël Capelle 622d4246a7 Clean some comments. 2020-05-09 21:32:23 +02:00
Mikaël Capelle 2f1646a2e6 Remove HANDLE_converters since it is not used anymore and did not really work. 2020-05-09 21:27:00 +02:00
Mikaël Capelle 716cbcf733 More cleaning. 2020-05-09 21:16:30 +02:00
Mikaël Capelle 62f10c03ff Temporary commit. 2020-05-09 21:13:45 +02:00
Mikaël Capelle 957b786d8c Start cleaning interfaces. 2020-05-09 18:19:31 +02:00
Mikaël Capelle 29a5eb8366 Remove wrong static_assert(). 2020-05-07 00:19:11 +02:00
Mikaël Capelle 4209d90533 Remove left-out method not implemented. 2020-05-06 22:08:25 +02:00
Mikaël Capelle 7bbf05bc0c Apply appendIfInstance for each class separately. 2020-05-06 12:51:27 +02:00
Mikaël Capelle 154c643bff Replace TRY_PLUGIN_TYPE with a more modern C++ variant. 2020-05-05 17:52:54 +02:00
Mikaël Capelle 2146cc9207 Fix wrapper for custom installer. 2020-05-04 20:17:02 +02:00
Mikaël Capelle 163ed213ef Update the install() bindings to allow more flexibility and be easier to use. 2020-05-04 20:09:24 +02:00
Mikaël Capelle 6919539d35 Remove left-out comment. 2020-05-04 18:40:53 +02:00
Mikaël Capelle 917b8dfb4b Slight modification for tuple and variant registers using C++17 fold expressions. 2020-05-04 01:58:56 +02:00
Mikaël Capelle 54e447c0a9 Switch from boost variant/tuple to std. 2020-05-04 01:47:30 +02:00
Mikaël Capelle 206454c34e Remove makeTree that was meant for testing purpose. 2020-05-03 23:16:58 +02:00
Mikaël Capelle 457529546b Add a bit of documentation to register functions. 2020-05-03 21:01:43 +02:00
Mikaël Capelle b9de35772f Clean sequence converters. 2020-05-03 20:57:50 +02:00
Mikaël Capelle 4589cab093 Add missing std::vector register for installation manager. 2020-05-03 20:19:25 +02:00
Mikaël Capelle 2b4eedb62b Clean std::set/std::vector from/to python conversions. 2020-05-03 20:17:49 +02:00
Mikaël Capelle 36022774e3 Add methods to utils to register map, vector and set. 2020-05-03 20:17:14 +02:00
Mikaël Capelle aac45a39e2 Make register_variant work for all variant types. 2020-05-03 20:16:57 +02:00
Mikaël Capelle 61ad2ef2b6 Clean bindings for IFileTree and FileTreeEntry. 2020-05-03 18:27:26 +02:00
Mikaël Capelle 1ea006b74e Add bindings for FileInfo. 2020-05-03 18:26:27 +02:00
Mikaël Capelle af37a451f9 Better variant handling for IFileTree::merge. 2020-05-03 18:26:11 +02:00
Mikaël Capelle 473cef6217 Add utility package with register_map for std::map. 2020-05-03 18:25:35 +02:00
Mikaël Capelle 5cd7917709 Add to_python to register_variant.h. 2020-05-03 18:25:16 +02:00
Mikaël Capelle 585296d527 Fix issue with shared pointer being registered twice. 2020-05-03 18:24:32 +02:00
Mikaël Capelle 221949f48b Fix issue with Functor_converter when return type was not bool or void. 2020-05-03 13:39:04 +02:00
Mikaël Capelle 8b118ad898 Better handling of boost::variant. 2020-05-03 13:38:47 +02:00
Mikaël Capelle f83ed39287 Modify FileTypes enumeration for FileTreeEntry. 2020-05-03 12:57:23 +02:00
Mikaël Capelle 8a286f9cfc Change Functor_converter to use a function-type template argument. 2020-05-03 12:57:01 +02:00
Mikaël Capelle 70172ddda6 Add possibility to create GuessedString from python. 2020-05-02 15:34:06 +02:00
Mikaël Capelle 817f8d7238 Fix variants() method of GuessedValue. 2020-05-02 15:05:24 +02:00
Mikaël Capelle 9f5dab285d Fix return_value_policy for methods returning QWidget. 2020-05-02 15:04:44 +02:00
Mikaël Capelle 53b91f0092 Update exposed method names for installer to be consistent with others. 2020-05-02 13:28:08 +02:00
Mikaël Capelle f1d5a1f31b Improve GuessedValue binding and switch to upper-case for enumerations in Python. 2020-05-02 13:22:55 +02:00
Mikaël Capelle 626a7bc670 Update python bindings for tree to be consistent with others. 2020-05-02 13:22:34 +02:00
Mikaël Capelle c9115e7ad0 Add python interface for IPluginInstallerSimple. 2020-05-02 00:30:17 +02:00
Mikaël Capelle 6cececc7a0 Fix issue with default QDateTime() throwing error. 2020-05-01 21:32:30 +02:00
Mikaël Capelle d82640455f Update wrappers and expose the new file tree interface. 2020-05-01 21:32:08 +02:00
Mikaël Capelle f25602cc69 Update the wrapper and the python interface after change to IModInterface. 2020-05-01 21:30:31 +02:00
Silarn 168b9d7b0d Appveyor CMAKE fix 2020-05-01 04:59:02 -05:00
isanae 247944baa8 Merge pull request #31 from isanae/new-cmakefiles
New cmakefiles
2020-04-26 02:53:56 -04:00
isanae 332b8239bb unused 2020-04-25 00:51:20 -04:00
isanae 97a93a5989 now using new cmakefiles 2020-04-25 00:51:14 -04:00
Al e871617843 Merge pull request #30 from AnyOldName3/threadsafe
Make runner threadsafe again
2020-04-17 14:41:38 +02:00
AnyOldName3 c5d4857f47 Make functor converter variadic
Duplicating code three times is silly.
2020-04-16 00:56:57 +01:00
AnyOldName3 5825661a4f Only initialise Python once
When switching instances, the Python runner gets another initPython call, despite the previous interpreter existing. Most of the function was either a no-op or was redundant, and the GIL state got confusing. This is the simplest fix.

If things catch fire that I haven't anticipated, instead of returning true, we can destroy the pre-existing interpreter and create a new one by running the rest of the function.
2020-04-16 00:53:29 +01:00
AnyOldName3 2e13cf0dab Ensure we relinquish the GIL after initialising Python and have it when destroying callables 2020-04-15 17:01:28 +01:00
Chris Bessent d6dd0342b7 Merge pull request #29 from ModOrganizer2/2_2_2
Pull in 2.2.2.1 changes
2020-01-13 23:05:28 -07:00
30 changed files with 3593 additions and 2399 deletions
+12 -22
View File
@@ -1,25 +1,15 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
cmake_minimum_required(VERSION 3.16)
ADD_COMPILE_OPTIONS($<$<CXX_COMPILER_ID:MSVC>:/MP> $<$<CXX_COMPILER_ID:MSVC>:$<$<CONFIG:RELEASE>:/O2>> $<$<CXX_COMPILER_ID:MSVC>:$<$<CONFIG:RELWITHDEBINFO>:/O2>>)
project(plugin_python)
set(project_type plugin)
set(enable_warnings OFF)
set(create_translations OFF)
SET(PROJ_NAME plugin_python)
if(DEFINED DEPENDENCIES_DIR)
include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/project.cmake)
else()
include(../cmake_common/project.cmake)
endif()
add_subdirectory(src/proxy)
PROJECT(${PROJ_NAME})
SET(DEPENDENCIES_DIR CACHE PATH "")
# hint to find qt in dependencies path
LIST(APPEND CMAKE_PREFIX_PATH ${QT_ROOT}/lib/cmake)
# need to install python
IF(EXISTS "${PYTHON_ROOT}/PCbuild/python37.dll")
INSTALL(FILES ${PYTHON_ROOT}/PCbuild/python37.dll DESTINATION bin)
INSTALL(FILES ${PYTHON_ROOT}/PCbuild/python37.pdb DESTINATION pdb)
ELSEIF(EXISTS "${PYTHON_ROOT}/PCbuild/amd64/python27.dll")
INSTALL(FILES ${PYTHON_ROOT}/PCbuild/amd64/python37.dll DESTINATION bin)
INSTALL(FILES ${PYTHON_ROOT}/PCbuild/amd64/python37.pdb DESTINATION pdb)
ENDIF()
ADD_SUBDIRECTORY(src/runner)
ADD_SUBDIRECTORY(src/proxy)
add_subdirectory(src/runner)
+12 -4
View File
@@ -7,14 +7,22 @@ environment:
build:
parallel: true
build_script:
- cmd: >-
git clone --depth=1 --branch=%APPVEYOR_REPO_BRANCH% https://github.com/ModOrganizer2/modorganizer-umbrella.git c:\projects\modorganizer-umbrella 2> $null
- pwsh: >-
$ErrorActionPreference = 'Stop'
mkdir c:\projects\modorganizer-build -type directory
git clone --depth=1 --no-single-branch https://github.com/ModOrganizer2/modorganizer-umbrella.git c:\projects\modorganizer-umbrella
New-Item -ItemType Directory -Path c:\projects\modorganizer-build
cd c:\projects\modorganizer-umbrella
($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH -eq $null) ? ($branch = $env:APPVEYOR_REPO_BRANCH) : ($branch = $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH)
C:\Python37-x64\python.exe unimake.py -d c:\projects\modorganizer-build -s Appveyor_Build=True %APPVEYOR_PROJECT_NAME%
git checkout $(git show-ref --verify --quiet refs/remotes/origin/${branch} || echo '-b') ${branch}
C:\Python37-x64\python.exe unimake.py -d c:\projects\modorganizer-build -s Appveyor_Build=True ${env:APPVEYOR_PROJECT_NAME}
if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode ) }
artifacts:
- path: build\src\proxy\plugin_python.dll
name: plugin_python_dll
+11 -69
View File
@@ -1,70 +1,12 @@
CMAKE_MINIMUM_REQUIRED (VERSION 2.8.11)
cmake_minimum_required(VERSION 3.16)
# appveyor does not build modorganizer in its standard location, so use
# DEPENDENCIES_DIR to find cmake_common
if(DEFINED DEPENDENCIES_DIR)
include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/src.cmake)
else()
include(../../../cmake_common/src.cmake)
endif()
CMAKE_POLICY(SET CMP0020 NEW)
CMAKE_POLICY(SET CMP0043 NEW)
SET(${PROJ_NAME}_SRCS proxypython.cpp)
SET(${PROJ_NAME}_HDRS
proxypython.h
resource.h)
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
SET(CMAKE_AUTOMOC ON)
SET(CMAKE_AUTOUIC ON)
FIND_PACKAGE(Qt5Widgets REQUIRED)
FIND_PACKAGE(Qt5Network REQUIRED)
#QT5_WRAP_UI(${PROJ_NAME}_UIHDRS ${${PROJ_NAME}_FORMS})
SET(Boost_USE_STATIC_LIBS ON)
SET(Boost_USE_MULTITHREADED ON)
SET(Boost_USE_STATIC_RUNTIME OFF)
FIND_PACKAGE(Boost)
IF (Boost_FOUND)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
ENDIF (Boost_FOUND)
SET(default_project_path "${DEPENDENCIES_DIR}/modorganizer_super")
GET_FILENAME_COMPONENT(${default_project_path} ${default_project_path} REALPATH)
SET(project_path "${default_project_path}" CACHE PATH "path to the other mo projects")
SET(lib_path "${project_path}/../../install/libs")
ADD_DEFINITIONS(-DUNICODE -D_UNICODE)
INCLUDE_DIRECTORIES(${project_path}/uibase/src
${CMAKE_SOURCE_DIR}/src/runner/
${PYTHON_ROOT}/Include)
LINK_DIRECTORIES(${lib_path})
ADD_LIBRARY(${PROJ_NAME} SHARED ${${PROJ_NAME}_HDRS} ${${PROJ_NAME}_SRCS} ${${PROJ_NAME}_UIHDRS})
TARGET_LINK_LIBRARIES(${PROJ_NAME}
Qt5::Widgets
Qt5::Network
${Boost_LIBRARIES}
uibase)
IF (MSVC)
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS "/std:c++latest")
ENDIF()
IF (MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4)
# 32 bits
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS "/LARGEADDRESSAWARE")
ENDIF()
IF (NOT "${OPTIMIZE_COMPILE_FLAGS}" STREQUAL "")
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO ${OPTIMIZE_COMPILE_FLAGS})
ENDIF()
IF (NOT "${OPTIMIZE_LINK_FLAGS}" STREQUAL "")
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS_RELWITHDEBINFO ${OPTIMIZE_LINK_FLAGS})
ENDIF()
###############
## Installation
INSTALL(TARGETS ${PROJ_NAME}
RUNTIME DESTINATION bin/plugins)
INSTALL(FILES $<TARGET_PDB_FILE:${PROJ_NAME}>
DESTINATION pdb)
requires_library(python)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/src/runner)
-7
View File
@@ -1,7 +0,0 @@
#include "resource.h"
#ifdef SCONS_BUILD
IDR_LOADER_DLL BINARY MOVEABLE PURE pythonRunner.dll
#else
IDR_LOADER_DLL BINARY MOVEABLE PURE "..\\runner\\pythonRunner.dll"
#endif
+126 -224
View File
@@ -26,157 +26,86 @@ along with python proxy plugin. If not, see <http://www.gnu.org/licenses/>.
#include <QWidget>
#include <QMessageBox>
#include <QCoreApplication>
#include "resource.h"
#include "log.h"
using namespace MOBase;
const char *ProxyPython::s_DownloadPythonURL = "http://www.python.org/download/releases/";
HMODULE GetOwnModuleHandle()
{
HMODULE hMod = nullptr;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&GetOwnModuleHandle), &hMod);
return hMod;
}
QString ExtractResource(WORD resourceID, const QString &szFilename)
{
HMODULE mod = GetOwnModuleHandle();
HRSRC hResource = FindResourceW(mod, MAKEINTRESOURCE(resourceID), L"BINARY");
if (hResource == nullptr) {
throw MyException("embedded dll not available: " + windowsErrorString(::GetLastError()));
}
HGLOBAL hFileResource = LoadResource(mod, hResource);
if (hFileResource == nullptr) {
throw MyException("failed to load embedded dll resource: " + windowsErrorString(::GetLastError()));
}
LPVOID lpFile = LockResource(hFileResource);
if (lpFile == nullptr) {
throw MyException(QString("failed to lock resource: %1").arg(windowsErrorString(::GetLastError())));
}
DWORD dwSize = SizeofResource(mod, hResource);
QString outFile = QDir::tempPath() + "/" + szFilename;
HANDLE hFile = CreateFileW(outFile.toStdWString().c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) {
if (::GetLastError() == ERROR_SHARING_VIOLATION) {
// dll exists and is opened by another instance of MO, shouldn't be outdated then...
return outFile;
} else {
throw MyException(QString("failed to open python runner: %1").arg(windowsErrorString(::GetLastError())));
}
}
HANDLE hFileMap = CreateFileMapping(hFile, nullptr, PAGE_READWRITE, 0, dwSize, nullptr);
if (hFileMap == NULL) {
throw MyException(QString("failed to map python runner: %1").arg(windowsErrorString(::GetLastError())));
}
LPVOID lpAddress = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);
if (lpAddress == nullptr) {
throw MyException(QString("failed to map view of file: %1").arg(windowsErrorString(::GetLastError())));
}
CopyMemory(lpAddress, lpFile, dwSize);
UnmapViewOfFile(lpAddress);
CloseHandle(hFileMap);
::FlushFileBuffers(hFile);
CloseHandle(hFile);
return outFile;
}
ProxyPython::ProxyPython()
: m_MOInfo(nullptr), m_Runner(nullptr), m_LoadFailure(FAIL_NOTINIT)
: m_MOInfo{ nullptr },
m_RunnerLib{ nullptr },
m_Runner{ nullptr },
m_LoadFailure(FailureType::NONE)
{
}
ProxyPython::~ProxyPython()
{
if (!m_TempRunnerFile.isEmpty()) {
::FreeLibrary(m_RunnerLib);
QFile(m_TempRunnerFile).remove();
}
}
typedef IPythonRunner* (*CreatePythonRunner_func)(const MOBase::IOrganizer *moInfo, const QString &pythonPath);
bool ProxyPython::init(IOrganizer *moInfo)
{
using CreatePythonRunner_func = IPythonRunner * (*)();
m_MOInfo = moInfo;
if (!m_MOInfo->pluginSetting(name(), "enabled").toBool()) {
m_LoadFailure = FAIL_NONE;
if (m_MOInfo && !m_MOInfo->isPluginEnabled(this)) {
return false;
}
m_LoadFailure = FAIL_OTHER;
if (QCoreApplication::applicationDirPath().contains(';')) {
m_LoadFailure = FAIL_SEMICOLON;
m_LoadFailure = FailureType::SEMICOLON;
return true;
}
QString pythonPath = m_MOInfo->pluginSetting(name(), "python_dir").toString();
// load the pythonrunner library
m_RunnerLib = ::LoadLibraryW(QDir::toNativeSeparators(
IOrganizer::getPluginDataPath() + "/pythonRunner.dll").toStdWString().c_str());
if (!pythonPath.isEmpty() && !QFile::exists(pythonPath + "/python.exe")) {
m_LoadFailure = FAIL_WRONGPYTHONPATH;
return true;
}
m_RunnerLib = ::LoadLibraryW(QDir::toNativeSeparators(m_MOInfo->pluginDataPath() + "/pythonRunner.dll").toStdWString().c_str());
if (m_RunnerLib != nullptr) {
CreatePythonRunner_func CreatePythonRunner = (CreatePythonRunner_func)::GetProcAddress(m_RunnerLib, "CreatePythonRunner");
if (CreatePythonRunner == nullptr) {
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 != nullptr) {
m_LoadFailure = FAIL_NONE;
} else {
m_LoadFailure = FAIL_INITFAIL;
}
return true;
} else {
if (!m_RunnerLib) {
DWORD error = ::GetLastError();
qCritical("Failed to load python runner (%s): %s", qUtf8Printable(m_TempRunnerFile), qUtf8Printable(windowsErrorString(error)));
log::error("failed to load python runner ({}): {}", qUtf8Printable(windowsErrorString(error)));
if (error == ERROR_MOD_NOT_FOUND) {
m_LoadFailure = FAIL_MISSINGDEPENDENCIES;
m_LoadFailure = FailureType::DLL_NOT_FOUND;
}
else {
m_LoadFailure = FailureType::INVALID_DLL;
}
return true;
}
const CreatePythonRunner_func createPythonRunner = (CreatePythonRunner_func)::GetProcAddress(m_RunnerLib, "CreatePythonRunner");
if (!createPythonRunner) {
m_LoadFailure = FailureType::INVALID_DLL;
return true;
}
if (m_MOInfo && m_MOInfo->persistent(name(), "tryInit", false).toBool()) {
m_LoadFailure = FailureType::INITIALIZATION;
if (QMessageBox::question(parentWidget(), tr("Python Initialization failed"),
tr("On a previous start the Python Plugin failed to initialize.\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) {
// we force enabled here (note: this is a persistent settings since MO2 2.4 or something), plugin
// usually should not handle enabled/disabled themselves but this is a base plugin so...
m_MOInfo->setPersistent(name(), "enabled", false, true);
return true;
}
}
if (m_MOInfo) {
m_MOInfo->setPersistent(name(), "tryInit", true);
}
m_Runner = std::unique_ptr<IPythonRunner>{ createPythonRunner() };
if (m_MOInfo) {
m_MOInfo->setPersistent(name(), "tryInit", false);
}
if (!m_Runner) {
m_LoadFailure = FailureType::INITIALIZATION;
}
return true;
}
QString ProxyPython::name() const
@@ -184,9 +113,14 @@ QString ProxyPython::name() const
return "Python Proxy";
}
QString ProxyPython::localizedName() const
{
return tr("Python Proxy");
}
QString ProxyPython::author() const
{
return "Tannin";
return "AnyOldName3, Holt59, Silarn, Tannin";
}
QString ProxyPython::description() const
@@ -196,145 +130,113 @@ QString ProxyPython::description() const
VersionInfo ProxyPython::version() const
{
return VersionInfo(2, 1, 0, VersionInfo::RELEASE_FINAL);
}
bool ProxyPython::isActive() const
{
return m_LoadFailure == FAIL_NOTINIT;
return VersionInfo(2, 3, 0, VersionInfo::RELEASE_FINAL);
}
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;
return {};
}
QStringList ProxyPython::pluginList(const QString &pluginPath) const
QStringList ProxyPython::pluginList(const QDir& pluginPath) const
{
QDirIterator iter(pluginPath, QStringList("*.py"));
QDir dir(pluginPath);
dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
QDirIterator iter(dir);
// Note: We put python script (.py) and directory names, not the __init__.py
// files in those since it is easier for the runner to import them.
QStringList result;
while (iter.hasNext()) {
result.append(iter.next());
QString name = iter.next();
QFileInfo info = iter.fileInfo();
if (info.isFile() && name.endsWith(".py")) {
result.append(name);
}
else if (info.isDir() && QDir(info.absoluteFilePath()).exists("__init__.py")) {
result.append(name);
}
}
return result;
}
QList<QObject*> ProxyPython::instantiate(const QString &pluginName)
QList<QObject*> ProxyPython::load(const QString& identifier)
{
if (m_Runner != nullptr) {
QList<QObject*> result = m_Runner->instantiate(pluginName);
return result;
} else {
return QList<QObject*>();
if (!m_Runner) {
return {};
}
return m_Runner->load(identifier);
}
void ProxyPython::unload(const QString& identifier)
{
if (m_Runner) {
return m_Runner->unload(identifier);
}
}
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_LoadFailure == FAIL_SEMICOLON) {
result.push_back(PROBLEM_SEMICOLON);
} else if (m_Runner != nullptr) {
if (!m_Runner->isPythonInstalled()) {
// don't know how this could happen but wth
result.push_back(PROBLEM_PYTHONMISSING);
}
if (!m_Runner->isPythonVersionSupported()) {
result.push_back(PROBLEM_PYTHONWRONGVERSION);
}
auto failure = m_LoadFailure;
// don't know how this could happen but wth
if (m_Runner && !m_Runner->isPythonInitialized()) {
failure = FailureType::INITIALIZATION;
}
return result;
if (failure != FailureType::NONE) {
return { static_cast<std::underlying_type_t<FailureType>>(failure) };
}
return {};
}
QString ProxyPython::shortDescription(unsigned int key) const
{
switch (key) {
case PROBLEM_PYTHONMISSING: {
return tr("Python not installed or not found");
} break;
case PROBLEM_PYTHONWRONGVERSION: {
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;
case PROBLEM_SEMICOLON: {
switch (static_cast<FailureType>(key)) {
case FailureType::SEMICOLON:
return tr("ModOrganizer path contains a semicolon");
} break;
case FailureType::DLL_NOT_FOUND:
return tr("Python DLL not found");
case FailureType::INVALID_DLL:
return tr("Invalid Python DLL");
case FailureType::INITIALIZATION:
return tr("Initializing Python failed");
default:
throw MyException(tr("invalid problem key %1").arg(key));
return tr("invalid problem key %1").arg(key);
}
}
QString ProxyPython::fullDescription(unsigned int key) const
{
switch (key) {
case PROBLEM_PYTHONMISSING: {
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 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;
case PROBLEM_SEMICOLON: {
return tr("The path to Mod Organizer (%1) contains a semicolon. <br>"
"While this is legal on NTFS drives there is a lot of software that doesn't handle it correctly.<br>"
"Unfortunately MO depends on libraries that seem to fall into that group.<br>"
"As a result the python plugin can't be loaded.<br>"
"The only solution I can offer is to remove the semicolon / move MO to a path without a semicolon.").arg(QCoreApplication::applicationDirPath());
} break;
default:
throw MyException(QString("invalid problem key %1").arg(key));
switch (static_cast<FailureType>(key)) {
case FailureType::SEMICOLON:
return tr("The path to Mod Organizer (%1) contains a semicolon. <br>"
"While this is legal on NTFS drives, many softwares do not handle it correctly.<br>"
"Unfortunately MO depends on libraries that seem to fall into that group.<br>"
"As a result the python plugin cannot be loaded, and the only solution we can"
"offer is to remove the semicolon or move MO to a path without a semicolon.").arg(QCoreApplication::applicationDirPath());
case FailureType::DLL_NOT_FOUND:
return tr("The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem.");
case FailureType::INVALID_DLL:
return tr("The Python plugin DLL is invalid, maybe your antivirus is blocking it. "
"Re-installing MO2 and adding exclusions for it to your AV might fix the problem.");
case FailureType::INITIALIZATION:
return tr("The initialization of the Python plugin DLL failed, unfortunately without any details.");
default:
return tr("invalid problem key %1").arg(key);
}
}
bool ProxyPython::hasGuidedFix(unsigned int key) const
{
return (key == PROBLEM_PYTHONMISSING) || (key == PROBLEM_PYTHONWRONGVERSION);
return false;
}
void ProxyPython::startGuidedFix(unsigned int key) const
{
if ((key == PROBLEM_PYTHONMISSING) || (key == PROBLEM_PYTHONWRONGVERSION)) {
::ShellExecuteA(nullptr, "open", s_DownloadPythonURL, nullptr, nullptr, SW_SHOWNORMAL);
}
}
+29 -42
View File
@@ -20,11 +20,14 @@ along with python proxy plugin. If not, see <http://www.gnu.org/licenses/>.
#ifndef PROXYPYTHON_H
#define PROXYPYTHON_H
#include <map>
#include <memory>
#include <Windows.h>
#include <ipluginproxy.h>
#include <iplugindiagnose.h>
#include <map>
#include <Windows.h>
#include <pythonrunner.h>
@@ -38,58 +41,42 @@ class ProxyPython : public QObject, public MOBase::IPluginProxy, public MOBase::
public:
ProxyPython();
~ProxyPython();
virtual bool init(MOBase::IOrganizer *moInfo);
virtual QString name() const;
virtual QString author() const;
virtual QString description() const;
virtual MOBase::VersionInfo version() const;
virtual bool isActive() const;
virtual QList<MOBase::PluginSetting> settings() const;
virtual QString name() const override;
virtual QString localizedName() const override;
virtual QString author() const override;
virtual QString description() const override;
virtual MOBase::VersionInfo version() const override;
virtual QList<MOBase::PluginSetting> settings() const override;
QStringList pluginList(const QString &pluginPath) const;
QList<QObject*> instantiate(const QString &pluginName);
/**
* @return the parent widget for newly created dialogs
* @note needs to be public so it can be exposed to plugins
*/
virtual QWidget *getParentWidget() { return parentWidget(); }
QStringList pluginList(const QDir& pluginPath) const override;
QList<QObject*> load(const QString& identifier) override;
void unload(const QString& identifier) override;
public: // IPluginDiagnose
virtual std::vector<unsigned int> activeProblems() const;
virtual QString shortDescription(unsigned int key) const;
virtual QString fullDescription(unsigned int key) const;
virtual bool hasGuidedFix(unsigned int key) const;
virtual void startGuidedFix(unsigned int key) const;
virtual std::vector<unsigned int> activeProblems() const override;
virtual QString shortDescription(unsigned int key) const override;
virtual QString fullDescription(unsigned int key) const override;
virtual bool hasGuidedFix(unsigned int key) const override;
virtual void startGuidedFix(unsigned int key) const override;
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 unsigned int PROBLEM_SEMICOLON = 6;
static const char *s_DownloadPythonURL;
MOBase::IOrganizer *m_MOInfo;
QString m_TempRunnerFile;
HMODULE m_RunnerLib;
IPythonRunner *m_Runner;
std::unique_ptr<IPythonRunner> m_Runner;
enum {
FAIL_NONE,
FAIL_SEMICOLON,
FAIL_NOTINIT,
FAIL_MISSINGDEPENDENCIES,
FAIL_INITFAIL,
FAIL_WRONGPYTHONPATH,
FAIL_PYTHONDETECTION,
FAIL_OTHER
} m_LoadFailure;
enum class FailureType : unsigned int {
NONE = 0,
SEMICOLON = 1,
DLL_NOT_FOUND = 2,
INVALID_DLL = 3,
INITIALIZATION = 4
};
FailureType m_LoadFailure;
};
+5 -78
View File
@@ -2,88 +2,15 @@
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>ProxyPython</name>
<name>QObject</name>
<message>
<location filename="proxy/proxypython.cpp" line="150"/>
<source>Python Initialization failed</source>
<location filename="runner/error.h" line="68"/>
<source>An unexpected C++ exception was thrown in python code.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="151"/>
<source>On a previous start the Python Plugin failed to initialize.
Either the value in Settings-&gt;Plugins-&gt;ProxyPython-&gt;plugin_dir is set incorrectly or it is empty and auto-detection doesn&apos;t work for whatever reason.
Do you want to try initializing python again (at the risk of another crash)?
Suggestion: Select &quot;no&quot;, and click the warning sign for further help. Afterwards you have to re-enable the python plugin.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="194"/>
<source>Proxy Plugin to allow plugins written in python to be loaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="269"/>
<source>Python not installed or not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="272"/>
<source>Python version is incompatible</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="275"/>
<source>Invalid python path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="278"/>
<source>Initializing Python failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="281"/>
<source>Python auto-detection failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="284"/>
<source>ModOrganizer path contains a semicolon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="287"/>
<source>invalid problem key %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="296"/>
<source>Some MO plugins require the python interpreter to be installed. These plugins will not even show up in settings-&amp;gt;plugins.&lt;br&gt;If you want to use those plugins, please install the 32-bit version of Python 2.7.x from &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;.&lt;br&gt;This is only required to use some extended functionality in MO, you do not need Python to play the game.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="302"/>
<source>Your installed python version has a different version than 2.7. Some MO plugins may not work.&lt;br&gt;If you have multiple versions of python installed you may have to configure the path to 2.7 (32 bit) in the settings dialog.&lt;br&gt;This is only required to use some extended functionality in MO, you do not need Python to play the game.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="309"/>
<source>Please set python_dir in Settings-&gt;Plugins-&gt;ProxyPython to the path of your python 2.7 (32 bit) installation.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="312"/>
<source>The auto-detection of the python path failed. I don&apos;t know why this would happen but you can try to fix it by setting python_dir in Settings-&gt;Plugins-&gt;ProxyPython to the path of your python 2.7 (32 bit) installation.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="316"/>
<source>Sorry, I don&apos;t know any details. Most likely your python installation is not supported.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="319"/>
<source>The path to Mod Organizer (%1) contains a semicolon. &lt;br&gt;While this is legal on NTFS drives there is a lot of software that doesn&apos;t handle it correctly.&lt;br&gt;Unfortunately MO depends on libraries that seem to fall into that group.&lt;br&gt;As a result the python plugin can&apos;t be loaded.&lt;br&gt;The only solution I can offer is to remove the semicolon / move MO to a path without a semicolon.</source>
<location filename="runner/error.h" line="117"/>
<source>An unknown exception was thrown in python code.</source>
<translation type="unfinished"></translation>
</message>
</context>
+44 -69
View File
@@ -1,77 +1,52 @@
CMAKE_MINIMUM_REQUIRED (VERSION 2.8.11)
cmake_minimum_required(VERSION 3.16)
SET(PROJ_NAME pythonrunner)
project(pythonrunner)
set(project_type plugin)
set(enable_warnings OFF)
set(enable_bigobj ON)
set(install_dir bin/plugins/data)
set(create_translations ON)
PROJECT(${PROJ_NAME})
add_compile_definitions(
QT_NO_KEYWORDS
PYTHONRUNNER_LIBRARY)
CMAKE_POLICY(SET CMP0020 NEW)
# appveyor does not build modorganizer in its standard location, so use
# DEPENDENCIES_DIR to find cmake_common
if(DEFINED DEPENDENCIES_DIR)
include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/project.cmake)
include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/src.cmake)
else()
include(../../../cmake_common/project.cmake)
include(../../../cmake_common/src.cmake)
endif()
requires_project(game_features)
requires_library(python)
FILE(GLOB ${PROJ_NAME}_SRCS *.cpp)
FILE(GLOB ${PROJ_NAME}_HDRS *.h)
add_filter(NAME src/converters GROUPS
converters
pythonutils
shared_ptr_converter
tuple_helper
variant_helper
)
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
SET(CMAKE_AUTOMOC ON)
SET(CMAKE_AUTOUIC ON)
ADD_DEFINITIONS(-DQT_NO_KEYWORDS)
FIND_PACKAGE(Qt5Widgets REQUIRED)
FIND_PACKAGE(Qt5Network REQUIRED)
#QT5_WRAP_UI(${PROJ_NAME}_UIHDRS ${${PROJ_NAME}_FORMS})
FIND_PACKAGE(Qt5LinguistTools)
QT5_CREATE_TRANSLATION(${PROJ_NAME}_translations_qm ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/${PROJ_NAME}_en.ts)
add_filter(NAME src/runner GROUPS
pythonrunner
pylogger
widgets
)
SET(Boost_USE_STATIC_LIBS OFF)
SET(Boost_USE_MULTITHREADED ON)
SET(Boost_USE_STATIC_RUNTIME OFF)
FIND_PACKAGE(Boost REQUIRED)
add_filter(NAME src/utils GROUPS
error
gilock
sipapiaccess
)
IF (Boost_FOUND)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
ENDIF (Boost_FOUND)
SET(default_project_path "${DEPENDENCIES_DIR}/modorganizer_super")
GET_FILENAME_COMPONENT(${default_project_path} ${default_project_path} REALPATH)
SET(project_path "${default_project_path}" CACHE PATH "path to the other mo projects")
SET(lib_path "${project_path}/../../install/libs")
INCLUDE_DIRECTORIES(${project_path}/uibase/src
${project_path}/game_features/src
${PYTHON_ROOT}/Include
${PYTHON_ROOT}/PC)
LINK_DIRECTORIES(${lib_path}
${PYTHON_ROOT}/PCbuild
${Boost_LIBRARY_DIRS})
ADD_LIBRARY(${PROJ_NAME} SHARED ${${PROJ_NAME}_HDRS} ${${PROJ_NAME}_SRCS} ${${PROJ_NAME}_translations_qm})
TARGET_LINK_LIBRARIES(${PROJ_NAME}
Qt5::Widgets
Qt5::Network
uibase)
ADD_DEFINITIONS(-DPYTHONRUNNER_LIBRARY)
IF (MSVC)
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS "/std:c++latest")
ENDIF()
IF (MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4)
# 32 bits
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS "/LARGEADDRESSAWARE")
ENDIF()
IF (NOT "${OPTIMIZE_COMPILE_FLAGS}" STREQUAL "")
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO ${OPTIMIZE_COMPILE_FLAGS})
ENDIF()
IF (NOT "${OPTIMIZE_LINK_FLAGS}" STREQUAL "")
SET_TARGET_PROPERTIES(${PROJ_NAME} PROPERTIES LINK_FLAGS_RELWITHDEBINFO ${OPTIMIZE_LINK_FLAGS})
ENDIF()
###############
## Installation
INSTALL(TARGETS ${PROJ_NAME}
RUNTIME DESTINATION bin/plugins/data
ARCHIVE DESTINATION libs)
INSTALL(FILES $<TARGET_PDB_FILE:${PROJ_NAME}>
DESTINATION pdb)
add_filter(NAME src/wrappers GROUPS
gamefeatureswrappers
proxypluginwrappers
pythonwrapperutilities
uibasewrappers
)
File diff suppressed because it is too large Load Diff
-17
View File
@@ -8,23 +8,6 @@
using namespace MOBase;
namespace bpy = boost::python;
void reportPythonError()
{
if (PyErr_Occurred()) {
ErrWrapper &errWrapper = ErrWrapper::instance();
errWrapper.startRecordingExceptionMessage();
PyErr_Print();
errWrapper.stopRecordingExceptionMessage();
QString errMsg = errWrapper.getLastExceptionMessage();
throw MyException(errMsg);
} else {
throw MyException("An unexpected C++ exception was thrown in python code");
}
}
ErrWrapper & ErrWrapper::instance()
{
static ErrWrapper err;
+104 -7
View File
@@ -1,16 +1,18 @@
#ifndef ERROR_H
#define ERROR_H
#include <QString>
#include <sstream>
// turn an error from the python interpreter into an exception
void reportPythonError();
#include <QString>
#include <fmt/format.h>
#include <sstream>
#include <utility.h>
struct ErrWrapper
{
static ErrWrapper & instance();
void write(const char * message);
static ErrWrapper& instance();
void write(const char* message);
void startRecordingExceptionMessage();
@@ -23,4 +25,99 @@ struct ErrWrapper
std::stringstream lastException;
};
namespace pyexcept {
/**
* @brief Exception to throw when a python implementation does not implement
* a pure virtual function.
*/
class MissingImplementation : public MOBase::Exception {
public:
MissingImplementation(std::string const& className, std::string const& methodName) :
Exception(QString::fromStdString(
fmt::format("Python class implementing \"{}\" has no implementation of method \"{}\".",
className, methodName))) { }
};
/**
* @brief Exception to throw when a python error occurs.
*/
class PythonError : public MOBase::Exception {
public:
/**
* @brief Create a new PythonError, fetching the error message from python. If the message
* cannot be retrieved, `defaultErrorMessage()` is used instead.
*/
PythonError() : Exception(getPythonErrorMessage()) { }
/**
* @brief Create a new PythonError with the given message.
*
* @param message Message for the exception.
*/
PythonError(QString message) : Exception(message) { }
protected:
/**
*
*/
static QString defaultErrorMessage() {
return QObject::tr("An unexpected C++ exception was thrown in python code.");
}
/**
*
*/
static QString getPythonErrorMessage() {
if (PyErr_Occurred()) {
ErrWrapper& errWrapper = ErrWrapper::instance();
errWrapper.startRecordingExceptionMessage();
PyErr_Print();
errWrapper.stopRecordingExceptionMessage();
return errWrapper.getLastExceptionMessage();
}
else {
return defaultErrorMessage();
}
}
};
/**
* @brief Exception to throw when an unknown error occured. This is typically thrown
* from a catch(...) block.
*/
class UnknownException : public MOBase::Exception {
public:
/**
* @brief Create a new UnknownException with the default message.
*
* @see defaultErrorMessage
*/
UnknownException() : Exception(defaultErrorMessage()) { }
/**
* @brief Create a new UnknownException with the given message.
*
* @param message Message for the exception.
*/
UnknownException(QString message) : Exception(message) { }
protected:
/**
*
*/
static QString defaultErrorMessage() {
return QObject::tr("An unknown exception was thrown in python code.");
}
};
}
#endif // ERROR_H
+132 -89
View File
@@ -1,7 +1,6 @@
#include "gamefeatureswrappers.h"
#include <boost/any.hpp>
#include <any>
#include <typeindex>
#include <ipluginlist.h>
@@ -9,7 +8,8 @@
#include <isavegame.h>
#include <isavegameinfowidget.h>
#include "gilock.h"
#include "shared_ptr_converter.h"
#include "ifiletree.h"
#include "pythonwrapperutilities.h"
/////////////////////////////
@@ -18,22 +18,22 @@
bool BSAInvalidationWrapper::isInvalidationBSA(const QString &bsaName)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, bool>(this, "isInvalidationBSA", bsaName);
return basicWrapperFunctionImplementation<bool>(this, "isInvalidationBSA", bsaName);
}
void BSAInvalidationWrapper::deactivate(MOBase::IProfile *profile)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, void>(this, "deactivate", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<void>(this, "deactivate", boost::python::ptr(profile));
}
void BSAInvalidationWrapper::activate(MOBase::IProfile *profile)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, void>(this, "activate", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<void>(this, "activate", boost::python::ptr(profile));
}
bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile *profile)
{
return basicWrapperFunctionImplementation<BSAInvalidationWrapper, bool>(this, "prepareProfile", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<bool>(this, "prepareProfile", boost::python::ptr(profile));
}
/// end BSAInvalidation Wrapper
/////////////////////////////
@@ -42,22 +42,22 @@ bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile *profile)
QStringList DataArchivesWrapper::vanillaArchives() const
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, QStringList>(this, "vanillaArchives");
return basicWrapperFunctionImplementation<QStringList>(this, "vanillaArchives");
}
QStringList DataArchivesWrapper::archives(const MOBase::IProfile *profile) const
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, QStringList>(this, "archives", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<QStringList>(this, "archives", boost::python::ptr(profile));
}
void DataArchivesWrapper::addArchive(MOBase::IProfile *profile, int index, const QString &archiveName)
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, void>(this, "addArchive", boost::python::ptr(profile), index, archiveName);
return basicWrapperFunctionImplementation<void>(this, "addArchive", boost::python::ptr(profile), index, archiveName);
}
void DataArchivesWrapper::removeArchive(MOBase::IProfile *profile, const QString &archiveName)
{
return basicWrapperFunctionImplementation<DataArchivesWrapper, void>(this, "removeArchive", boost::python::ptr(profile), archiveName);
return basicWrapperFunctionImplementation<void>(this, "removeArchive", boost::python::ptr(profile), archiveName);
}
/// end DataArchives Wrapper
/////////////////////////////
@@ -66,22 +66,22 @@ void DataArchivesWrapper::removeArchive(MOBase::IProfile *profile, const QString
void GamePluginsWrapper::writePluginLists(const MOBase::IPluginList * pluginList)
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, void>(this, "writePluginLists", boost::python::ptr(pluginList));
return basicWrapperFunctionImplementation<void>(this, "writePluginLists", boost::python::ptr(pluginList));
}
void GamePluginsWrapper::readPluginLists(MOBase::IPluginList * pluginList)
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, void>(this, "readPluginLists", boost::python::ptr(pluginList));
return basicWrapperFunctionImplementation<void>(this, "readPluginLists", boost::python::ptr(pluginList));
}
void GamePluginsWrapper::getLoadOrder(QStringList &loadOrder)
QStringList GamePluginsWrapper::getLoadOrder()
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, void>(this, "getLoadOrder", loadOrder);
return basicWrapperFunctionImplementation<QStringList>(this, "getLoadOrder");
}
bool GamePluginsWrapper::lightPluginsAreSupported()
{
return basicWrapperFunctionImplementation<GamePluginsWrapper, bool>(this, "lightPluginsAreSupported");
return basicWrapperFunctionImplementation<bool>(this, "lightPluginsAreSupported");
}
/// end GamePlugins Wrapper
@@ -91,81 +91,95 @@ bool GamePluginsWrapper::lightPluginsAreSupported()
MappingType LocalSavegamesWrapper::mappings(const QDir & profileSaveDir) const
{
return basicWrapperFunctionImplementation<LocalSavegamesWrapper, MappingType>(this, "mappings", profileSaveDir);
return basicWrapperFunctionImplementation<MappingType>(this, "mappings", profileSaveDir);
}
bool LocalSavegamesWrapper::prepareProfile(MOBase::IProfile * profile)
{
return basicWrapperFunctionImplementation<LocalSavegamesWrapper, bool>(this, "prepareProfile", boost::python::ptr(profile));
return basicWrapperFunctionImplementation<bool>(this, "prepareProfile", boost::python::ptr(profile));
}
/// end LocalSavegames Wrapper
/////////////////////////////
/// ModDataChecker Wrapper
ModDataChecker::CheckReturn ModDataCheckerWrapper::dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const {
return basicWrapperFunctionImplementation<CheckReturn>(this, "dataLooksValid", fileTree);
}
std::shared_ptr<MOBase::IFileTree> ModDataCheckerWrapper::fix(std::shared_ptr<MOBase::IFileTree> fileTree) const {
return utils::clean_shared_ptr(
basicWrapperFunctionImplementationWithDefault<std::shared_ptr<MOBase::IFileTree>>(
this, [](auto&&... args) { return nullptr; }, "fix", fileTree));
}
/// end ModDataChecker Wrapper
/////////////////////////////
/// ModDataContent Wrapper
std::vector<ModDataContent::Content> ModDataContentWrapper::getAllContents() const {
return basicWrapperFunctionImplementation<std::vector<Content>>(this, "getAllContents");
}
std::vector<int> ModDataContentWrapper::getContentsFor(std::shared_ptr<const MOBase::IFileTree> fileTree) const {
return basicWrapperFunctionImplementation<std::vector<int>>(this, "getContentsFor", fileTree);
}
/// end ModDataContent Wrapper
/////////////////////////////
/// SaveGameInfo Wrapper
MOBase::ISaveGame const * SaveGameInfoWrapper::getSaveGameInfo(QString const & file) const
SaveGameInfoWrapper::MissingAssets SaveGameInfoWrapper::getMissingAssets(MOBase::ISaveGame const& save) const
{
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, MOBase::ISaveGame const *>(this, "getSaveGameInfo", file);
return basicWrapperFunctionImplementation<SaveGameInfoWrapper::MissingAssets>(this, "getMissingAssets", boost::ref(save));
}
SaveGameInfoWrapper::MissingAssets SaveGameInfoWrapper::getMissingAssets(QString const & file) const
MOBase::ISaveGameInfoWidget* SaveGameInfoWrapper::getSaveGameWidget(QWidget* parent) const
{
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, SaveGameInfoWrapper::MissingAssets>(this, "getMissingAssets", file);
return basicWrapperFunctionImplementation<MOBase::ISaveGameInfoWidget*>(this, m_SaveGameWidget, "getSaveGameWidget", parent);
}
MOBase::ISaveGameInfoWidget * SaveGameInfoWrapper::getSaveGameWidget(QWidget * parent) const
{
qCritical("Calling method with unimplemented from_python converter.");
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, MOBase::ISaveGameInfoWidget *>(this, "getSaveGameWidget", boost::python::ptr(parent));
}
bool SaveGameInfoWrapper::hasScriptExtenderSave(QString const & file) const
{
return basicWrapperFunctionImplementation<SaveGameInfoWrapper, bool>(this, "hasScriptExtenderSave", file);
}
/// end SaveGameInfo Wrapper
/////////////////////////////
/// ScriptExtender Wrapper
QString ScriptExtenderWrapper::BinaryName() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "BinaryName");
return basicWrapperFunctionImplementation<QString>(this, "BinaryName");
}
QString ScriptExtenderWrapper::PluginPath() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "PluginPath");
return basicWrapperFunctionImplementation<QString>(this, "PluginPath");
}
QString ScriptExtenderWrapper::loaderName() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "loaderName");
return basicWrapperFunctionImplementation<QString>(this, "loaderName");
}
QString ScriptExtenderWrapper::loaderPath() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "loaderPath");
return basicWrapperFunctionImplementation<QString>(this, "loaderPath");
}
QStringList ScriptExtenderWrapper::saveGameAttachmentExtensions() const
QString ScriptExtenderWrapper::savegameExtension() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QStringList>(this, "saveGameAttachmentExtensions");
return basicWrapperFunctionImplementation<QString>(this, "savegameExtension");
}
bool ScriptExtenderWrapper::isInstalled() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, bool>(this, "isInstalled");
return basicWrapperFunctionImplementation<bool>(this, "isInstalled");
}
QString ScriptExtenderWrapper::getExtenderVersion() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, QString>(this, "getExtenderVersion");
return basicWrapperFunctionImplementation<QString>(this, "getExtenderVersion");
}
WORD ScriptExtenderWrapper::getArch() const
{
return basicWrapperFunctionImplementation<ScriptExtenderWrapper, WORD>(this, "getArch");
return basicWrapperFunctionImplementation<WORD>(this, "getArch");
}
/// end ScriptExtender Wrapper
@@ -175,35 +189,30 @@ WORD ScriptExtenderWrapper::getArch() const
QStringList UnmanagedModsWrapper::mods(bool onlyOfficial) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QStringList>(this, "mods", onlyOfficial);
return basicWrapperFunctionImplementation<QStringList>(this, "mods", onlyOfficial);
}
QString UnmanagedModsWrapper::displayName(const QString & modName) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QString>(this, "displayName", modName);
return basicWrapperFunctionImplementation<QString>(this, "displayName", modName);
}
QFileInfo UnmanagedModsWrapper::referenceFile(const QString & modName) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QFileInfo>(this, "referenceFile", modName);
return basicWrapperFunctionImplementation<QFileInfo>(this, "referenceFile", modName);
}
QStringList UnmanagedModsWrapper::secondaryFiles(const QString & modName) const
{
return basicWrapperFunctionImplementation<UnmanagedModsWrapper, QStringList>(this, "secondaryFiles", modName);
return basicWrapperFunctionImplementation<QStringList>(this, "secondaryFiles", modName);
}
/// end UnmanagedMods Wrapper
/////////////////////////////
template<typename T>
void insertGameFeature(std::map<std::type_index, boost::any> &map, const boost::python::object &pyObject)
{
map[std::type_index(typeid(T))] = boost::python::extract<T*>(pyObject)();
}
game_features_map_from_python::game_features_map_from_python()
{
boost::python::converter::registry::push_back(&convertible, &construct, boost::python::type_id<std::map<std::type_index, boost::any>>());
boost::python::converter::registry::push_back(&convertible, &construct, boost::python::type_id<std::map<std::type_index, std::any>>());
}
void * game_features_map_from_python::convertible(PyObject * objPtr)
@@ -211,32 +220,34 @@ void * game_features_map_from_python::convertible(PyObject * objPtr)
return PyDict_Check(objPtr) ? objPtr : nullptr;
}
template<typename T>
void insertGameFeature(std::map<std::type_index, std::any>& map, const boost::python::object& pyObject)
{
map[std::type_index(typeid(T))] = boost::python::extract<T*>(pyObject)();
}
void game_features_map_from_python::construct(PyObject * objPtr, boost::python::converter::rvalue_from_python_stage1_data * data)
{
void *storage = ((boost::python::converter::rvalue_from_python_storage<std::map<std::type_index, boost::any>>*)data)->storage.bytes;
std::map<std::type_index, boost::any> *result = new (storage) std::map<std::type_index, boost::any>();
void *storage = ((boost::python::converter::rvalue_from_python_storage<std::map<std::type_index, std::any>>*)data)->storage.bytes;
std::map<std::type_index, std::any> *result = new (storage) std::map<std::type_index, std::any>();
boost::python::dict source(boost::python::handle<>(boost::python::borrowed(objPtr)));
boost::python::list keys = source.keys();
int len = boost::python::len(keys);
for (int i = 0; i < len; ++i)
{
boost::python::object pyKey = keys[i];
// pyKey should be a Boost.Python.class corresponding to a game feature.
std::string className = boost::python::extract<std::string>(pyKey.attr("__name__"))();
if (className == "BSAInvalidation")
insertGameFeature<BSAInvalidation>(*result, source[pyKey]);
else if (className == "DataArchives")
insertGameFeature<DataArchives>(*result, source[pyKey]);
else if (className == "GamePlugins")
insertGameFeature<GamePlugins>(*result, source[pyKey]);
else if (className == "LocalSavegames")
insertGameFeature<LocalSavegames>(*result, source[pyKey]);
else if (className == "SaveGameInfo")
insertGameFeature<SaveGameInfo>(*result, source[pyKey]);
else if (className == "ScriptExtender")
insertGameFeature<ScriptExtender>(*result, source[pyKey]);
else if (className == "UnmanagedMods")
insertGameFeature<UnmanagedMods>(*result, source[pyKey]);
boost::python::object pyValue = source[pyKey];
boost::mp11::mp_for_each<
// Must user pointers because mp_for_each construct object:
boost::mp11::mp_transform<std::add_pointer_t, MpGameFeaturesList>
>([&](auto* pt) {
using T = std::remove_pointer_t<decltype(pt)>;
boost::python::extract<T*> extract(pyValue);
if (extract.check()) {
(*result)[std::type_index(typeid(T))] = extract();
}
});
}
data->convertible = storage;
@@ -250,35 +261,67 @@ void registerGameFeaturesPythonConverters()
// Features require defs for all methods as Python can access C++ features
bpy::class_<BSAInvalidationWrapper, boost::noncopyable>("BSAInvalidation")
.def("isInvalidationBSA", bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA))
.def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate))
.def("activate", bpy::pure_virtual(&BSAInvalidation::activate))
.def("isInvalidationBSA", bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA), bpy::arg("name"))
.def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate), bpy::arg("profile"))
.def("activate", bpy::pure_virtual(&BSAInvalidation::activate), bpy::arg("profile"))
;
bpy::class_<DataArchivesWrapper, boost::noncopyable>("DataArchives")
.def("vanillaArchives", bpy::pure_virtual(&DataArchives::vanillaArchives))
.def("archives", bpy::pure_virtual(&DataArchives::archives))
.def("addArchive", bpy::pure_virtual(&DataArchives::addArchive))
.def("removeArchive", bpy::pure_virtual(&DataArchives::removeArchive))
.def("archives", bpy::pure_virtual(&DataArchives::archives), bpy::arg("profile"))
.def("addArchive", bpy::pure_virtual(&DataArchives::addArchive), (bpy::arg("profile"), "index", "name"))
.def("removeArchive", bpy::pure_virtual(&DataArchives::removeArchive), (bpy::arg("profile"), "name"))
;
bpy::class_<GamePluginsWrapper, boost::noncopyable>("GamePlugins")
.def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists))
.def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists))
.def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists), bpy::arg("plugin_list"))
.def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists), bpy::arg("plugin_list"))
.def("getLoadOrder", bpy::pure_virtual(&GamePlugins::getLoadOrder))
.def("lightPluginsAreSupported", bpy::pure_virtual(&GamePlugins::lightPluginsAreSupported))
;
bpy::class_<LocalSavegamesWrapper, boost::noncopyable>("LocalSavegames")
.def("mappings", bpy::pure_virtual(&LocalSavegames::mappings))
.def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile))
.def("mappings", bpy::pure_virtual(&LocalSavegames::mappings), bpy::arg("profile_save_dir"))
.def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile), bpy::arg("profile"))
;
auto modDataCheckerClass = bpy::class_<ModDataCheckerWrapper, boost::noncopyable>("ModDataChecker");
{
bpy::scope scope = modDataCheckerClass;
bpy::enum_<ModDataChecker::CheckReturn>("CheckReturn")
.value("INVALID", ModDataChecker::CheckReturn::INVALID)
.value("FIXABLE", ModDataChecker::CheckReturn::FIXABLE)
.value("VALID", ModDataChecker::CheckReturn::VALID)
.export_values()
;
modDataCheckerClass
.def("dataLooksValid", bpy::pure_virtual(&ModDataChecker::dataLooksValid), bpy::arg("filetree"))
.def("fix", bpy::pure_virtual(&ModDataChecker::fix), bpy::arg("filetree"))
;
}
{
bpy::scope scope = bpy::class_<ModDataContentWrapper, boost::noncopyable>("ModDataContent")
.def("getAllContents", bpy::pure_virtual(&ModDataContent::getAllContents))
.def("getContentsFor", bpy::pure_virtual(&ModDataContent::getContentsFor), bpy::arg("filetree"))
;
bpy::class_<ModDataContent::Content>("Content",
bpy::init<int, QString, QString, bpy::optional<bool>>((bpy::arg("id"), "name", "icon", bpy::arg("filter_only") = false)))
.add_property("id", &ModDataContent::Content::id)
.add_property("name", &ModDataContent::Content::name)
.add_property("icon", &ModDataContent::Content::icon)
.def("isOnlyForFilter", &ModDataContent::Content::isOnlyForFilter)
;
}
bpy::class_<SaveGameInfoWrapper, boost::noncopyable>("SaveGameInfo")
.def("getSaveGameInfo", bpy::pure_virtual(&SaveGameInfo::getSaveGameInfo), bpy::return_value_policy<bpy::manage_new_object>())
.def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets))
.def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy<bpy::manage_new_object>())
.def("hasScriptExtenderSave", bpy::pure_virtual(&SaveGameInfo::hasScriptExtenderSave))
.def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets), bpy::arg("save"))
.def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy<bpy::manage_new_object>(),
bpy::arg("parent"), "[optional]")
;
bpy::class_<ScriptExtenderWrapper, boost::noncopyable>("ScriptExtender")
@@ -286,16 +329,16 @@ void registerGameFeaturesPythonConverters()
.def("PluginPath", bpy::pure_virtual(&ScriptExtender::PluginPath))
.def("loaderName", bpy::pure_virtual(&ScriptExtender::loaderName))
.def("loaderPath", bpy::pure_virtual(&ScriptExtender::loaderPath))
.def("saveGameAttachmentExtensions", bpy::pure_virtual(&ScriptExtender::saveGameAttachmentExtensions))
.def("savegameExtension", bpy::pure_virtual(&ScriptExtender::savegameExtension))
.def("isInstalled", bpy::pure_virtual(&ScriptExtender::isInstalled))
.def("getExtenderVersion", bpy::pure_virtual(&ScriptExtender::getExtenderVersion))
.def("getArch", bpy::pure_virtual(&ScriptExtender::getArch))
;
bpy::class_<UnmanagedModsWrapper, boost::noncopyable>("UnmanagedMods")
.def("mods", bpy::pure_virtual(&UnmanagedMods::mods))
.def("displayName", bpy::pure_virtual(&UnmanagedMods::displayName))
.def("referenceFile", bpy::pure_virtual(&UnmanagedMods::referenceFile))
.def("secondaryFiles", bpy::pure_virtual(&UnmanagedMods::secondaryFiles))
.def("mods", bpy::pure_virtual(&UnmanagedMods::mods), bpy::arg("official_only"))
.def("displayName", bpy::pure_virtual(&UnmanagedMods::displayName), bpy::arg("mod_name"))
.def("referenceFile", bpy::pure_virtual(&UnmanagedMods::referenceFile), bpy::arg("mod_name"))
.def("secondaryFiles", bpy::pure_virtual(&UnmanagedMods::secondaryFiles), bpy::arg("mod_name"))
;
}
+47 -5
View File
@@ -1,16 +1,34 @@
#ifndef GAMEFEATURESWRAPPERS_H
#define GAMEFEATURESWRAPPERS_H
#include <map>
#include <bsainvalidation.h>
#include <dataarchives.h>
#include <gameplugins.h>
#include <localsavegames.h>
#include <moddatachecker.h>
#include <moddatacontent.h>
#include <savegameinfo.h>
#include <scriptextender.h>
#include <unmanagedmods.h>
// this might need turning off if Q_MOC_RUN is defined
#include <boost/python.hpp>
#include <boost/mp11.hpp>
// This is a simple MPL list that contains all the game features in one place:
using MpGameFeaturesList = boost::mp11::mp_list<
BSAInvalidation,
DataArchives,
GamePlugins,
LocalSavegames,
ModDataChecker,
ModDataContent,
SaveGameInfo,
ScriptExtender,
UnmanagedMods
>;
/////////////////////////////
/// Wrapper declarations
@@ -47,7 +65,7 @@ public:
virtual void writePluginLists(const MOBase::IPluginList *pluginList) override;
virtual void readPluginLists(MOBase::IPluginList *pluginList) override;
virtual void getLoadOrder(QStringList &loadOrder) override;
virtual QStringList getLoadOrder() override;
virtual bool lightPluginsAreSupported() override;
};
@@ -61,16 +79,40 @@ public:
virtual bool prepareProfile(MOBase::IProfile *profile) override;
};
class ModDataCheckerWrapper : public ModDataChecker, public boost::python::wrapper<ModDataChecker>
{
public:
static constexpr const char* className = "ModDataCheckerWrapper";
using boost::python::wrapper<ModDataChecker>::get_override;
virtual CheckReturn dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const override;
virtual std::shared_ptr<MOBase::IFileTree> fix(std::shared_ptr<MOBase::IFileTree> fileTree) const override;
};
class ModDataContentWrapper : public ModDataContent, public boost::python::wrapper<ModDataContent>
{
public:
static constexpr const char* className = "ModDataContentWrapper";
using boost::python::wrapper<ModDataContent>::get_override;
virtual std::vector<Content> getAllContents() const override;
virtual std::vector<int> getContentsFor(std::shared_ptr<const MOBase::IFileTree> fileTree) const override;
};
class SaveGameInfoWrapper : public SaveGameInfo, public boost::python::wrapper<SaveGameInfo>
{
public:
static constexpr const char* className = "SaveGameInfoWrapper";
using boost::python::wrapper<SaveGameInfo>::get_override;
virtual MOBase::ISaveGame const *getSaveGameInfo(QString const &file) const override;
virtual MissingAssets getMissingAssets(QString const &file) const override;
virtual MissingAssets getMissingAssets(MOBase::ISaveGame const& save) const override;
virtual MOBase::ISaveGameInfoWidget *getSaveGameWidget(QWidget *parent = 0) const override;
virtual bool hasScriptExtenderSave(QString const &file) const override;
private:
// We need to keep the python objects alive:
mutable std::map<QString, boost::python::object> m_SaveGames;
mutable boost::python::object m_SaveGameWidget;
};
class ScriptExtenderWrapper : public ScriptExtender, public boost::python::wrapper<ScriptExtender>
@@ -83,7 +125,7 @@ public:
virtual QString PluginPath() const override;
virtual QString loaderName() const override;
virtual QString loaderPath() const override;
virtual QStringList saveGameAttachmentExtensions() const override;
virtual QString savegameExtension() const override;
virtual bool isInstalled() const override;
virtual QString getExtenderVersion() const override;
virtual WORD getArch() const override;
+1
View File
@@ -7,5 +7,6 @@ GILock::GILock()
GILock::~GILock()
{
PyErr_Clear();
PyGILState_Release(m_State);
}
File diff suppressed because it is too large Load Diff
+79 -24
View File
@@ -2,6 +2,7 @@
#define PROXYPLUGINWRAPPERS_H
#include <iplugin.h>
#include <iplugindiagnose.h>
#include <ipluginfilemapper.h>
#include <iplugingame.h>
@@ -13,18 +14,30 @@
#ifndef Q_MOC_RUN
#include <boost/python.hpp>
#include <boost/preprocessor/control/expr_if.hpp>
#endif
#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: \
// The wrapper for IPluginGame cannot override requirements or enabledByDefault since they're final,
// so we need to be able to exclude the declarations.
#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(include_requirements) \
public: \
virtual bool init(MOBase::IOrganizer *moInfo) override; \
virtual QString name() const override; \
virtual QString localizedName() const override; \
virtual QString master() const override; \
virtual QString author() const override; \
virtual QString description() const override; \
virtual MOBase::VersionInfo version() const override; \
virtual bool isActive() const override; \
virtual QList<MOBase::PluginSetting> settings() const override;
virtual QList<MOBase::PluginSetting> settings() const override; \
QString localizedName_Default() const; \
QString master_Default() const; \
BOOST_PP_EXPR_IF(include_requirements, \
virtual std::vector<std::shared_ptr<const MOBase::IPluginRequirement>> requirements() const override; \
std::vector<std::shared_ptr<const MOBase::IPluginRequirement>> requirements_Default() const; \
virtual bool enabledByDefault() const override; \
bool enabledByDefault_Default() const;)
#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(1)
// Even though the base interface is not a QObject, this has to be because we have no way to pass Mod Organizer a plugin that implements multiple interfaces.
// QObject must be the first base class because moc assumes the first base class is a QObject
@@ -51,14 +64,14 @@ public:
static constexpr const char* className = "IPluginDiagnoseWrapper";
using boost::python::wrapper<MOBase::IPluginDiagnose>::get_override;
// Bring in public scope:
using IPluginDiagnose::invalidate;
virtual std::vector<unsigned int> activeProblems() const override;
virtual QString shortDescription(unsigned int key) const override;
virtual QString fullDescription(unsigned int key) const override;
virtual bool hasGuidedFix(unsigned int key) const override;
virtual void startGuidedFix(unsigned int key) const override;
// Other functions exist, but shouldn't need wrapping as a default implementation exists
// This was protected, but Python doesn't have that, so it needs making public
virtual void invalidate();
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
};
@@ -89,10 +102,10 @@ public:
static constexpr const char* className = "IPluginGameWrapper";
using boost::python::wrapper<MOBase::IPluginGame>::get_override;
virtual void detectGame() override;
virtual QString gameName() const override;
virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override;
virtual QString savegameExtension() const override;
virtual QString savegameSEExtension() const override;
virtual std::vector<std::shared_ptr<const MOBase::ISaveGame>> listSaves(QDir folder) const override;
virtual bool isInstalled() const override;
virtual QIcon gameIcon() const override;
virtual QDir gameDirectory() const override;
@@ -122,35 +135,63 @@ public:
virtual QString gameVersion() const override;
virtual QString getLauncherName() const override;
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(0)
protected:
// Apparently, Python developers interpret an underscore in a function name as it being protected
virtual std::map<std::type_index, boost::any> featureList() const override;
virtual std::map<std::type_index, std::any> featureList() const override;
// Thankfully, the default implementation of the templated 'T *feature()' function should allow us to get away without overriding it.
};
#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS public: \
using IPluginInstaller::parentWidget; \
using IPluginInstaller::manager; \
virtual unsigned int priority() const override; \
virtual bool isManualInstaller() const override; \
virtual void onInstallationStart(QString const& archive, bool reinstallation, MOBase::IModInterface* currentMod) override; \
void onInstallationStart_Default(QString const& archive, bool reinstallation, MOBase::IModInterface* currentMod) { \
return IPluginInstaller::onInstallationStart(archive, reinstallation, currentMod); } \
virtual void onInstallationEnd(EInstallResult result, MOBase::IModInterface* newMod) override; \
void onInstallationEnd_Default(EInstallResult result, MOBase::IModInterface* newMod) { \
return IPluginInstaller::onInstallationEnd(result, newMod); } \
virtual bool isArchiveSupported(std::shared_ptr<const MOBase::IFileTree> tree) const override;
class IPluginInstallerSimpleWrapper : public MOBase::IPluginInstallerSimple, public boost::python::wrapper<MOBase::IPluginInstallerSimple>
{
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginInstallerSimpleWrapper";
using boost::python::wrapper<MOBase::IPluginInstallerSimple>::get_override;
virtual EInstallResult install(MOBase::GuessedValue<QString>& modName, std::shared_ptr<MOBase::IFileTree>& tree,
QString& version, int& nexusID) override;
};
class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, public boost::python::wrapper<MOBase::IPluginInstallerCustom>
{
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginInstallerCustomWrapper";
using boost::python::wrapper<MOBase::IPluginInstallerCustom>::get_override;
virtual unsigned int priority() const;
virtual bool isManualInstaller() const;
virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const;
virtual bool isArchiveSupported(const QString &archiveName) const;
virtual std::set<QString> supportedExtensions() const;
virtual bool isArchiveSupported(const QString &archiveName) const override;
virtual std::set<QString> supportedExtensions() const override;
virtual EInstallResult install(MOBase::GuessedValue<QString> &modName, QString gameName, const QString &archiveName,
const QString &version, int modID);
virtual void setParentWidget(QWidget *parent);
const QString &version, int modID) override;
};
@@ -164,12 +205,19 @@ public:
static constexpr const char* className = "IPluginModPageWrapper";
using boost::python::wrapper<MOBase::IPluginModPage>::get_override;
// Bring in public scope:
using IPluginModPage::parentWidget;
virtual QString displayName() const override;
virtual QIcon icon() const override;
virtual QUrl pageURL() const override;
virtual bool useIntegratedBrowser() const override;
virtual bool handlesDownload(const QUrl &pageURL, const QUrl &downloadURL, MOBase::ModRepositoryFileInfo &fileInfo) const override;
virtual void setParentWidget(QWidget *widget) override;
void setParentWidget_Default(QWidget* parent) {
IPluginModPage::setParentWidget(parent);
}
};
@@ -198,13 +246,20 @@ public:
static constexpr const char* className = "IPluginToolWrapper";
using boost::python::wrapper<MOBase::IPluginTool>::get_override;
virtual QString displayName() const;
virtual QString tooltip() const;
virtual QIcon icon() const;
virtual void setParentWidget(QWidget *parent);
// Bring in public scope:
using IPluginTool::parentWidget;
virtual QString displayName() const override;
virtual QString tooltip() const override;
virtual QIcon icon() const override;
virtual void setParentWidget(QWidget *parent) override;
void setParentWidget_Default(QWidget* parent) {
IPluginTool::setParentWidget(parent);
}
public Q_SLOTS:
virtual void display() const;
virtual void display() const override;
};
+74
View File
@@ -0,0 +1,74 @@
#include "pylogger.h"
#include "log.h"
#include <boost/python.hpp>
namespace bpy = boost::python;
// Small structure to hold the levels - There are copy paste from
// my Python version and I assume these will not change soon:
struct PyLogLevel {
static constexpr int CRITICAL = 50;
static constexpr int ERROR = 40;
static constexpr int WARNING = 30;
static constexpr int INFO = 20;
static constexpr int DEBUG = 10;
};
// This is the function we are going to use as our Handler .emit
// method.
void emit_function(bpy::object self, bpy::object record) {
// There are other parameters that could be used, but this is minimal for
// now (filename, line number, etc.).
const int level = bpy::extract<int>(record.attr("levelno"));
const std::wstring msg = bpy::extract<std::wstring>(bpy::str(record.attr("msg")));
switch (level) {
case PyLogLevel::CRITICAL:
case PyLogLevel::ERROR:
MOBase::log::error("{}", msg);
break;
case PyLogLevel::WARNING:
MOBase::log::warn("{}", msg);
break;
case PyLogLevel::INFO:
MOBase::log::info("{}", msg);
break;
case PyLogLevel::DEBUG:
default: // There is a "NOTSET" level in theory:
MOBase::log::debug("{}", msg);
break;
}
};
void configure_python_logging(bpy::object mobase)
{
// Most of this is dealing with actual Python objects since it is not possible
// to derive from logging.Handler in C++ using Boost.Python, and since a lot of
// this would require extra register only for this.
// Retrieve the logging module and the Handler class.
auto logging = bpy::import("logging");
auto Handler = logging.attr("Handler");
// This is ugly but that's how it's done in C Python.
auto type = (PyObject*)&PyType_Type;
// Create the "MO2Handler" python class:
auto methods = bpy::dict();
methods["emit"] = bpy::make_function(emit_function);
auto MO2Handler = bpy::call<bpy::object>(type, "LogHandler", bpy::make_tuple(Handler), methods);
// Create the default logger:
auto handler = MO2Handler();
handler.attr("setLevel")(PyLogLevel::DEBUG);
auto logger = logging.attr("getLogger")(bpy::object(mobase.attr("__name__")));
logger.attr("setLevel")(PyLogLevel::DEBUG);
logger.attr("addHandler")(handler);
// Set mobase attributes:
mobase.attr("LogHandler") = MO2Handler;
mobase.attr("logger") = logger;
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef MO2_PYTHON_LOGGER_H
#define MO2_PYTHON_LOGGER_H
#include <boost/python.hpp>
/**
* @brief Configure logging for MO2 python plugin.
*
* @param mobase The mobase module.
*/
void configure_python_logging(boost::python::object mobase);
#endif
+1240 -1139
View File
File diff suppressed because it is too large Load Diff
+8 -4
View File
@@ -10,9 +10,13 @@
class IPythonRunner {
public:
virtual QList<QObject*> instantiate(const QString &pluginName) = 0;
virtual bool isPythonInstalled() const = 0;
virtual bool isPythonVersionSupported() const = 0;
virtual QList<QObject*> load(const QString& identifier) = 0;
virtual void unload(const QString& identifier) = 0;
virtual bool isPythonInitialized() const = 0;
virtual ~IPythonRunner() { }
};
@@ -22,7 +26,7 @@ public:
#define PYDLLEXPORT Q_DECL_IMPORT
#endif // PYTHONRUNNER_LIBRARY
extern "C" PYDLLEXPORT IPythonRunner *CreatePythonRunner(MOBase::IOrganizer *moInfo, const QString &pythonDir);
extern "C" PYDLLEXPORT IPythonRunner *CreatePythonRunner();

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