mirror of
https://github.com/ModOrganizer2/modorganizer-plugin_python.git
synced 2026-07-27 14:03:33 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c54a45f63 | ||
|
|
b090d1341b | ||
|
|
f3b43a5375 | ||
|
|
0423ccb9ca | ||
|
|
d4d958018d | ||
|
|
b1c51ada08 | ||
|
|
5f56aa99ca | ||
|
|
0c5622ea69 | ||
|
|
071498f305 | ||
|
|
f134881da6 | ||
|
|
931996347b | ||
|
|
4775bafc33 | ||
|
|
f5a8446230 | ||
|
|
120c13170c | ||
|
|
ae975e52d7 | ||
|
|
e5ac21442f | ||
|
|
40e6924714 | ||
|
|
e4ff66b727 | ||
|
|
a9a3d49efa | ||
|
|
fce4f39dc8 | ||
|
|
57441bd3c5 | ||
|
|
bab25d25ca | ||
|
|
ca052389b5 | ||
|
|
5f86de5d9e | ||
|
|
91c55c909f | ||
|
|
d2d31f993d | ||
|
|
56dbb1f40e | ||
|
|
cde7f79bc3 | ||
|
|
96a7bc3898 | ||
|
|
f4ae1c94a0 | ||
|
|
87c91b8259 | ||
|
|
2ed4b901c0 | ||
|
|
cd9b16dcc2 | ||
|
|
5cad8a9ff8 | ||
|
|
e961d21f24 | ||
|
|
7f0848c9d2 | ||
|
|
bc05ec6477 | ||
|
|
631fc2b31b | ||
|
|
f17ce3d2e6 | ||
|
|
73d7771703 | ||
|
|
e737f0a5a8 | ||
|
|
a75e94d46f | ||
|
|
b7855ca7d8 | ||
|
|
f6cf0e8b33 | ||
|
|
4fdbabdf76 | ||
|
|
efc7d76a0a | ||
|
|
832aced4d3 | ||
|
|
a335adf928 | ||
|
|
ccef4be741 | ||
|
|
aacf3654d8 | ||
|
|
234586823d | ||
|
|
5b84588732 | ||
|
|
7dbc139b10 | ||
|
|
31e4f724ed | ||
|
|
6e088406c8 | ||
|
|
9784881921 | ||
|
|
aa8caee65c | ||
|
|
9a10c01cc0 | ||
|
|
d9243bb617 | ||
|
|
adf5888faf | ||
|
|
7bec9237e6 | ||
|
|
0846ffd177 | ||
|
|
f3601e5bfe | ||
|
|
b37d3a9c17 | ||
|
|
cbb911f6a0 | ||
|
|
27c34af2aa | ||
|
|
b20c015643 | ||
|
|
d89eae1646 | ||
|
|
de5fde0f28 | ||
|
|
e84777d550 | ||
|
|
1ca6fee64d | ||
|
|
57c774103d | ||
|
|
ead8352c93 | ||
|
|
04e1af60a0 | ||
|
|
ebb566f773 | ||
|
|
c45bfb1e35 | ||
|
|
5f8e1803c0 | ||
|
|
9bd22cfceb | ||
|
|
c2c63e0624 | ||
|
|
240622565c | ||
|
|
ba7ba616ea | ||
|
|
93dcf8d5b3 | ||
|
|
7d0e97ed2b | ||
|
|
86297b1170 | ||
|
|
78e4e5c548 | ||
|
|
6a73c23a4b | ||
|
|
7afade8642 | ||
|
|
6445f7f47f | ||
|
|
c6e89dead6 | ||
|
|
20c2e5a14d | ||
|
|
83dec58e4a | ||
|
|
f4b8cbcd0b | ||
|
|
96d15ae762 | ||
|
|
7668783d3c | ||
|
|
40b7cd8e86 | ||
|
|
2aa65ec5bb | ||
|
|
b1e554526a | ||
|
|
8640890f8c | ||
|
|
2526b75ca1 | ||
|
|
138de736ef | ||
|
|
9e4cfaa0f0 | ||
|
|
8b703f2e4f | ||
|
|
984a307a04 | ||
|
|
2c30cd25cd | ||
|
|
9ba91b1ffa | ||
|
|
d53e8de91c | ||
|
|
702dc6c66f | ||
|
|
9c9d1ee574 | ||
|
|
7a83a472b8 | ||
|
|
e4b367e909 | ||
|
|
eb5232c1eb | ||
|
|
eb033c3e9e | ||
|
|
28704a8a2e | ||
|
|
5a15a374bf | ||
|
|
f4478dcf66 | ||
|
|
df663f6e4f | ||
|
|
bb522b9126 | ||
|
|
d549a99867 | ||
|
|
44553b4621 | ||
|
|
1d51b3c457 | ||
|
|
2c0f1006cf | ||
|
|
58d5100dfc | ||
|
|
e545aec65a | ||
|
|
f10194a4bb | ||
|
|
afe671d484 | ||
|
|
bfb85087a0 | ||
|
|
b4ae13314c | ||
|
|
c5be2c3605 | ||
|
|
0f33ee2176 | ||
|
|
1d2fa7f269 | ||
|
|
c0828f2553 | ||
|
|
847e73a0db | ||
|
|
1d111cdd8f | ||
|
|
5b06bb75a9 | ||
|
|
04a54c7566 | ||
|
|
b96e006e7d | ||
|
|
bfa5c28285 | ||
|
|
cf6679292f | ||
|
|
65e73aa537 | ||
|
|
194bba9a9b | ||
|
|
64fd574c3b | ||
|
|
93fee5bd6c | ||
|
|
accb270353 | ||
|
|
d8657946e9 | ||
|
|
3780f157e4 | ||
|
|
7a9cd34878 | ||
|
|
829ac65cea | ||
|
|
5c34d11e79 | ||
|
|
6001d88f40 | ||
|
|
139666705a | ||
|
|
fed261e9eb | ||
|
|
f531905e4a | ||
|
|
e25fa00285 | ||
|
|
5c63d62a1e | ||
|
|
624c5257bd | ||
|
|
480eb3f30e | ||
|
|
4909b6cff0 | ||
|
|
331416d1e7 | ||
|
|
c955b4e8de | ||
|
|
727a55e1a8 | ||
|
|
6c1f961d83 | ||
|
|
a3e9916400 | ||
|
|
0fbb88b3c1 | ||
|
|
5023a13aa2 | ||
|
|
fe8a0b849c | ||
|
|
e1a1b70121 | ||
|
|
671776f306 | ||
|
|
95ed8ae14a | ||
|
|
e6ca79c694 | ||
|
|
c521178769 | ||
|
|
1cbfde96a0 | ||
|
|
e7ae48f633 | ||
|
|
d0fd776648 | ||
|
|
cbdf8fee14 | ||
|
|
5fe9c44f26 | ||
|
|
3f02f975aa | ||
|
|
b2b0b6f5c5 | ||
|
|
6ee58c6bf9 | ||
|
|
2efc6d4af6 | ||
|
|
760cca172f | ||
|
|
26c0e628b8 | ||
|
|
ab4a067042 | ||
|
|
38188e0b17 | ||
|
|
863893cc31 | ||
|
|
06b11c86ba | ||
|
|
d4a6a57f14 | ||
|
|
2ea33f4faa | ||
|
|
b4c23ad733 | ||
|
|
71db9d81d4 | ||
|
|
e712448ca0 | ||
|
|
98b8d92350 | ||
|
|
7cc0f974f5 | ||
|
|
48c35a8f92 | ||
|
|
4b9fc2d32a | ||
|
|
be63c1d9de | ||
|
|
100a2d03f0 | ||
|
|
fcd1177acd | ||
|
|
5eeb02403f | ||
|
|
eca495c849 | ||
|
|
024a333194 | ||
|
|
ead37d1dbe | ||
|
|
65003ef4ad | ||
|
|
a3b593f714 | ||
|
|
ab4168fd93 | ||
|
|
bfad575467 | ||
|
|
d5f44f424d | ||
|
|
da679b1d6d | ||
|
|
751e67e86c | ||
|
|
c96fe2d21a | ||
|
|
4b4c36df7a | ||
|
|
5f9d2b296d | ||
|
|
05e79cc13b | ||
|
|
3f5aa49dee | ||
|
|
3e9209a191 | ||
|
|
d70942efa1 | ||
|
|
d9393a2d2b | ||
|
|
7c1389d479 | ||
|
|
7333bde006 | ||
|
|
fae9f1b52f | ||
|
|
23c6f3fad9 | ||
|
|
f12bb05d61 | ||
|
|
d931d71a43 | ||
|
|
aa75c3b18e | ||
|
|
eb29202b2f | ||
|
|
fe0a75b819 | ||
|
|
90a99b37c9 | ||
|
|
7c474c58ca | ||
|
|
288f9f9a95 | ||
|
|
a05006a0b3 | ||
|
|
d6b8c66911 | ||
|
|
0c52c2417c | ||
|
|
24205bba66 | ||
|
|
f0f949c4ef | ||
|
|
6b88cee9d8 | ||
|
|
1825c58666 | ||
|
|
83a89ca595 | ||
|
|
e9fdfd40aa | ||
|
|
fcf1aa8b42 | ||
|
|
d404a37fed | ||
|
|
b360cae555 | ||
|
|
039e33f5fa | ||
|
|
622d4246a7 | ||
|
|
2f1646a2e6 | ||
|
|
716cbcf733 | ||
|
|
62f10c03ff | ||
|
|
957b786d8c | ||
|
|
29a5eb8366 | ||
|
|
4209d90533 | ||
|
|
7bbf05bc0c | ||
|
|
154c643bff | ||
|
|
2146cc9207 | ||
|
|
163ed213ef | ||
|
|
6919539d35 | ||
|
|
917b8dfb4b | ||
|
|
54e447c0a9 | ||
|
|
206454c34e | ||
|
|
457529546b | ||
|
|
b9de35772f | ||
|
|
4589cab093 | ||
|
|
2b4eedb62b | ||
|
|
36022774e3 | ||
|
|
aac45a39e2 | ||
|
|
61ad2ef2b6 | ||
|
|
1ea006b74e | ||
|
|
af37a451f9 | ||
|
|
473cef6217 | ||
|
|
5cd7917709 | ||
|
|
585296d527 | ||
|
|
221949f48b | ||
|
|
8b118ad898 | ||
|
|
f83ed39287 | ||
|
|
8a286f9cfc | ||
|
|
70172ddda6 | ||
|
|
817f8d7238 | ||
|
|
9f5dab285d | ||
|
|
53b91f0092 | ||
|
|
f1d5a1f31b | ||
|
|
626a7bc670 | ||
|
|
c9115e7ad0 | ||
|
|
6cececc7a0 | ||
|
|
d82640455f | ||
|
|
f25602cc69 | ||
|
|
168b9d7b0d | ||
|
|
247944baa8 | ||
|
|
332b8239bb | ||
|
|
97a93a5989 | ||
|
|
e871617843 | ||
|
|
c5d4857f47 | ||
|
|
5825661a4f | ||
|
|
2e13cf0dab | ||
|
|
d6dd0342b7 |
+12
-22
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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->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
@@ -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
@@ -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->Plugins->ProxyPython->plugin_dir is set incorrectly or it is empty and auto-detection doesn't work for whatever reason.
|
||||
Do you want to try initializing python again (at the risk of another crash)?
|
||||
Suggestion: Select "no", 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-&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.</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.<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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxy/proxypython.cpp" line="309"/>
|
||||
<source>Please set python_dir in Settings->Plugins->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'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.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxy/proxypython.cpp" line="316"/>
|
||||
<source>Sorry, I don'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. <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.</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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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"))
|
||||
;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,5 +7,6 @@ GILock::GILock()
|
||||
|
||||
GILock::~GILock()
|
||||
{
|
||||
PyErr_Clear();
|
||||
PyGILState_Release(m_State);
|
||||
}
|
||||
|
||||
+164
-117
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
Reference in New Issue
Block a user