mirror of
https://github.com/UltimMC/Launcher.git
synced 2025-12-13 20:22:13 +00:00
Compare commits
6 Commits
feature/op
...
0.5.2-fina
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b85d7c0de | ||
|
|
1eb6e77f75 | ||
|
|
f49f8b875a | ||
|
|
23d2a99619 | ||
|
|
170bd677fd | ||
|
|
84e23e2e7a |
@@ -1,4 +1,4 @@
|
|||||||
UseTab: false
|
UseTab: true
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
TabWidth: 4
|
TabWidth: 4
|
||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE.md
vendored
1
.github/ISSUE_TEMPLATE.md
vendored
@@ -9,7 +9,6 @@ Before submitting this issue, please make sure you have:
|
|||||||
- We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored.
|
- We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored.
|
||||||
4. Given the issue a descriptive title.
|
4. Given the issue a descriptive title.
|
||||||
- A good title includes a brief summary of the issue and avoids things such as "Help" and "What?!".
|
- A good title includes a brief summary of the issue and avoids things such as "Help" and "What?!".
|
||||||
Use of UPPERCASE is discouraged, as it reads like someone is screaming.
|
|
||||||
5. Place all information below the ---- of lines.
|
5. Place all information below the ---- of lines.
|
||||||
- It makes the issue look pretty
|
- It makes the issue look pretty
|
||||||
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,8 +15,6 @@ CMakeLists.txt.user
|
|||||||
CMakeLists.txt.user.*
|
CMakeLists.txt.user.*
|
||||||
/.project
|
/.project
|
||||||
/.settings
|
/.settings
|
||||||
/.idea
|
|
||||||
cmake-build-*/
|
|
||||||
|
|
||||||
# Build dirs
|
# Build dirs
|
||||||
build
|
build
|
||||||
|
|||||||
41
.travis.yml
41
.travis.yml
@@ -2,28 +2,29 @@
|
|||||||
language: cpp
|
language: cpp
|
||||||
cache: apt
|
cache: apt
|
||||||
|
|
||||||
|
# Build matrix set up
|
||||||
|
compiler:
|
||||||
|
- gcc
|
||||||
|
# - clang
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
# - osx
|
||||||
|
env:
|
||||||
|
- QT_VERSION=5.4.2
|
||||||
|
- QT_VERSION=5.5.1
|
||||||
|
# - QT_VERSION=5.6.2
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
exclude:
|
||||||
- os: linux
|
# only use clang on OS X
|
||||||
dist: precise
|
- os: osx
|
||||||
sudo: required
|
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
env: TRAVIS_DIST=precise QT_VERSION=5.4.2
|
# only use the qt available from homebrew
|
||||||
- os: linux
|
- os: osx
|
||||||
dist: precise
|
env: QT_VERSION=5.4.2
|
||||||
sudo: required
|
- os: osx
|
||||||
compiler: gcc
|
env: QT_VERSION=5.5.1
|
||||||
env: TRAVIS_DIST=precise QT_VERSION=5.6.2
|
# - os: osx
|
||||||
- os: linux
|
# env: QT_VERSION=5.6
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
compiler: gcc
|
|
||||||
env: TRAVIS_DIST=trusty QT_VERSION=5.4.2
|
|
||||||
- os: linux
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
compiler: gcc
|
|
||||||
env: TRAVIS_DIST=trusty QT_VERSION=5.6.2
|
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
install:
|
install:
|
||||||
|
|||||||
20
BUILD.md
20
BUILD.md
@@ -32,11 +32,11 @@ git submodule update
|
|||||||
Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution.
|
Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution.
|
||||||
|
|
||||||
## Build dependencies
|
## Build dependencies
|
||||||
* A C++ compiler capable of building C++11 code.
|
* Ideally a compiler capable of building C++14 code (for example, GCC 5.2 and above).
|
||||||
* Qt 5.6+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager. It is always better to use the Qt from your distribution, as long as it has a new enough version.
|
* Qt 5.4.1+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager. It is always better to use the Qt from your distribution.
|
||||||
* cmake 3.1 or newer
|
* cmake 3.1 or newer
|
||||||
* zlib (for example, `zlib1g-dev`)
|
* zlib (for example, `zlib1g-dev`)
|
||||||
* Java JDK 8 (for example, `openjdk-8-jdk`)
|
* java (for example, `openjdk-8-jdk`)
|
||||||
* GL headers (for example, `libgl1-mesa-dev`)
|
* GL headers (for example, `libgl1-mesa-dev`)
|
||||||
|
|
||||||
### Building from command line
|
### Building from command line
|
||||||
@@ -64,7 +64,7 @@ You can use IDEs like KDevelop or QtCreator to open the CMake project if you wan
|
|||||||
1. Run the Qt installer.
|
1. Run the Qt installer.
|
||||||
2. Choose a place to install Qt.
|
2. Choose a place to install Qt.
|
||||||
3. Choose the components you want to install.
|
3. Choose the components you want to install.
|
||||||
- You need Qt 5.6.x 64-bit ticked.
|
- You need Qt 5.4.1/gcc 64-bit ticked.
|
||||||
- You need Tools/Qt Creator ticked.
|
- You need Tools/Qt Creator ticked.
|
||||||
- Other components are selected by default, you can untick them if you don't need them.
|
- Other components are selected by default, you can untick them if you don't need them.
|
||||||
4. Accept the license agreements.
|
4. Accept the license agreements.
|
||||||
@@ -77,7 +77,7 @@ You can use IDEs like KDevelop or QtCreator to open the CMake project if you wan
|
|||||||
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt.
|
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt.
|
||||||
4. Read the instructions that just popped up about a build location and choose one.
|
4. Read the instructions that just popped up about a build location and choose one.
|
||||||
5. You should see "Run CMake" in the window.
|
5. You should see "Run CMake" in the window.
|
||||||
- Make sure that Generator is set to "Unix Generator (Desktop Qt 5.6.x GCC 64bit)".
|
- Make sure that Generator is set to "Unix Generator (Desktop Qt 5.4.1 GCC 64bit)".
|
||||||
- Hit the "Run CMake" button.
|
- Hit the "Run CMake" button.
|
||||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||||
- Hit "Finish" if CMake ran successfully.
|
- Hit "Finish" if CMake ran successfully.
|
||||||
@@ -91,13 +91,13 @@ You can use IDEs like KDevelop or QtCreator to open the CMake project if you wan
|
|||||||
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using Microsoft build tools, because that's not something we do. If it does compile, it is by chance only.
|
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using Microsoft build tools, because that's not something we do. If it does compile, it is by chance only.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
* [Qt 5.6+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
|
* [Qt 5.4.1+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
|
||||||
* [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) -- Newest Win32 OpenSSL Light
|
* [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) -- Newest Win32 OpenSSL Light
|
||||||
- Microsoft Visual C++ 2008 Redist is required for this, there's a link on the OpenSSL download page above next to the main download.
|
- Microsoft Visual C++ 2008 Redist is required for this, there's a link on the OpenSSL download page above next to the main download.
|
||||||
- We use a custom build of OpenSSL that doesn't have this dependency. For normal development, the custom build is not necessary though.
|
- We use a custom build of OpenSSL that doesn't have this dependency. For normal development, the custom build is not necessary though.
|
||||||
* [zlib 1.2+](http://gnuwin32.sourceforge.net/packages/zlib.htm) - the Setup is fine
|
* [zlib 1.2.8+](http://zlib.net/zlib128-dll.zip)
|
||||||
* [Java JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
|
|
||||||
* [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
|
* [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
|
||||||
|
* [patch.exe from the GnuWin project](http://gnuwin32.sourceforge.net/packages/patch.htm).
|
||||||
|
|
||||||
Put it somewhere on the `PATH`, so that it is accessible from the console.
|
Put it somewhere on the `PATH`, so that it is accessible from the console.
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
|||||||
1. Run the Qt installer
|
1. Run the Qt installer
|
||||||
2. Choose a place to install Qt (C:\Qt is the default),
|
2. Choose a place to install Qt (C:\Qt is the default),
|
||||||
3. Choose the components you want to install
|
3. Choose the components you want to install
|
||||||
- You need Qt 5.6 (32 bit) ticked,
|
- You need Qt 5.4.1/MinGW 4.9 (32 bit) ticked,
|
||||||
- You need Tools/Qt Creator ticked,
|
- You need Tools/Qt Creator ticked,
|
||||||
- Other components are selected by default, you can untick them if you don't need them.
|
- Other components are selected by default, you can untick them if you don't need them.
|
||||||
4. Accept the license agreements,
|
4. Accept the license agreements,
|
||||||
@@ -132,7 +132,7 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
|||||||
5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
|
5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
|
||||||
- Otherwise you can skip this step.
|
- Otherwise you can skip this step.
|
||||||
6. You should see "Run CMake" in the window,
|
6. You should see "Run CMake" in the window,
|
||||||
- Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.6.x MinGW 32bit)",
|
- Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.4.1 MinGW 32bit)",
|
||||||
- Hit the "Run CMake" button,
|
- Hit the "Run CMake" button,
|
||||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||||
- Hit "Finish" if CMake ran successfully.
|
- Hit "Finish" if CMake ran successfully.
|
||||||
|
|||||||
186
CMakeLists.txt
186
CMakeLists.txt
@@ -13,7 +13,7 @@ endif()
|
|||||||
project(MultiMC)
|
project(MultiMC)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
##################################### Set CMake options #####################################
|
######## Set CMake options ########
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
||||||
@@ -32,61 +32,22 @@ set(CMAKE_C_STANDARD_REQUIRED true)
|
|||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
|
set(CMAKE_CXX_FLAGS " -Wall -D_GLIBCXX_USE_CXX11_ABI=0 ${CMAKE_CXX_FLAGS}")
|
||||||
if(UNIX AND APPLE)
|
if(UNIX AND APPLE)
|
||||||
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
|
||||||
|
|
||||||
##################################### Set Application options #####################################
|
|
||||||
|
|
||||||
######## Set URLs ########
|
|
||||||
set(MultiMC_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
|
|
||||||
|
|
||||||
######## Set version numbers ########
|
|
||||||
set(MultiMC_VERSION_MAJOR 0)
|
|
||||||
set(MultiMC_VERSION_MINOR 6)
|
|
||||||
set(MultiMC_VERSION_HOTFIX 6)
|
|
||||||
|
|
||||||
# Build number
|
|
||||||
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
|
||||||
|
|
||||||
# Build platform.
|
|
||||||
set(MultiMC_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
|
|
||||||
|
|
||||||
# Channel list URL
|
|
||||||
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
|
|
||||||
|
|
||||||
# Notification URL
|
|
||||||
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
|
||||||
|
|
||||||
# paste.ee API key
|
|
||||||
set(MultiMC_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
|
|
||||||
|
|
||||||
# Google analytics ID
|
|
||||||
set(MultiMC_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
|
|
||||||
|
|
||||||
#### Check the current Git commit and branch
|
|
||||||
include(GetGitRevisionDescription)
|
|
||||||
get_git_head_revision(MultiMC_GIT_REFSPEC MultiMC_GIT_COMMIT)
|
|
||||||
|
|
||||||
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
|
|
||||||
message(STATUS "Git refspec: ${MultiMC_GIT_REFSPEC}")
|
|
||||||
|
|
||||||
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}")
|
|
||||||
|
|
||||||
#### Custom target to just print the version.
|
|
||||||
add_custom_target(version echo "Version: ${MultiMC_RELEASE_VERSION_NAME}")
|
|
||||||
|
|
||||||
################################ 3rd Party Libs ################################
|
################################ 3rd Party Libs ################################
|
||||||
|
|
||||||
# Find the required Qt parts
|
# Find the required Qt parts
|
||||||
find_package(Qt5Core REQUIRED)
|
find_package(Qt5Core)
|
||||||
find_package(Qt5Widgets REQUIRED)
|
find_package(Qt5Widgets)
|
||||||
find_package(Qt5Concurrent REQUIRED)
|
find_package(Qt5Concurrent)
|
||||||
find_package(Qt5Network REQUIRED)
|
find_package(Qt5Network)
|
||||||
find_package(Qt5Test REQUIRED)
|
find_package(Qt5Test)
|
||||||
find_package(Qt5Xml REQUIRED)
|
find_package(Qt5Xml)
|
||||||
|
|
||||||
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
||||||
include(QMakeQuery)
|
include(QMakeQuery)
|
||||||
@@ -101,133 +62,6 @@ if (Qt5_POSITION_INDEPENDENT_CODE)
|
|||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
####################################### Install layout #######################################
|
|
||||||
|
|
||||||
# How to install the build results
|
|
||||||
set(MultiMC_LAYOUT "auto" CACHE STRING "The layout for MultiMC installation (auto, win-bundle, lin-bundle, lin-nodeps, lin-system, mac-bundle)")
|
|
||||||
set_property(CACHE MultiMC_LAYOUT PROPERTY STRINGS auto win-bundle lin-bundle lin-nodeps lin-system mac-bundle)
|
|
||||||
|
|
||||||
if(MultiMC_LAYOUT STREQUAL "auto")
|
|
||||||
if(UNIX AND APPLE)
|
|
||||||
set(MultiMC_LAYOUT_REAL "mac-bundle")
|
|
||||||
elseif(UNIX)
|
|
||||||
set(MultiMC_LAYOUT_REAL "lin-nodeps")
|
|
||||||
elseif(WIN32)
|
|
||||||
set(MultiMC_LAYOUT_REAL "win-bundle")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(MultiMC_LAYOUT_REAL ${MultiMC_LAYOUT})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MultiMC_LAYOUT_REAL STREQUAL "mac-bundle")
|
|
||||||
set(BINARY_DEST_DIR "MultiMC.app/Contents/MacOS")
|
|
||||||
set(LIBRARY_DEST_DIR "MultiMC.app/Contents/MacOS")
|
|
||||||
set(PLUGIN_DEST_DIR "MultiMC.app/Contents/MacOS")
|
|
||||||
set(RESOURCES_DEST_DIR "MultiMC.app/Contents/Resources")
|
|
||||||
set(JARS_DEST_DIR "MultiMC.app/Contents/MacOS/jars")
|
|
||||||
|
|
||||||
set(BUNDLE_DEST_DIR ".")
|
|
||||||
|
|
||||||
# Apps to bundle
|
|
||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
|
|
||||||
|
|
||||||
# Mac bundle settings
|
|
||||||
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
|
|
||||||
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
|
|
||||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
|
|
||||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
|
||||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
|
||||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
|
||||||
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
|
|
||||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2019 MultiMC Contributors")
|
|
||||||
|
|
||||||
# directories to look for dependencies
|
|
||||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
|
||||||
|
|
||||||
# install as bundle
|
|
||||||
set(INSTALL_BUNDLE "full")
|
|
||||||
|
|
||||||
# Add the icon
|
|
||||||
install(FILES application/resources/MultiMC.icns DESTINATION ${RESOURCES_DEST_DIR})
|
|
||||||
|
|
||||||
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-bundle")
|
|
||||||
set(BINARY_DEST_DIR "bin")
|
|
||||||
set(LIBRARY_DEST_DIR "bin")
|
|
||||||
set(PLUGIN_DEST_DIR "plugins")
|
|
||||||
set(BUNDLE_DEST_DIR ".")
|
|
||||||
set(RESOURCES_DEST_DIR ".")
|
|
||||||
set(JARS_DEST_DIR "bin/jars")
|
|
||||||
|
|
||||||
# Apps to bundle
|
|
||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
|
|
||||||
|
|
||||||
# directories to look for dependencies
|
|
||||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
|
||||||
|
|
||||||
# install as bundle
|
|
||||||
set(INSTALL_BUNDLE "full")
|
|
||||||
|
|
||||||
# Set RPATH
|
|
||||||
SET(MultiMC_BINARY_RPATH "$ORIGIN/")
|
|
||||||
|
|
||||||
# Install basic runner script
|
|
||||||
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
|
|
||||||
|
|
||||||
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-nodeps")
|
|
||||||
set(BINARY_DEST_DIR "bin")
|
|
||||||
set(LIBRARY_DEST_DIR "bin")
|
|
||||||
set(PLUGIN_DEST_DIR "plugins")
|
|
||||||
set(BUNDLE_DEST_DIR ".")
|
|
||||||
set(RESOURCES_DEST_DIR ".")
|
|
||||||
set(JARS_DEST_DIR "bin/jars")
|
|
||||||
|
|
||||||
# install as bundle with no dependencies included
|
|
||||||
set(INSTALL_BUNDLE "nodeps")
|
|
||||||
|
|
||||||
# Set RPATH
|
|
||||||
SET(MultiMC_BINARY_RPATH "$ORIGIN/")
|
|
||||||
|
|
||||||
# Install basic runner script
|
|
||||||
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
|
|
||||||
|
|
||||||
elseif(MultiMC_LAYOUT_REAL STREQUAL "lin-system")
|
|
||||||
set(MultiMC_APP_BINARY_NAME "multimc" CACHE STRING "Name of the MultiMC binary")
|
|
||||||
set(MultiMC_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
|
|
||||||
set(MultiMC_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
|
|
||||||
set(MultiMC_SHARE_DEST_DIR "share/multimc" CACHE STRING "Path to the shared data directory")
|
|
||||||
set(JARS_DEST_DIR "${MultiMC_SHARE_DEST_DIR}/jars")
|
|
||||||
|
|
||||||
set(BINARY_DEST_DIR ${MultiMC_BINARY_DEST_DIR})
|
|
||||||
set(LIBRARY_DEST_DIR ${MultiMC_LIBRARY_DEST_DIR})
|
|
||||||
|
|
||||||
MESSAGE(STATUS "Compiling for linux system with ${MultiMC_SHARE_DEST_DIR} and MULTIMC_LINUX_DATADIR")
|
|
||||||
SET(MultiMC_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DMULTIMC_LINUX_DATADIR")
|
|
||||||
|
|
||||||
# install as bundle with no dependencies included
|
|
||||||
set(INSTALL_BUNDLE "nodeps")
|
|
||||||
|
|
||||||
elseif(MultiMC_LAYOUT_REAL STREQUAL "win-bundle")
|
|
||||||
set(BINARY_DEST_DIR ".")
|
|
||||||
set(LIBRARY_DEST_DIR ".")
|
|
||||||
set(PLUGIN_DEST_DIR ".")
|
|
||||||
set(BUNDLE_DEST_DIR ".")
|
|
||||||
set(RESOURCES_DEST_DIR ".")
|
|
||||||
set(JARS_DEST_DIR "jars")
|
|
||||||
|
|
||||||
# Apps to bundle
|
|
||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
|
|
||||||
|
|
||||||
# directories to look for dependencies
|
|
||||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
|
||||||
|
|
||||||
# install as bundle
|
|
||||||
set(INSTALL_BUNDLE "full")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "No sensible install layout set.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
################################ Included Libs ################################
|
################################ Included Libs ################################
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
@@ -237,7 +71,6 @@ option(NBT_BUILD_SHARED "Build NBT shared library" ON)
|
|||||||
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
|
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
|
||||||
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
|
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
|
||||||
set(NBT_NAME MultiMC_nbt++)
|
set(NBT_NAME MultiMC_nbt++)
|
||||||
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
|
|
||||||
add_subdirectory(libraries/libnbtplusplus)
|
add_subdirectory(libraries/libnbtplusplus)
|
||||||
|
|
||||||
add_subdirectory(libraries/ganalytics) # google analytics library
|
add_subdirectory(libraries/ganalytics) # google analytics library
|
||||||
@@ -251,12 +84,11 @@ add_subdirectory(libraries/pack200) # java pack200 compression
|
|||||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||||
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
||||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||||
add_subdirectory(libraries/classparser) # google analytics library
|
|
||||||
|
|
||||||
############################### Built Artifacts ###############################
|
############################### Built Artifacts ###############################
|
||||||
|
|
||||||
add_subdirectory(api/logic)
|
add_subdirectory(api/logic)
|
||||||
add_subdirectory(api/gui)
|
add_subdirectory(api/gui)
|
||||||
|
|
||||||
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
|
||||||
add_subdirectory(application)
|
add_subdirectory(application)
|
||||||
|
add_subdirectory(wonkoclient)
|
||||||
|
|||||||
95
COPYING.md
95
COPYING.md
@@ -1,6 +1,6 @@
|
|||||||
#MultiMC
|
#MultiMC
|
||||||
|
|
||||||
Copyright 2012-2019 MultiMC Contributors
|
Copyright 2012-2017 MultiMC Contributors
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
@@ -60,6 +60,26 @@
|
|||||||
You should have received a copy of the GNU Lesser General Public License
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#Group View
|
||||||
|
|
||||||
|
Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
|
||||||
|
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public License
|
||||||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#rainbow (KGuiAddons)
|
#rainbow (KGuiAddons)
|
||||||
|
|
||||||
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
||||||
@@ -117,17 +137,6 @@
|
|||||||
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
||||||
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
# Material Design Icons
|
|
||||||
|
|
||||||
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
|
|
||||||
with Reserved Font Name Material Design Icons.
|
|
||||||
Copyright (c) 2014, Google (http://www.google.com/design/)
|
|
||||||
uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
#Pack200
|
#Pack200
|
||||||
|
|
||||||
The GNU General Public License (GPL)
|
The GNU General Public License (GPL)
|
||||||
@@ -192,63 +201,5 @@
|
|||||||
|
|
||||||
#ColumnResizer
|
#ColumnResizer
|
||||||
|
|
||||||
Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
|
Copyright 2011 Aurélien Gâteau <agateau@kde.org>
|
||||||
|
License: LGPL v2.1 or later (see COPYING)
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted (subject to the limitations in the
|
|
||||||
disclaimer below) provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
* The name of the contributors may not be used to endorse or
|
|
||||||
promote products derived from this software without specific prior
|
|
||||||
written permission.
|
|
||||||
|
|
||||||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
|
||||||
GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
|
||||||
HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
||||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
||||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
||||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
||||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
# lionshead
|
|
||||||
|
|
||||||
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
|
||||||
translated to C++ laced with Qt.
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2017 Nate Coraor
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -1,6 +1,4 @@
|
|||||||
<p align="center">
|

|
||||||
<img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
MultiMC 5
|
MultiMC 5
|
||||||
=========
|
=========
|
||||||
@@ -13,7 +11,7 @@ The project uses C++ and Qt5 as the language and base framework. This might seem
|
|||||||
|
|
||||||
We can do more, with less, on worse hardware and leave more resources for the game while keeping the launcher running and providing extra features.
|
We can do more, with less, on worse hardware and leave more resources for the game while keeping the launcher running and providing extra features.
|
||||||
|
|
||||||
If you want to contribute, either talk to us on [Discord](https://discord.gg/0k2zsXGNHs0fE4Wm), [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC) or pick up some item from [workflowy](https://workflowy.com/s/2EyDMcp7CU) - there are many.
|
If you want to contribute, either talk to us on [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC), or pick up one of the issues that are ready for development: [](http://waffle.io/MultiMC/MultiMC5)
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||||
@@ -27,7 +25,9 @@ We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the
|
|||||||
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
Translations can be done either directly in the [translations repository](https://github.com/MultiMC/MultiMC5). For more details, see: [Translating-MultiMC](https://github.com/MultiMC/MultiMC5/wiki/Translating-MultiMC).
|
Translations can be done either directly in the [translations repository](https://github.com/MultiMC/MultiMC5) or using our [translation server](http://translate.multimc.org). For more details, see: [Translating-MultiMC](https://github.com/MultiMC/MultiMC5/wiki/Translating-MultiMC).
|
||||||
|
|
||||||
|
Currently, MultiMC is [](http://translate.multimc.org/engage/multimc/?utm_source=widget)
|
||||||
|
|
||||||
## Forking/Redistributing
|
## Forking/Redistributing
|
||||||
We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
|
We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
|
||||||
@@ -38,7 +38,7 @@ Apache covers reasonable use for the name - a mention of the project's origins i
|
|||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright © 2013-2019 MultiMC Contributors
|
Copyright © 2013-2017 MultiMC Contributors
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
|
||||||
|
|||||||
@@ -21,14 +21,8 @@ set_target_properties(MultiMC_gui PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBI
|
|||||||
generate_export_header(MultiMC_gui)
|
generate_export_header(MultiMC_gui)
|
||||||
|
|
||||||
# Link
|
# Link
|
||||||
target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic Qt5::Gui)
|
target_link_libraries(MultiMC_gui iconfix MultiMC_logic)
|
||||||
|
qt5_use_modules(MultiMC_gui Gui)
|
||||||
|
|
||||||
# Mark and export headers
|
# Mark and export headers
|
||||||
target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
# Install it
|
|
||||||
install(
|
|
||||||
TARGETS MultiMC_gui
|
|
||||||
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
|
|
||||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
|
|
||||||
)
|
|
||||||
@@ -34,4 +34,4 @@ namespace DesktopServices
|
|||||||
* Open the URL, most likely in a browser. Maybe.
|
* Open the URL, most likely in a browser. Maybe.
|
||||||
*/
|
*/
|
||||||
MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
|
MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -187,7 +187,8 @@ Qt::DropActions IconList::supportedDropActions() const
|
|||||||
return Qt::CopyAction;
|
return Qt::CopyAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
|
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
|
||||||
|
const QModelIndex &parent)
|
||||||
{
|
{
|
||||||
if (action == Qt::IgnoreAction)
|
if (action == Qt::IgnoreAction)
|
||||||
return true;
|
return true;
|
||||||
@@ -260,7 +261,7 @@ void IconList::installIcons(const QStringList &iconFiles)
|
|||||||
QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName());
|
QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName());
|
||||||
|
|
||||||
QString suffix = fileinfo.suffix();
|
QString suffix = fileinfo.suffix();
|
||||||
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico" && suffix != "svg" && suffix != "gif")
|
if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!QFile::copy(file, target))
|
if (!QFile::copy(file, target))
|
||||||
@@ -268,17 +269,6 @@ void IconList::installIcons(const QStringList &iconFiles)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IconList::installIcon(const QString &file, const QString &name)
|
|
||||||
{
|
|
||||||
QFileInfo fileinfo(file);
|
|
||||||
if(!fileinfo.isReadable() || !fileinfo.isFile())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString target = FS::PathCombine(m_dir.dirName(), name);
|
|
||||||
|
|
||||||
QFile::copy(file, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IconList::iconFileExists(const QString &key) const
|
bool IconList::iconFileExists(const QString &key) const
|
||||||
{
|
{
|
||||||
auto iconEntry = icon(key);
|
auto iconEntry = icon(key);
|
||||||
@@ -341,7 +331,7 @@ bool IconList::addIcon(const QString &key, const QString &name, const QString &p
|
|||||||
{
|
{
|
||||||
// replace the icon even? is the input valid?
|
// replace the icon even? is the input valid?
|
||||||
QIcon icon(path);
|
QIcon icon(path);
|
||||||
if (icon.isNull())
|
if (!icon.availableSizes().size())
|
||||||
return false;
|
return false;
|
||||||
auto iter = name_index.find(key);
|
auto iter = name_index.find(key);
|
||||||
if (iter != name_index.end())
|
if (iter != name_index.end())
|
||||||
@@ -402,6 +392,20 @@ QIcon IconList::getIcon(const QString &key) const
|
|||||||
return QIcon();
|
return QIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QIcon IconList::getBigIcon(const QString &key) const
|
||||||
|
{
|
||||||
|
int icon_index = getIconIndex(key);
|
||||||
|
|
||||||
|
// Fallback for icons that don't exist.
|
||||||
|
icon_index = getIconIndex(icon_index == -1 ? "infinity" : key);
|
||||||
|
|
||||||
|
if (icon_index == -1)
|
||||||
|
return QIcon();
|
||||||
|
|
||||||
|
QPixmap bigone = icons[icon_index].icon().pixmap(256,256).scaled(256,256);
|
||||||
|
return QIcon(bigone);
|
||||||
|
}
|
||||||
|
|
||||||
int IconList::getIconIndex(const QString &key) const
|
int IconList::getIconIndex(const QString &key) const
|
||||||
{
|
{
|
||||||
auto iter = name_index.find(key == "default" ? "infinity" : key);
|
auto iter = name_index.find(key == "default" ? "infinity" : key);
|
||||||
@@ -411,9 +415,4 @@ int IconList::getIconIndex(const QString &key) const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString IconList::getDirectory() const
|
|
||||||
{
|
|
||||||
return m_dir.absolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
//#include "IconList.moc"
|
//#include "IconList.moc"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -38,8 +38,8 @@ public:
|
|||||||
virtual ~IconList() {};
|
virtual ~IconList() {};
|
||||||
|
|
||||||
QIcon getIcon(const QString &key) const;
|
QIcon getIcon(const QString &key) const;
|
||||||
|
QIcon getBigIcon(const QString &key) const;
|
||||||
int getIconIndex(const QString &key) const;
|
int getIconIndex(const QString &key) const;
|
||||||
QString getDirectory() const;
|
|
||||||
|
|
||||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
@@ -56,7 +56,6 @@ public:
|
|||||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
|
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
|
||||||
void installIcons(const QStringList &iconFiles) override;
|
void installIcons(const QStringList &iconFiles) override;
|
||||||
void installIcon(const QString &file, const QString &name) override;
|
|
||||||
|
|
||||||
const MMCIcon * icon(const QString &key) const;
|
const MMCIcon * icon(const QString &key) const;
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ protected slots:
|
|||||||
void fileChanged(const QString &path);
|
void fileChanged(const QString &path);
|
||||||
void SettingChanged(const Setting & setting, QVariant value);
|
void SettingChanged(const Setting & setting, QVariant value);
|
||||||
private:
|
private:
|
||||||
shared_qobject_ptr<QFileSystemWatcher> m_watcher;
|
std::shared_ptr<QFileSystemWatcher> m_watcher;
|
||||||
bool is_watching;
|
bool is_watching;
|
||||||
QMap<QString, int> name_index;
|
QMap<QString, int> name_index;
|
||||||
QVector<MMCIcon> icons;
|
QVector<MMCIcon> icons;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -102,17 +102,3 @@ void MMCIcon::replace(IconType new_type, const QString& key)
|
|||||||
m_images[new_type].filename = QString();
|
m_images[new_type].filename = QString();
|
||||||
m_images[new_type].key = key;
|
m_images[new_type].key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MMCIcon::getFilePath() const
|
|
||||||
{
|
|
||||||
if(m_current_type == IconType::ToBeDeleted){
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return m_images[m_current_type].filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool MMCIcon::isBuiltIn() const
|
|
||||||
{
|
|
||||||
return m_current_type == IconType::Builtin;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -46,6 +46,4 @@ struct MULTIMC_GUI_EXPORT MMCIcon
|
|||||||
void remove(IconType rm_type);
|
void remove(IconType rm_type);
|
||||||
void replace(IconType new_type, QIcon icon, QString path = QString());
|
void replace(IconType new_type, QIcon icon, QString path = QString());
|
||||||
void replace(IconType new_type, const QString &key);
|
void replace(IconType new_type, const QString &key);
|
||||||
bool isBuiltIn() const;
|
|
||||||
QString getFilePath() const;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,19 +16,19 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include "BaseInstaller.h"
|
#include "BaseInstaller.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/onesix/OneSixInstance.h"
|
||||||
|
|
||||||
BaseInstaller::BaseInstaller()
|
BaseInstaller::BaseInstaller()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstaller::isApplied(MinecraftInstance *on)
|
bool BaseInstaller::isApplied(OneSixInstance *on)
|
||||||
{
|
{
|
||||||
return QFile::exists(filename(on->instanceRoot()));
|
return QFile::exists(filename(on->instanceRoot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstaller::add(MinecraftInstance *to)
|
bool BaseInstaller::add(OneSixInstance *to)
|
||||||
{
|
{
|
||||||
if (!patchesDir(to->instanceRoot()).exists())
|
if (!patchesDir(to->instanceRoot()).exists())
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@ bool BaseInstaller::add(MinecraftInstance *to)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstaller::remove(MinecraftInstance *from)
|
bool BaseInstaller::remove(OneSixInstance *from)
|
||||||
{
|
{
|
||||||
return QFile::remove(filename(from->instanceRoot()));
|
return QFile::remove(filename(from->instanceRoot()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
class MinecraftInstance;
|
class OneSixInstance;
|
||||||
class QDir;
|
class QDir;
|
||||||
class QString;
|
class QString;
|
||||||
class QObject;
|
class QObject;
|
||||||
@@ -32,12 +32,12 @@ class MULTIMC_LOGIC_EXPORT BaseInstaller
|
|||||||
public:
|
public:
|
||||||
BaseInstaller();
|
BaseInstaller();
|
||||||
virtual ~BaseInstaller(){};
|
virtual ~BaseInstaller(){};
|
||||||
bool isApplied(MinecraftInstance *on);
|
bool isApplied(OneSixInstance *on);
|
||||||
|
|
||||||
virtual bool add(MinecraftInstance *to);
|
virtual bool add(OneSixInstance *to);
|
||||||
virtual bool remove(MinecraftInstance *from);
|
virtual bool remove(OneSixInstance *from);
|
||||||
|
|
||||||
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
|
virtual Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QString id() const = 0;
|
virtual QString id() const = 0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "settings/Setting.h"
|
#include "settings/Setting.h"
|
||||||
#include "settings/OverrideSetting.h"
|
#include "settings/OverrideSetting.h"
|
||||||
|
|
||||||
|
#include "minecraft/MinecraftVersionList.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "Commandline.h"
|
#include "Commandline.h"
|
||||||
|
|
||||||
@@ -102,6 +103,13 @@ void BaseInstance::invalidate()
|
|||||||
qDebug() << "Instance" << id() << "has been invalidated.";
|
qDebug() << "Instance" << id() << "has been invalidated.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseInstance::nuke()
|
||||||
|
{
|
||||||
|
changeStatus(Status::Gone);
|
||||||
|
qDebug() << "Instance" << id() << "has been deleted by MultiMC.";
|
||||||
|
FS::deletePath(instanceRoot());
|
||||||
|
}
|
||||||
|
|
||||||
void BaseInstance::changeStatus(BaseInstance::Status newStatus)
|
void BaseInstance::changeStatus(BaseInstance::Status newStatus)
|
||||||
{
|
{
|
||||||
Status status = currentStatus();
|
Status status = currentStatus();
|
||||||
@@ -175,6 +183,11 @@ QString BaseInstance::instanceRoot() const
|
|||||||
return m_rootDir;
|
return m_rootDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstancePtr BaseInstance::getSharedPtr()
|
||||||
|
{
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
SettingsObjectPtr BaseInstance::settings() const
|
SettingsObjectPtr BaseInstance::settings() const
|
||||||
{
|
{
|
||||||
return m_settings;
|
return m_settings;
|
||||||
@@ -185,7 +198,7 @@ bool BaseInstance::canLaunch() const
|
|||||||
return (!hasVersionBroken() && !isRunning());
|
return (!hasVersionBroken() && !isRunning());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstance::reloadSettings()
|
bool BaseInstance::reload()
|
||||||
{
|
{
|
||||||
return m_settings->reload();
|
return m_settings->reload();
|
||||||
}
|
}
|
||||||
@@ -202,6 +215,31 @@ void BaseInstance::setLastLaunch(qint64 val)
|
|||||||
emit propertiesChanged(this);
|
emit propertiesChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseInstance::setGroupInitial(QString val)
|
||||||
|
{
|
||||||
|
if(m_group == val)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_group = val;
|
||||||
|
emit propertiesChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::setGroupPost(QString val)
|
||||||
|
{
|
||||||
|
if(m_group == val)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setGroupInitial(val);
|
||||||
|
emit groupChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseInstance::group() const
|
||||||
|
{
|
||||||
|
return m_group;
|
||||||
|
}
|
||||||
|
|
||||||
void BaseInstance::setNotes(QString val)
|
void BaseInstance::setNotes(QString val)
|
||||||
{
|
{
|
||||||
//FIXME: if no change, do not set. setting involves saving a file.
|
//FIXME: if no change, do not set. setting involves saving a file.
|
||||||
@@ -239,16 +277,31 @@ QString BaseInstance::name() const
|
|||||||
|
|
||||||
QString BaseInstance::windowTitle() const
|
QString BaseInstance::windowTitle() const
|
||||||
{
|
{
|
||||||
return "MultiMC: " + name().replace(QRegExp("[ \n\r\t]+"), " ");
|
return "MultiMC: " + name();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
|
||||||
QStringList BaseInstance::extraArguments() const
|
QStringList BaseInstance::extraArguments() const
|
||||||
{
|
{
|
||||||
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
|
std::shared_ptr<LaunchTask> BaseInstance::getLaunchTask()
|
||||||
{
|
{
|
||||||
return m_launchProcess;
|
return m_launchProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseInstance::setProvider(BaseInstanceProvider* provider)
|
||||||
|
{
|
||||||
|
// only once.
|
||||||
|
assert(!m_provider);
|
||||||
|
if(m_provider)
|
||||||
|
{
|
||||||
|
qWarning() << "Provider set more than once for instance" << id();
|
||||||
|
}
|
||||||
|
m_provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseInstanceProvider* BaseInstance::provider() const
|
||||||
|
{
|
||||||
|
return m_provider;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -30,14 +30,13 @@
|
|||||||
#include "MessageLevel.h"
|
#include "MessageLevel.h"
|
||||||
#include "pathmatcher/IPathMatcher.h"
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
|
||||||
#include "net/Mode.h"
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
class QDir;
|
class QDir;
|
||||||
class Task;
|
class Task;
|
||||||
class LaunchTask;
|
class LaunchTask;
|
||||||
class BaseInstance;
|
class BaseInstance;
|
||||||
|
class BaseInstanceProvider;
|
||||||
|
|
||||||
// pointer for lazy people
|
// pointer for lazy people
|
||||||
typedef std::shared_ptr<BaseInstance> InstancePtr;
|
typedef std::shared_ptr<BaseInstance> InstancePtr;
|
||||||
@@ -68,7 +67,13 @@ public:
|
|||||||
/// virtual destructor to make sure the destruction is COMPLETE
|
/// virtual destructor to make sure the destruction is COMPLETE
|
||||||
virtual ~BaseInstance() {};
|
virtual ~BaseInstance() {};
|
||||||
|
|
||||||
virtual void saveNow() = 0;
|
virtual void copy(SettingsObjectPtr newSettings, const QDir &newDir) {}
|
||||||
|
|
||||||
|
virtual void init() = 0;
|
||||||
|
|
||||||
|
/// nuke thoroughly - deletes the instance contents, notifies the list/model which is
|
||||||
|
/// responsible of cleaning up the husk
|
||||||
|
void nuke();
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
|
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
|
||||||
@@ -87,18 +92,15 @@ public:
|
|||||||
int64_t totalTimePlayed() const;
|
int64_t totalTimePlayed() const;
|
||||||
void resetTimePlayed();
|
void resetTimePlayed();
|
||||||
|
|
||||||
|
void setProvider(BaseInstanceProvider * provider);
|
||||||
|
BaseInstanceProvider * provider() const;
|
||||||
|
|
||||||
/// get the type of this instance
|
/// get the type of this instance
|
||||||
QString instanceType() const;
|
QString instanceType() const;
|
||||||
|
|
||||||
/// Path to the instance's root directory.
|
/// Path to the instance's root directory.
|
||||||
QString instanceRoot() const;
|
QString instanceRoot() const;
|
||||||
|
|
||||||
/// Path to the instance's game root directory.
|
|
||||||
virtual QString gameRoot() const
|
|
||||||
{
|
|
||||||
return instanceRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString name() const;
|
QString name() const;
|
||||||
void setName(QString val);
|
void setName(QString val);
|
||||||
|
|
||||||
@@ -111,6 +113,10 @@ public:
|
|||||||
QString notes() const;
|
QString notes() const;
|
||||||
void setNotes(QString val);
|
void setNotes(QString val);
|
||||||
|
|
||||||
|
QString group() const;
|
||||||
|
void setGroupInitial(QString val);
|
||||||
|
void setGroupPost(QString val);
|
||||||
|
|
||||||
QString getPreLaunchCommand();
|
QString getPreLaunchCommand();
|
||||||
QString getPostExitCommand();
|
QString getPostExitCommand();
|
||||||
QString getWrapperCommand();
|
QString getWrapperCommand();
|
||||||
@@ -123,8 +129,25 @@ public:
|
|||||||
|
|
||||||
virtual QStringList extraArguments() const;
|
virtual QStringList extraArguments() const;
|
||||||
|
|
||||||
|
virtual QString intendedVersionId() const = 0;
|
||||||
|
virtual bool setIntendedVersionId(QString version) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The instance's current version.
|
||||||
|
* This value represents the instance's current version. If this value is
|
||||||
|
* different from the intendedVersion, the instance should be updated.
|
||||||
|
* \warning Don't change this value unless you know what you're doing.
|
||||||
|
*/
|
||||||
|
virtual QString currentVersionId() const = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Whether or not 'the game' should be downloaded when the instance is launched.
|
||||||
|
*/
|
||||||
|
virtual bool shouldUpdate() const = 0;
|
||||||
|
virtual void setShouldUpdate(bool val) = 0;
|
||||||
|
|
||||||
/// Traits. Normally inside the version, depends on instance implementation.
|
/// Traits. Normally inside the version, depends on instance implementation.
|
||||||
virtual QSet <QString> traits() const = 0;
|
virtual QSet <QString> traits() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the time that the instance was last launched.
|
* Gets the time that the instance was last launched.
|
||||||
@@ -134,6 +157,14 @@ public:
|
|||||||
/// Sets the last launched time to 'val' milliseconds since epoch
|
/// Sets the last launched time to 'val' milliseconds since epoch
|
||||||
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
|
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
|
||||||
|
|
||||||
|
InstancePtr getSharedPtr();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets a pointer to this instance's version list.
|
||||||
|
* \return A pointer to the available version list for this instance.
|
||||||
|
*/
|
||||||
|
virtual std::shared_ptr<BaseVersionList> versionList() const = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets this instance's settings object.
|
* \brief Gets this instance's settings object.
|
||||||
* This settings object stores instance-specific settings.
|
* This settings object stores instance-specific settings.
|
||||||
@@ -142,13 +173,19 @@ public:
|
|||||||
virtual SettingsObjectPtr settings() const;
|
virtual SettingsObjectPtr settings() const;
|
||||||
|
|
||||||
/// returns a valid update task
|
/// returns a valid update task
|
||||||
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
|
virtual shared_qobject_ptr<Task> createUpdateTask() = 0;
|
||||||
|
|
||||||
/// returns a valid launcher (task container)
|
/// returns a valid launcher (task container)
|
||||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
|
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
|
||||||
|
|
||||||
/// returns the current launch task (if any)
|
/// returns the current launch task (if any)
|
||||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
std::shared_ptr<LaunchTask> getLaunchTask();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns a task that should be done right before launch
|
||||||
|
* This task should do any extra preparations needed
|
||||||
|
*/
|
||||||
|
virtual std::shared_ptr<Task> createJarModdingTask() = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Create envrironment variables for running the instance
|
* Create envrironment variables for running the instance
|
||||||
@@ -214,11 +251,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool canLaunch() const;
|
bool canLaunch() const;
|
||||||
virtual bool canEdit() const = 0;
|
|
||||||
virtual bool canExport() const = 0;
|
virtual bool canExport() const = 0;
|
||||||
|
|
||||||
bool reloadSettings();
|
virtual bool reload();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 'print' a verbose desription of the instance into a QStringList
|
* 'print' a verbose desription of the instance into a QStringList
|
||||||
@@ -238,8 +274,12 @@ signals:
|
|||||||
* \brief Signal emitted when properties relevant to the instance view change
|
* \brief Signal emitted when properties relevant to the instance view change
|
||||||
*/
|
*/
|
||||||
void propertiesChanged(BaseInstance *inst);
|
void propertiesChanged(BaseInstance *inst);
|
||||||
|
/*!
|
||||||
|
* \brief Signal emitted when groups are affected in any way
|
||||||
|
*/
|
||||||
|
void groupChanged();
|
||||||
|
|
||||||
void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
|
void launchTaskChanged(std::shared_ptr<LaunchTask>);
|
||||||
|
|
||||||
void runningStatusChanged(bool running);
|
void runningStatusChanged(bool running);
|
||||||
|
|
||||||
@@ -250,11 +290,13 @@ protected slots:
|
|||||||
|
|
||||||
protected: /* data */
|
protected: /* data */
|
||||||
QString m_rootDir;
|
QString m_rootDir;
|
||||||
|
QString m_group;
|
||||||
SettingsObjectPtr m_settings;
|
SettingsObjectPtr m_settings;
|
||||||
// InstanceFlags m_flags;
|
// InstanceFlags m_flags;
|
||||||
bool m_isRunning = false;
|
bool m_isRunning = false;
|
||||||
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
std::shared_ptr<LaunchTask> m_launchProcess;
|
||||||
QDateTime m_timeStarted;
|
QDateTime m_timeStarted;
|
||||||
|
BaseInstanceProvider * m_provider = nullptr;
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
Status m_status = Status::Present;
|
Status m_status = Status::Present;
|
||||||
@@ -263,6 +305,6 @@ private: /* data */
|
|||||||
bool m_hasBrokenVersion = false;
|
bool m_hasBrokenVersion = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
Q_DECLARE_METATYPE(std::shared_ptr<BaseInstance>)
|
||||||
//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
|
//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
|
||||||
//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
|
//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
|
||||||
|
|||||||
57
api/logic/BaseInstanceProvider.h
Normal file
57
api/logic/BaseInstanceProvider.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include "BaseInstance.h"
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
|
|
||||||
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
|
using InstanceId = QString;
|
||||||
|
using InstanceLocator = std::pair<InstancePtr, int>;
|
||||||
|
|
||||||
|
enum class InstCreateError
|
||||||
|
{
|
||||||
|
NoCreateError = 0,
|
||||||
|
NoSuchVersion,
|
||||||
|
UnknownCreateError,
|
||||||
|
InstExists,
|
||||||
|
CantCreateDir
|
||||||
|
};
|
||||||
|
|
||||||
|
class MULTIMC_LOGIC_EXPORT BaseInstanceProvider : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
BaseInstanceProvider(SettingsObjectPtr settings) : m_globalSettings(settings)
|
||||||
|
{
|
||||||
|
// nil
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
virtual QList<InstanceId> discoverInstances() = 0;
|
||||||
|
virtual InstancePtr loadInstance(const InstanceId &id) = 0;
|
||||||
|
virtual void loadGroupList() = 0;
|
||||||
|
virtual void saveGroupList() = 0;
|
||||||
|
|
||||||
|
virtual QString getStagedInstancePath()
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
virtual bool commitStagedInstance(const QString & keyPath, const QString & path, const QString& instanceName, const QString & groupName)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual bool destroyStagingPath(const QString & path)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Emit this when the list of provided instances changed
|
||||||
|
void instancesChanged();
|
||||||
|
// Emit when the set of groups your provider supplies changes.
|
||||||
|
void groupsChanged(QSet<QString> groups);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SettingsObjectPtr m_globalSettings;
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -30,7 +30,7 @@ BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
|
|||||||
return BaseVersionPtr();
|
return BaseVersionPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVersionPtr BaseVersionList::getRecommended() const
|
BaseVersionPtr BaseVersionList::getLatestStable() const
|
||||||
{
|
{
|
||||||
if (count() <= 0)
|
if (count() <= 0)
|
||||||
return BaseVersionPtr();
|
return BaseVersionPtr();
|
||||||
@@ -38,6 +38,11 @@ BaseVersionPtr BaseVersionList::getRecommended() const
|
|||||||
return at(0);
|
return at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BaseVersionPtr BaseVersionList::getRecommended() const
|
||||||
|
{
|
||||||
|
return getLatestStable();
|
||||||
|
}
|
||||||
|
|
||||||
QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
@@ -88,7 +93,7 @@ QHash<int, QByteArray> BaseVersionList::roleNames() const
|
|||||||
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
|
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
|
||||||
roles.insert(VersionRole, "version");
|
roles.insert(VersionRole, "version");
|
||||||
roles.insert(VersionIdRole, "versionId");
|
roles.insert(VersionIdRole, "versionId");
|
||||||
roles.insert(ParentVersionRole, "parentGameVersion");
|
roles.insert(ParentGameVersionRole, "parentGameVersion");
|
||||||
roles.insert(RecommendedRole, "recommended");
|
roles.insert(RecommendedRole, "recommended");
|
||||||
roles.insert(LatestRole, "latest");
|
roles.insert(LatestRole, "latest");
|
||||||
roles.insert(TypeRole, "type");
|
roles.insert(TypeRole, "type");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Class that each instance type's version list derives from.
|
* \brief Class that each instance type's version list derives from.
|
||||||
@@ -45,7 +44,7 @@ public:
|
|||||||
VersionPointerRole = Qt::UserRole,
|
VersionPointerRole = Qt::UserRole,
|
||||||
VersionRole,
|
VersionRole,
|
||||||
VersionIdRole,
|
VersionIdRole,
|
||||||
ParentVersionRole,
|
ParentGameVersionRole,
|
||||||
RecommendedRole,
|
RecommendedRole,
|
||||||
LatestRole,
|
LatestRole,
|
||||||
TypeRole,
|
TypeRole,
|
||||||
@@ -64,7 +63,7 @@ public:
|
|||||||
* The task returned by this function should reset the model when it's done.
|
* The task returned by this function should reset the model when it's done.
|
||||||
* \return A pointer to a task that reloads the version list.
|
* \return A pointer to a task that reloads the version list.
|
||||||
*/
|
*/
|
||||||
virtual shared_qobject_ptr<Task> getLoadTask() = 0;
|
virtual Task *getLoadTask() = 0;
|
||||||
|
|
||||||
//! Checks whether or not the list is loaded. If this returns false, the list should be
|
//! Checks whether or not the list is loaded. If this returns false, the list should be
|
||||||
//loaded.
|
//loaded.
|
||||||
@@ -77,22 +76,27 @@ public:
|
|||||||
virtual int count() const = 0;
|
virtual int count() const = 0;
|
||||||
|
|
||||||
//////// List Model Functions ////////
|
//////// List Model Functions ////////
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
virtual QVariant data(const QModelIndex &index, int role) const;
|
||||||
int rowCount(const QModelIndex &parent) const override;
|
virtual int rowCount(const QModelIndex &parent) const;
|
||||||
int columnCount(const QModelIndex &parent) const override;
|
virtual int columnCount(const QModelIndex &parent) const;
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
virtual QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
//! which roles are provided by this version list?
|
//! which roles are provided by this version list?
|
||||||
virtual RoleList providesRoles() const;
|
virtual RoleList providesRoles() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Finds a version by its descriptor.
|
* \brief Finds a version by its descriptor.
|
||||||
* \param descriptor The descriptor of the version to find.
|
* \param The descriptor of the version to find.
|
||||||
* \return A const pointer to the version with the given descriptor. NULL if
|
* \return A const pointer to the version with the given descriptor. NULL if
|
||||||
* one doesn't exist.
|
* one doesn't exist.
|
||||||
*/
|
*/
|
||||||
virtual BaseVersionPtr findVersion(const QString &descriptor);
|
virtual BaseVersionPtr findVersion(const QString &descriptor);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Gets the latest stable version from this list
|
||||||
|
*/
|
||||||
|
virtual BaseVersionPtr getLatestStable() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets the recommended version from this list
|
* \brief Gets the recommended version from this list
|
||||||
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
||||||
|
|||||||
@@ -8,14 +8,21 @@ set(CORE_SOURCES
|
|||||||
BaseInstaller.cpp
|
BaseInstaller.cpp
|
||||||
BaseVersionList.h
|
BaseVersionList.h
|
||||||
BaseVersionList.cpp
|
BaseVersionList.cpp
|
||||||
|
InstanceCreationTask.h
|
||||||
|
InstanceCreationTask.cpp
|
||||||
|
InstanceCopyTask.h
|
||||||
|
InstanceCopyTask.cpp
|
||||||
|
InstanceImportTask.h
|
||||||
|
InstanceImportTask.cpp
|
||||||
InstanceList.h
|
InstanceList.h
|
||||||
InstanceList.cpp
|
InstanceList.cpp
|
||||||
InstanceTask.h
|
|
||||||
InstanceTask.cpp
|
|
||||||
LoggedProcess.h
|
LoggedProcess.h
|
||||||
LoggedProcess.cpp
|
LoggedProcess.cpp
|
||||||
MessageLevel.cpp
|
MessageLevel.cpp
|
||||||
MessageLevel.h
|
MessageLevel.h
|
||||||
|
BaseInstanceProvider.h
|
||||||
|
FolderInstanceProvider.h
|
||||||
|
FolderInstanceProvider.cpp
|
||||||
BaseVersion.h
|
BaseVersion.h
|
||||||
BaseInstance.h
|
BaseInstance.h
|
||||||
BaseInstance.cpp
|
BaseInstance.cpp
|
||||||
@@ -25,14 +32,6 @@ set(CORE_SOURCES
|
|||||||
MMCStrings.h
|
MMCStrings.h
|
||||||
MMCStrings.cpp
|
MMCStrings.cpp
|
||||||
|
|
||||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
|
||||||
InstanceCreationTask.h
|
|
||||||
InstanceCreationTask.cpp
|
|
||||||
InstanceCopyTask.h
|
|
||||||
InstanceCopyTask.cpp
|
|
||||||
InstanceImportTask.h
|
|
||||||
InstanceImportTask.cpp
|
|
||||||
|
|
||||||
# Use tracking separate from memory management
|
# Use tracking separate from memory management
|
||||||
Usable.h
|
Usable.h
|
||||||
|
|
||||||
@@ -43,10 +42,6 @@ set(CORE_SOURCES
|
|||||||
Env.h
|
Env.h
|
||||||
Env.cpp
|
Env.cpp
|
||||||
|
|
||||||
# String filters
|
|
||||||
Filter.h
|
|
||||||
Filter.cpp
|
|
||||||
|
|
||||||
# JSON parsing helpers
|
# JSON parsing helpers
|
||||||
Json.h
|
Json.h
|
||||||
Json.cpp
|
Json.cpp
|
||||||
@@ -182,11 +177,9 @@ set(NEWS_SOURCES
|
|||||||
|
|
||||||
# Icon interface
|
# Icon interface
|
||||||
set(ICONS_SOURCES
|
set(ICONS_SOURCES
|
||||||
# Icons System and related code
|
# News System
|
||||||
icons/IIconList.h
|
icons/IIconList.h
|
||||||
icons/IIconList.cpp
|
icons/IIconList.cpp
|
||||||
icons/IconUtils.h
|
|
||||||
icons/IconUtils.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Minecraft services status checker
|
# Minecraft services status checker
|
||||||
@@ -213,16 +206,22 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/auth/flows/RefreshTask.cpp
|
minecraft/auth/flows/RefreshTask.cpp
|
||||||
minecraft/auth/flows/ValidateTask.h
|
minecraft/auth/flows/ValidateTask.h
|
||||||
minecraft/auth/flows/ValidateTask.cpp
|
minecraft/auth/flows/ValidateTask.cpp
|
||||||
minecraft/gameoptions/GameOptions.h
|
minecraft/onesix/OneSixUpdate.h
|
||||||
minecraft/gameoptions/GameOptions.cpp
|
minecraft/onesix/OneSixUpdate.cpp
|
||||||
minecraft/update/AssetUpdateTask.h
|
minecraft/onesix/OneSixInstance.h
|
||||||
minecraft/update/AssetUpdateTask.cpp
|
minecraft/onesix/OneSixInstance.cpp
|
||||||
minecraft/update/FMLLibrariesTask.cpp
|
minecraft/onesix/OneSixProfileStrategy.cpp
|
||||||
minecraft/update/FMLLibrariesTask.h
|
minecraft/onesix/OneSixProfileStrategy.h
|
||||||
minecraft/update/FoldersTask.cpp
|
minecraft/onesix/OneSixVersionFormat.cpp
|
||||||
minecraft/update/FoldersTask.h
|
minecraft/onesix/OneSixVersionFormat.h
|
||||||
minecraft/update/LibrariesTask.cpp
|
minecraft/onesix/update/AssetUpdateTask.h
|
||||||
minecraft/update/LibrariesTask.h
|
minecraft/onesix/update/AssetUpdateTask.cpp
|
||||||
|
minecraft/onesix/update/FMLLibrariesTask.cpp
|
||||||
|
minecraft/onesix/update/FMLLibrariesTask.h
|
||||||
|
minecraft/onesix/update/FoldersTask.cpp
|
||||||
|
minecraft/onesix/update/FoldersTask.h
|
||||||
|
minecraft/onesix/update/LibrariesTask.cpp
|
||||||
|
minecraft/onesix/update/LibrariesTask.h
|
||||||
minecraft/launch/ClaimAccount.cpp
|
minecraft/launch/ClaimAccount.cpp
|
||||||
minecraft/launch/ClaimAccount.h
|
minecraft/launch/ClaimAccount.h
|
||||||
minecraft/launch/CreateServerResourcePacksFolder.cpp
|
minecraft/launch/CreateServerResourcePacksFolder.cpp
|
||||||
@@ -237,66 +236,86 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/launch/LauncherPartLaunch.h
|
minecraft/launch/LauncherPartLaunch.h
|
||||||
minecraft/launch/PrintInstanceInfo.cpp
|
minecraft/launch/PrintInstanceInfo.cpp
|
||||||
minecraft/launch/PrintInstanceInfo.h
|
minecraft/launch/PrintInstanceInfo.h
|
||||||
minecraft/launch/ReconstructAssets.cpp
|
|
||||||
minecraft/launch/ReconstructAssets.h
|
|
||||||
minecraft/legacy/LegacyModList.h
|
minecraft/legacy/LegacyModList.h
|
||||||
minecraft/legacy/LegacyModList.cpp
|
minecraft/legacy/LegacyModList.cpp
|
||||||
|
minecraft/legacy/LegacyUpdate.h
|
||||||
|
minecraft/legacy/LegacyUpdate.cpp
|
||||||
minecraft/legacy/LegacyInstance.h
|
minecraft/legacy/LegacyInstance.h
|
||||||
minecraft/legacy/LegacyInstance.cpp
|
minecraft/legacy/LegacyInstance.cpp
|
||||||
minecraft/legacy/LegacyUpgradeTask.h
|
minecraft/legacy/LwjglVersionList.h
|
||||||
minecraft/legacy/LegacyUpgradeTask.cpp
|
minecraft/legacy/LwjglVersionList.cpp
|
||||||
minecraft/GradleSpecifier.h
|
minecraft/GradleSpecifier.h
|
||||||
minecraft/MinecraftInstance.cpp
|
minecraft/MinecraftProfile.cpp
|
||||||
minecraft/MinecraftInstance.h
|
minecraft/MinecraftProfile.h
|
||||||
minecraft/LaunchProfile.cpp
|
|
||||||
minecraft/LaunchProfile.h
|
|
||||||
minecraft/Component.cpp
|
|
||||||
minecraft/Component.h
|
|
||||||
minecraft/ComponentList.cpp
|
|
||||||
minecraft/ComponentList.h
|
|
||||||
minecraft/ComponentUpdateTask.cpp
|
|
||||||
minecraft/ComponentUpdateTask.h
|
|
||||||
minecraft/MinecraftLoadAndCheck.h
|
|
||||||
minecraft/MinecraftLoadAndCheck.cpp
|
|
||||||
minecraft/MinecraftUpdate.h
|
|
||||||
minecraft/MinecraftUpdate.cpp
|
|
||||||
minecraft/MojangVersionFormat.cpp
|
minecraft/MojangVersionFormat.cpp
|
||||||
minecraft/MojangVersionFormat.h
|
minecraft/MojangVersionFormat.h
|
||||||
|
minecraft/JarMod.h
|
||||||
|
minecraft/MinecraftInstance.cpp
|
||||||
|
minecraft/MinecraftInstance.h
|
||||||
|
minecraft/MinecraftVersion.cpp
|
||||||
|
minecraft/MinecraftVersion.h
|
||||||
|
minecraft/MinecraftVersionList.cpp
|
||||||
|
minecraft/MinecraftVersionList.h
|
||||||
minecraft/Rule.cpp
|
minecraft/Rule.cpp
|
||||||
minecraft/Rule.h
|
minecraft/Rule.h
|
||||||
minecraft/OneSixVersionFormat.cpp
|
|
||||||
minecraft/OneSixVersionFormat.h
|
|
||||||
minecraft/OpSys.cpp
|
minecraft/OpSys.cpp
|
||||||
minecraft/OpSys.h
|
minecraft/OpSys.h
|
||||||
minecraft/ParseUtils.cpp
|
minecraft/ParseUtils.cpp
|
||||||
minecraft/ParseUtils.h
|
minecraft/ParseUtils.h
|
||||||
minecraft/ProfileUtils.cpp
|
minecraft/ProfileUtils.cpp
|
||||||
minecraft/ProfileUtils.h
|
minecraft/ProfileUtils.h
|
||||||
|
minecraft/ProfileStrategy.h
|
||||||
minecraft/Library.cpp
|
minecraft/Library.cpp
|
||||||
minecraft/Library.h
|
minecraft/Library.h
|
||||||
minecraft/MojangDownloadInfo.h
|
minecraft/MojangDownloadInfo.h
|
||||||
|
minecraft/VersionBuildError.h
|
||||||
minecraft/VersionFile.cpp
|
minecraft/VersionFile.cpp
|
||||||
minecraft/VersionFile.h
|
minecraft/VersionFile.h
|
||||||
|
minecraft/ProfilePatch.h
|
||||||
minecraft/VersionFilterData.h
|
minecraft/VersionFilterData.h
|
||||||
minecraft/VersionFilterData.cpp
|
minecraft/VersionFilterData.cpp
|
||||||
minecraft/Mod.h
|
minecraft/Mod.h
|
||||||
minecraft/Mod.cpp
|
minecraft/Mod.cpp
|
||||||
minecraft/SimpleModList.h
|
minecraft/ModList.h
|
||||||
minecraft/SimpleModList.cpp
|
minecraft/ModList.cpp
|
||||||
minecraft/World.h
|
minecraft/World.h
|
||||||
minecraft/World.cpp
|
minecraft/World.cpp
|
||||||
minecraft/WorldList.h
|
minecraft/WorldList.h
|
||||||
minecraft/WorldList.cpp
|
minecraft/WorldList.cpp
|
||||||
|
|
||||||
|
# FTB
|
||||||
|
minecraft/ftb/OneSixFTBInstance.h
|
||||||
|
minecraft/ftb/OneSixFTBInstance.cpp
|
||||||
|
minecraft/ftb/LegacyFTBInstance.h
|
||||||
|
minecraft/ftb/LegacyFTBInstance.cpp
|
||||||
|
minecraft/ftb/FTBProfileStrategy.h
|
||||||
|
minecraft/ftb/FTBProfileStrategy.cpp
|
||||||
|
minecraft/ftb/FTBInstanceProvider.cpp
|
||||||
|
minecraft/ftb/FTBInstanceProvider.h
|
||||||
|
minecraft/ftb/FTBPlugin.h
|
||||||
|
minecraft/ftb/FTBPlugin.cpp
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
minecraft/AssetsUtils.h
|
minecraft/AssetsUtils.h
|
||||||
minecraft/AssetsUtils.cpp
|
minecraft/AssetsUtils.cpp
|
||||||
|
|
||||||
# Forge and all things forge related
|
# Forge and all things forge related
|
||||||
|
minecraft/forge/ForgeVersion.h
|
||||||
|
minecraft/forge/ForgeVersion.cpp
|
||||||
|
minecraft/forge/ForgeVersionList.h
|
||||||
|
minecraft/forge/ForgeVersionList.cpp
|
||||||
minecraft/forge/ForgeXzDownload.h
|
minecraft/forge/ForgeXzDownload.h
|
||||||
minecraft/forge/ForgeXzDownload.cpp
|
minecraft/forge/ForgeXzDownload.cpp
|
||||||
|
minecraft/forge/LegacyForge.h
|
||||||
|
minecraft/forge/LegacyForge.cpp
|
||||||
|
minecraft/forge/ForgeInstaller.h
|
||||||
|
minecraft/forge/ForgeInstaller.cpp
|
||||||
|
|
||||||
# Skin upload utilities
|
# Liteloader and related things
|
||||||
|
minecraft/liteloader/LiteLoaderInstaller.h
|
||||||
|
minecraft/liteloader/LiteLoaderInstaller.cpp
|
||||||
|
minecraft/liteloader/LiteLoaderVersionList.h
|
||||||
|
minecraft/liteloader/LiteLoaderVersionList.cpp
|
||||||
minecraft/SkinUpload.cpp
|
minecraft/SkinUpload.cpp
|
||||||
minecraft/SkinUpload.h
|
minecraft/SkinUpload.h
|
||||||
)
|
)
|
||||||
@@ -318,8 +337,8 @@ add_unit_test(Library
|
|||||||
)
|
)
|
||||||
|
|
||||||
# FIXME: shares data with FileSystem test
|
# FIXME: shares data with FileSystem test
|
||||||
add_unit_test(SimpleModList
|
add_unit_test(ModList
|
||||||
SOURCES minecraft/SimpleModList_test.cpp
|
SOURCES minecraft/ModList_test.cpp
|
||||||
DATA testdata
|
DATA testdata
|
||||||
LIBS MultiMC_logic
|
LIBS MultiMC_logic
|
||||||
)
|
)
|
||||||
@@ -342,6 +361,8 @@ set(TASKS_SOURCES
|
|||||||
# Tasks
|
# Tasks
|
||||||
tasks/Task.h
|
tasks/Task.h
|
||||||
tasks/Task.cpp
|
tasks/Task.cpp
|
||||||
|
tasks/ThreadTask.h
|
||||||
|
tasks/ThreadTask.cpp
|
||||||
tasks/SequentialTask.h
|
tasks/SequentialTask.h
|
||||||
tasks/SequentialTask.cpp
|
tasks/SequentialTask.cpp
|
||||||
)
|
)
|
||||||
@@ -393,8 +414,6 @@ add_unit_test(JavaVersion
|
|||||||
set(TRANSLATIONS_SOURCES
|
set(TRANSLATIONS_SOURCES
|
||||||
translations/TranslationsModel.h
|
translations/TranslationsModel.h
|
||||||
translations/TranslationsModel.cpp
|
translations/TranslationsModel.cpp
|
||||||
translations/POTranslator.h
|
|
||||||
translations/POTranslator.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TOOLS_SOURCES
|
set(TOOLS_SOURCES
|
||||||
@@ -411,44 +430,32 @@ set(TOOLS_SOURCES
|
|||||||
tools/MCEditTool.h
|
tools/MCEditTool.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(META_SOURCES
|
set(WONKO_SOURCES
|
||||||
# Metadata sources
|
# Wonko
|
||||||
meta/JsonFormat.cpp
|
wonko/tasks/BaseWonkoEntityRemoteLoadTask.cpp
|
||||||
meta/JsonFormat.h
|
wonko/tasks/BaseWonkoEntityRemoteLoadTask.h
|
||||||
meta/BaseEntity.cpp
|
wonko/tasks/BaseWonkoEntityLocalLoadTask.cpp
|
||||||
meta/BaseEntity.h
|
wonko/tasks/BaseWonkoEntityLocalLoadTask.h
|
||||||
meta/VersionList.cpp
|
wonko/format/WonkoFormatV1.cpp
|
||||||
meta/VersionList.h
|
wonko/format/WonkoFormatV1.h
|
||||||
meta/Version.cpp
|
wonko/format/WonkoFormat.cpp
|
||||||
meta/Version.h
|
wonko/format/WonkoFormat.h
|
||||||
meta/Index.cpp
|
wonko/BaseWonkoEntity.cpp
|
||||||
meta/Index.h
|
wonko/BaseWonkoEntity.h
|
||||||
|
wonko/WonkoVersionList.cpp
|
||||||
|
wonko/WonkoVersionList.h
|
||||||
|
wonko/WonkoVersion.cpp
|
||||||
|
wonko/WonkoVersion.h
|
||||||
|
wonko/WonkoIndex.cpp
|
||||||
|
wonko/WonkoIndex.h
|
||||||
|
wonko/WonkoUtil.cpp
|
||||||
|
wonko/WonkoUtil.h
|
||||||
|
wonko/WonkoReference.cpp
|
||||||
|
wonko/WonkoReference.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(FTB_SOURCES
|
add_unit_test(WonkoIndex
|
||||||
modplatform/ftb/FtbPackFetchTask.h
|
SOURCES wonko/WonkoIndex_test.cpp
|
||||||
modplatform/ftb/FtbPackFetchTask.cpp
|
|
||||||
modplatform/ftb/FtbPackInstallTask.h
|
|
||||||
modplatform/ftb/FtbPackInstallTask.cpp
|
|
||||||
|
|
||||||
modplatform/ftb/FtbPrivatePackManager.h
|
|
||||||
modplatform/ftb/FtbPrivatePackManager.cpp
|
|
||||||
|
|
||||||
modplatform/ftb/PackHelpers.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(FLAME_SOURCES
|
|
||||||
# Flame
|
|
||||||
modplatform/flame/PackManifest.h
|
|
||||||
modplatform/flame/PackManifest.cpp
|
|
||||||
modplatform/flame/FileResolvingTask.h
|
|
||||||
modplatform/flame/FileResolvingTask.cpp
|
|
||||||
modplatform/flame/UrlResolvingTask.h
|
|
||||||
modplatform/flame/UrlResolvingTask.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_unit_test(Index
|
|
||||||
SOURCES meta/Index_test.cpp
|
|
||||||
LIBS MultiMC_logic
|
LIBS MultiMC_logic
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -473,29 +480,19 @@ set(LOGIC_SOURCES
|
|||||||
${JAVA_SOURCES}
|
${JAVA_SOURCES}
|
||||||
${TRANSLATIONS_SOURCES}
|
${TRANSLATIONS_SOURCES}
|
||||||
${TOOLS_SOURCES}
|
${TOOLS_SOURCES}
|
||||||
${META_SOURCES}
|
${WONKO_SOURCES}
|
||||||
${ICONS_SOURCES}
|
${ICONS_SOURCES}
|
||||||
${FTB_SOURCES}
|
|
||||||
${FLAME_SOURCES}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
message(STATUS "FOO! ${LOGIC_SOURCES}")
|
|
||||||
|
|
||||||
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
|
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
|
||||||
set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
|
set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
|
||||||
|
|
||||||
generate_export_header(MultiMC_logic)
|
generate_export_header(MultiMC_logic)
|
||||||
|
|
||||||
# Link
|
# Link
|
||||||
target_link_libraries(MultiMC_logic xz-embedded MultiMC_unpack200 systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES})
|
target_link_libraries(MultiMC_logic xz-embedded unpack200 systeminfo MultiMC_quazip ${NBT_NAME} ${ZLIB_LIBRARIES})
|
||||||
target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent)
|
|
||||||
|
qt5_use_modules(MultiMC_logic Core Xml Network Concurrent)
|
||||||
|
|
||||||
# Mark and export headers
|
# Mark and export headers
|
||||||
target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PRIVATE "${ZLIB_INCLUDE_DIRS}")
|
target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PRIVATE "${ZLIB_INCLUDE_DIRS}")
|
||||||
|
|
||||||
# Install it
|
|
||||||
install(
|
|
||||||
TARGETS MultiMC_logic
|
|
||||||
RUNTIME DESTINATION ${LIBRARY_DEST_DIR}
|
|
||||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
*
|
*
|
||||||
@@ -44,7 +44,7 @@ QStringList splitArgs(QString args)
|
|||||||
}
|
}
|
||||||
else if (!inquotes.isNull())
|
else if (!inquotes.isNull())
|
||||||
{
|
{
|
||||||
if (cchar == '\\')
|
if (cchar == 0x5C)
|
||||||
escape = true;
|
escape = true;
|
||||||
else if (cchar == inquotes)
|
else if (cchar == inquotes)
|
||||||
inquotes = 0;
|
inquotes = 0;
|
||||||
@@ -54,7 +54,7 @@ QStringList splitArgs(QString args)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (cchar == ' ')
|
if (cchar == 0x20)
|
||||||
{
|
{
|
||||||
if (!current.isEmpty())
|
if (!current.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -62,7 +62,7 @@ QStringList splitArgs(QString args)
|
|||||||
current.clear();
|
current.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cchar == '"' || cchar == '\'')
|
else if (cchar == 0x22 || cchar == 0x27)
|
||||||
inquotes = cchar;
|
inquotes = cchar;
|
||||||
else
|
else
|
||||||
current += cchar;
|
current += cchar;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,24 +3,23 @@
|
|||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "BaseVersionList.h"
|
#include "BaseVersionList.h"
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
#include "meta/Index.h"
|
#include "wonko/WonkoIndex.h"
|
||||||
#include "FileSystem.h"
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
|
||||||
struct Env::Private
|
class Env::Private
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
QNetworkAccessManager m_qnam;
|
QNetworkAccessManager m_qnam;
|
||||||
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
||||||
std::shared_ptr<IIconList> m_iconlist;
|
std::shared_ptr<IIconList> m_iconlist;
|
||||||
shared_qobject_ptr<Meta::Index> m_metadataIndex;
|
QMap<QString, std::shared_ptr<BaseVersionList>> m_versionLists;
|
||||||
QString m_jarsPath;
|
shared_qobject_ptr<WonkoIndex> m_wonkoIndex;
|
||||||
QSet<QString> m_features;
|
QString m_wonkoRootUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Env * instance;
|
static Env * instance;
|
||||||
@@ -74,13 +73,39 @@ void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
|
|||||||
d->m_iconlist = iconlist;
|
d->m_iconlist = iconlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_qobject_ptr<Meta::Index> Env::metadataIndex()
|
BaseVersionPtr Env::getVersion(QString component, QString version)
|
||||||
{
|
{
|
||||||
if (!d->m_metadataIndex)
|
auto list = getVersionList(component);
|
||||||
|
if(!list)
|
||||||
{
|
{
|
||||||
d->m_metadataIndex.reset(new Meta::Index());
|
return nullptr;
|
||||||
}
|
}
|
||||||
return d->m_metadataIndex;
|
return list->findVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr< BaseVersionList > Env::getVersionList(QString component)
|
||||||
|
{
|
||||||
|
auto iter = d->m_versionLists.find(component);
|
||||||
|
if(iter != d->m_versionLists.end())
|
||||||
|
{
|
||||||
|
return *iter;
|
||||||
|
}
|
||||||
|
//return std::make_shared<NullVersionList>();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Env::registerVersionList(QString name, std::shared_ptr< BaseVersionList > vlist)
|
||||||
|
{
|
||||||
|
d->m_versionLists[name] = vlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_qobject_ptr<WonkoIndex> Env::wonkoIndex()
|
||||||
|
{
|
||||||
|
if (!d->m_wonkoIndex)
|
||||||
|
{
|
||||||
|
d->m_wonkoIndex.reset(new WonkoIndex());
|
||||||
|
}
|
||||||
|
return d->m_wonkoIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -96,12 +121,11 @@ void Env::initHttpMetaCache()
|
|||||||
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
|
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
|
||||||
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
||||||
m_metacache->addBase("general", QDir("cache").absolutePath());
|
m_metacache->addBase("general", QDir("cache").absolutePath());
|
||||||
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
|
|
||||||
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
||||||
m_metacache->addBase("root", QDir::currentPath());
|
m_metacache->addBase("root", QDir::currentPath());
|
||||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
m_metacache->addBase("wonko", QDir("cache/wonko").absolutePath());
|
||||||
m_metacache->Load();
|
m_metacache->Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,43 +191,14 @@ void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QStr
|
|||||||
qDebug() << proxyDesc;
|
qDebug() << proxyDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Env::getJarsPath()
|
QString Env::wonkoRootUrl() const
|
||||||
{
|
{
|
||||||
if(d->m_jarsPath.isEmpty())
|
return d->m_wonkoRootUrl;
|
||||||
{
|
|
||||||
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
|
|
||||||
}
|
|
||||||
return d->m_jarsPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Env::setJarsPath(const QString& path)
|
void Env::setWonkoRootUrl(const QString& url)
|
||||||
{
|
{
|
||||||
d->m_jarsPath = path;
|
d->m_wonkoRootUrl = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Env::enableFeature(const QString& featureName, bool state)
|
#include "Env.moc"
|
||||||
{
|
|
||||||
if(state)
|
|
||||||
{
|
|
||||||
d->m_features.insert(featureName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
d->m_features.remove(featureName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Env::isFeatureEnabled(const QString& featureName) const
|
|
||||||
{
|
|
||||||
return d->m_features.contains(featureName);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Env::getEnabledFeatures(QSet<QString>& features) const
|
|
||||||
{
|
|
||||||
features = d->m_features;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Env::setEnabledFeatures(const QSet<QString>& features) const
|
|
||||||
{
|
|
||||||
d->m_features = features;
|
|
||||||
}
|
|
||||||
@@ -13,23 +13,18 @@ class QNetworkAccessManager;
|
|||||||
class HttpMetaCache;
|
class HttpMetaCache;
|
||||||
class BaseVersionList;
|
class BaseVersionList;
|
||||||
class BaseVersion;
|
class BaseVersion;
|
||||||
|
class WonkoIndex;
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
class Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(ENV)
|
#if defined(ENV)
|
||||||
#undef ENV
|
#undef ENV
|
||||||
#endif
|
#endif
|
||||||
#define ENV (Env::getInstance())
|
#define ENV (Env::getInstance())
|
||||||
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT Env
|
class MULTIMC_LOGIC_EXPORT Env
|
||||||
{
|
{
|
||||||
friend class MultiMC;
|
friend class MultiMC;
|
||||||
private:
|
private:
|
||||||
struct Private;
|
class Private;
|
||||||
Env();
|
Env();
|
||||||
~Env();
|
~Env();
|
||||||
static void dispose();
|
static void dispose();
|
||||||
@@ -48,17 +43,20 @@ public:
|
|||||||
/// Updates the application proxy settings from the settings object.
|
/// Updates the application proxy settings from the settings object.
|
||||||
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
|
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
|
||||||
|
|
||||||
|
/// get a version list by name
|
||||||
|
std::shared_ptr<BaseVersionList> getVersionList(QString component);
|
||||||
|
|
||||||
|
/// get a version by list name and version name
|
||||||
|
std::shared_ptr<BaseVersion> getVersion(QString component, QString version);
|
||||||
|
|
||||||
|
void registerVersionList(QString name, std::shared_ptr<BaseVersionList> vlist);
|
||||||
|
|
||||||
void registerIconList(std::shared_ptr<IIconList> iconlist);
|
void registerIconList(std::shared_ptr<IIconList> iconlist);
|
||||||
|
|
||||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
shared_qobject_ptr<WonkoIndex> wonkoIndex();
|
||||||
|
|
||||||
QString getJarsPath();
|
QString wonkoRootUrl() const;
|
||||||
void setJarsPath(const QString & path);
|
void setWonkoRootUrl(const QString &url);
|
||||||
|
|
||||||
bool isFeatureEnabled(const QString & featureName) const;
|
|
||||||
void enableFeature(const QString & featureName, bool state = true);
|
|
||||||
void getEnabledFeatures(QSet<QString> & features) const;
|
|
||||||
void setEnabledFeatures(const QSet<QString> & features) const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Private * d;
|
Private * d;
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline void clamp(T& current, T min, T max)
|
|
||||||
{
|
|
||||||
if (current < min)
|
|
||||||
{
|
|
||||||
current = min;
|
|
||||||
}
|
|
||||||
else if(current > max)
|
|
||||||
{
|
|
||||||
current = max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of numbers from min to max. Next is exponent times bigger than previous.
|
|
||||||
|
|
||||||
class ExponentialSeries
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
|
|
||||||
{
|
|
||||||
m_current = m_min = min;
|
|
||||||
m_max = max;
|
|
||||||
m_exponent = exponent;
|
|
||||||
}
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
m_current = m_min;
|
|
||||||
}
|
|
||||||
unsigned operator()()
|
|
||||||
{
|
|
||||||
unsigned retval = m_current;
|
|
||||||
m_current *= m_exponent;
|
|
||||||
clamp(m_current, m_min, m_max);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
unsigned m_current;
|
|
||||||
unsigned m_min;
|
|
||||||
unsigned m_max;
|
|
||||||
unsigned m_exponent;
|
|
||||||
};
|
|
||||||
@@ -3,27 +3,11 @@
|
|||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QTextStream>
|
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/utime.h>
|
|
||||||
#include <winnls.h>
|
|
||||||
#include <shobjidl.h>
|
|
||||||
#include <objbase.h>
|
|
||||||
#include <objidl.h>
|
|
||||||
#include <shlguid.h>
|
|
||||||
#include <shlobj.h>
|
|
||||||
#else
|
|
||||||
#include <utime.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace FS {
|
namespace FS {
|
||||||
|
|
||||||
@@ -78,13 +62,21 @@ QByteArray read(const QString &filename)
|
|||||||
|
|
||||||
bool updateTimestamp(const QString& filename)
|
bool updateTimestamp(const QString& filename)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN32
|
QFile file(filename);
|
||||||
std::wstring filename_utf_16 = filename.toStdWString();
|
if (!file.exists())
|
||||||
return (_wutime64(filename_utf_16.c_str(), nullptr) == 0);
|
{
|
||||||
#else
|
return false;
|
||||||
QByteArray filenameBA = QFile::encodeName(filename);
|
}
|
||||||
return (utime(filenameBA.data(), nullptr) == 0);
|
if (!file.open(QIODevice::ReadWrite))
|
||||||
#endif
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const quint64 size = file.size();
|
||||||
|
file.seek(size);
|
||||||
|
file.write( QByteArray(1, '0') );
|
||||||
|
file.resize(size);
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ensureFilePathExists(QString filenamepath)
|
bool ensureFilePathExists(QString filenamepath)
|
||||||
@@ -171,6 +163,11 @@ bool copy::operator()(const QString &offset)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string>
|
||||||
|
#endif
|
||||||
bool deletePath(QString path)
|
bool deletePath(QString path)
|
||||||
{
|
{
|
||||||
bool OK = true;
|
bool OK = true;
|
||||||
@@ -228,7 +225,7 @@ bool deletePath(QString path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString PathCombine(const QString & path1, const QString & path2)
|
QString PathCombine(QString path1, QString path2)
|
||||||
{
|
{
|
||||||
if(!path1.size())
|
if(!path1.size())
|
||||||
return path2;
|
return path2;
|
||||||
@@ -237,16 +234,11 @@ QString PathCombine(const QString & path1, const QString & path2)
|
|||||||
return QDir::cleanPath(path1 + QDir::separator() + path2);
|
return QDir::cleanPath(path1 + QDir::separator() + path2);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PathCombine(const QString & path1, const QString & path2, const QString & path3)
|
QString PathCombine(QString path1, QString path2, QString path3)
|
||||||
{
|
{
|
||||||
return PathCombine(PathCombine(path1, path2), path3);
|
return PathCombine(PathCombine(path1, path2), path3);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
|
|
||||||
{
|
|
||||||
return PathCombine(PathCombine(path1, path2, path3), path4);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AbsolutePath(QString path)
|
QString AbsolutePath(QString path)
|
||||||
{
|
{
|
||||||
return QFileInfo(path).absolutePath();
|
return QFileInfo(path).absolutePath();
|
||||||
@@ -294,7 +286,7 @@ QString NormalizePath(QString path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString badFilenameChars = "\"\\/?<>:*|!+\r\n";
|
QString badFilenameChars = "\"\\/?<>:*|!";
|
||||||
|
|
||||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
||||||
{
|
{
|
||||||
@@ -340,9 +332,21 @@ bool checkProblemticPathJava(QDir folder)
|
|||||||
return pathfoldername.contains("!", Qt::CaseInsensitive);
|
return pathfoldername.contains("!", Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
// Win32 crap
|
// Win32 crap
|
||||||
#if defined Q_OS_WIN
|
#if defined Q_OS_WIN
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winnls.h>
|
||||||
|
#include <shobjidl.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
#include <objidl.h>
|
||||||
|
#include <shlguid.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
|
||||||
bool called_coinit = false;
|
bool called_coinit = false;
|
||||||
|
|
||||||
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
|
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
|
||||||
@@ -356,7 +360,7 @@ HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
|
|||||||
|
|
||||||
if (!SUCCEEDED(hres))
|
if (!SUCCEEDED(hres))
|
||||||
{
|
{
|
||||||
qWarning("Failed to initialize COM. Error 0x%08lX", hres);
|
qWarning("Failed to initialize COM. Error 0x%08X", hres);
|
||||||
return hres;
|
return hres;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,9 +83,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
MULTIMC_LOGIC_EXPORT bool deletePath(QString path);
|
MULTIMC_LOGIC_EXPORT bool deletePath(QString path);
|
||||||
|
|
||||||
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2);
|
MULTIMC_LOGIC_EXPORT QString PathCombine(QString path1, QString path2);
|
||||||
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
|
MULTIMC_LOGIC_EXPORT QString PathCombine(QString path1, QString path2, QString path3);
|
||||||
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
|
|
||||||
|
|
||||||
MULTIMC_LOGIC_EXPORT QString AbsolutePath(QString path);
|
MULTIMC_LOGIC_EXPORT QString AbsolutePath(QString path);
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
#include "Filter.h"
|
|
||||||
|
|
||||||
Filter::~Filter(){}
|
|
||||||
|
|
||||||
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
|
|
||||||
ContainsFilter::~ContainsFilter(){}
|
|
||||||
bool ContainsFilter::accepts(const QString& value)
|
|
||||||
{
|
|
||||||
return value.contains(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
|
|
||||||
ExactFilter::~ExactFilter(){}
|
|
||||||
bool ExactFilter::accepts(const QString& value)
|
|
||||||
{
|
|
||||||
return value == pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
|
|
||||||
:invert(invert)
|
|
||||||
{
|
|
||||||
pattern.setPattern(regexp);
|
|
||||||
pattern.optimize();
|
|
||||||
}
|
|
||||||
RegexpFilter::~RegexpFilter(){}
|
|
||||||
bool RegexpFilter::accepts(const QString& value)
|
|
||||||
{
|
|
||||||
auto match = pattern.match(value);
|
|
||||||
bool matched = match.hasMatch();
|
|
||||||
return invert ? (!matched) : (matched);
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT Filter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~Filter();
|
|
||||||
virtual bool accepts(const QString & value) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT ContainsFilter: public Filter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ContainsFilter(const QString &pattern);
|
|
||||||
virtual ~ContainsFilter();
|
|
||||||
bool accepts(const QString & value) override;
|
|
||||||
private:
|
|
||||||
QString pattern;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ExactFilter(const QString &pattern);
|
|
||||||
virtual ~ExactFilter();
|
|
||||||
bool accepts(const QString & value) override;
|
|
||||||
private:
|
|
||||||
QString pattern;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RegexpFilter(const QString ®exp, bool invert);
|
|
||||||
virtual ~RegexpFilter();
|
|
||||||
bool accepts(const QString & value) override;
|
|
||||||
private:
|
|
||||||
QRegularExpression pattern;
|
|
||||||
bool invert = false;
|
|
||||||
};
|
|
||||||
356
api/logic/FolderInstanceProvider.cpp
Normal file
356
api/logic/FolderInstanceProvider.cpp
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
#include "FolderInstanceProvider.h"
|
||||||
|
#include "settings/INISettingsObject.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "minecraft/onesix/OneSixInstance.h"
|
||||||
|
#include "minecraft/legacy/LegacyInstance.h"
|
||||||
|
#include "NullInstance.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
||||||
|
|
||||||
|
struct WatchLock
|
||||||
|
{
|
||||||
|
WatchLock(QFileSystemWatcher * watcher, const QString& instDir)
|
||||||
|
: m_watcher(watcher), m_instDir(instDir)
|
||||||
|
{
|
||||||
|
m_watcher->removePath(m_instDir);
|
||||||
|
}
|
||||||
|
~WatchLock()
|
||||||
|
{
|
||||||
|
m_watcher->addPath(m_instDir);
|
||||||
|
}
|
||||||
|
QFileSystemWatcher * m_watcher;
|
||||||
|
QString m_instDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
FolderInstanceProvider::FolderInstanceProvider(SettingsObjectPtr settings, const QString& instDir)
|
||||||
|
: BaseInstanceProvider(settings)
|
||||||
|
{
|
||||||
|
m_instDir = instDir;
|
||||||
|
if (!QDir::current().exists(m_instDir))
|
||||||
|
{
|
||||||
|
QDir::current().mkpath(m_instDir);
|
||||||
|
}
|
||||||
|
m_watcher = new QFileSystemWatcher(this);
|
||||||
|
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &FolderInstanceProvider::instanceDirContentsChanged);
|
||||||
|
m_watcher->addPath(m_instDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList< InstanceId > FolderInstanceProvider::discoverInstances()
|
||||||
|
{
|
||||||
|
QList<InstanceId> out;
|
||||||
|
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, QDirIterator::FollowSymlinks);
|
||||||
|
while (iter.hasNext())
|
||||||
|
{
|
||||||
|
QString subDir = iter.next();
|
||||||
|
QFileInfo dirInfo(subDir);
|
||||||
|
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
|
||||||
|
continue;
|
||||||
|
// if it is a symlink, ignore it if it goes to the instance folder
|
||||||
|
if(dirInfo.isSymLink())
|
||||||
|
{
|
||||||
|
QFileInfo targetInfo(dirInfo.symLinkTarget());
|
||||||
|
QFileInfo instDirInfo(m_instDir);
|
||||||
|
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
|
||||||
|
{
|
||||||
|
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto id = dirInfo.fileName();
|
||||||
|
out.append(id);
|
||||||
|
qDebug() << "Found instance ID" << id;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id)
|
||||||
|
{
|
||||||
|
if(!m_groupsLoaded)
|
||||||
|
{
|
||||||
|
loadGroupList();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto instanceRoot = FS::PathCombine(m_instDir, id);
|
||||||
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
|
||||||
|
InstancePtr inst;
|
||||||
|
|
||||||
|
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||||
|
|
||||||
|
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||||
|
|
||||||
|
if (inst_type == "OneSix" || inst_type == "Nostalgia")
|
||||||
|
{
|
||||||
|
inst.reset(new OneSixInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
|
}
|
||||||
|
else if (inst_type == "Legacy")
|
||||||
|
{
|
||||||
|
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
|
}
|
||||||
|
inst->init();
|
||||||
|
inst->setProvider(this);
|
||||||
|
auto iter = groupMap.find(id);
|
||||||
|
if (iter != groupMap.end())
|
||||||
|
{
|
||||||
|
inst->setGroupInitial((*iter));
|
||||||
|
}
|
||||||
|
connect(inst.get(), &BaseInstance::groupChanged, this, &FolderInstanceProvider::groupChanged);
|
||||||
|
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "InstanceImportTask.h"
|
||||||
|
Task * FolderInstanceProvider::zipImportTask(const QUrl sourceUrl, const QString& instName, const QString& instGroup, const QString& instIcon)
|
||||||
|
{
|
||||||
|
return new InstanceImportTask(m_globalSettings, sourceUrl, this, instName, instIcon, instGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "InstanceCreationTask.h"
|
||||||
|
Task * FolderInstanceProvider::creationTask(BaseVersionPtr version, const QString& instName, const QString& instGroup, const QString& instIcon)
|
||||||
|
{
|
||||||
|
return new InstanceCreationTask(m_globalSettings, this, version, instName, instIcon, instGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "InstanceCopyTask.h"
|
||||||
|
Task * FolderInstanceProvider::copyTask(const InstancePtr& oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves)
|
||||||
|
{
|
||||||
|
return new InstanceCopyTask(m_globalSettings, this, oldInstance, instName, instIcon, instGroup, copySaves);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderInstanceProvider::saveGroupList()
|
||||||
|
{
|
||||||
|
WatchLock foo(m_watcher, m_instDir);
|
||||||
|
QString groupFileName = m_instDir + "/instgroups.json";
|
||||||
|
QMap<QString, QSet<QString>> reverseGroupMap;
|
||||||
|
for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++)
|
||||||
|
{
|
||||||
|
QString id = iter.key();
|
||||||
|
QString group = iter.value();
|
||||||
|
if (group.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!reverseGroupMap.count(group))
|
||||||
|
{
|
||||||
|
QSet<QString> set;
|
||||||
|
set.insert(id);
|
||||||
|
reverseGroupMap[group] = set;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QSet<QString> &set = reverseGroupMap[group];
|
||||||
|
set.insert(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QJsonObject toplevel;
|
||||||
|
toplevel.insert("formatVersion", QJsonValue(QString("1")));
|
||||||
|
QJsonObject groupsArr;
|
||||||
|
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
|
||||||
|
{
|
||||||
|
auto list = iter.value();
|
||||||
|
auto name = iter.key();
|
||||||
|
QJsonObject groupObj;
|
||||||
|
QJsonArray instanceArr;
|
||||||
|
groupObj.insert("hidden", QJsonValue(QString("false")));
|
||||||
|
for (auto item : list)
|
||||||
|
{
|
||||||
|
instanceArr.append(QJsonValue(item));
|
||||||
|
}
|
||||||
|
groupObj.insert("instances", instanceArr);
|
||||||
|
groupsArr.insert(name, groupObj);
|
||||||
|
}
|
||||||
|
toplevel.insert("groups", groupsArr);
|
||||||
|
QJsonDocument doc(toplevel);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FS::write(groupFileName, doc.toJson());
|
||||||
|
}
|
||||||
|
catch(FS::FileSystemException & e)
|
||||||
|
{
|
||||||
|
qCritical() << "Failed to write instance group file :" << e.cause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderInstanceProvider::loadGroupList()
|
||||||
|
{
|
||||||
|
QSet<QString> groupSet;
|
||||||
|
|
||||||
|
QString groupFileName = m_instDir + "/instgroups.json";
|
||||||
|
|
||||||
|
// if there's no group file, fail
|
||||||
|
if (!QFileInfo(groupFileName).exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QByteArray jsonData;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
jsonData = FS::read(groupFileName);
|
||||||
|
}
|
||||||
|
catch (FS::FileSystemException & e)
|
||||||
|
{
|
||||||
|
qCritical() << "Failed to read instance group file :" << e.cause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
|
||||||
|
|
||||||
|
// if the json was bad, fail
|
||||||
|
if (error.error != QJsonParseError::NoError)
|
||||||
|
{
|
||||||
|
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
|
||||||
|
.arg(error.errorString(), QString::number(error.offset))
|
||||||
|
.toUtf8();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the root of the json wasn't an object, fail
|
||||||
|
if (!jsonDoc.isObject())
|
||||||
|
{
|
||||||
|
qWarning() << "Invalid group file. Root entry should be an object.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject rootObj = jsonDoc.object();
|
||||||
|
|
||||||
|
// Make sure the format version matches, otherwise fail.
|
||||||
|
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the groups. if it's not an object, fail
|
||||||
|
if (!rootObj.value("groups").isObject())
|
||||||
|
{
|
||||||
|
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
groupMap.clear();
|
||||||
|
|
||||||
|
// Iterate through all the groups.
|
||||||
|
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
||||||
|
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
|
||||||
|
{
|
||||||
|
QString groupName = iter.key();
|
||||||
|
|
||||||
|
// If not an object, complain and skip to the next one.
|
||||||
|
if (!iter.value().isObject())
|
||||||
|
{
|
||||||
|
qWarning() << QString("Group '%1' in the group list should "
|
||||||
|
"be an object.")
|
||||||
|
.arg(groupName)
|
||||||
|
.toUtf8();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject groupObj = iter.value().toObject();
|
||||||
|
if (!groupObj.value("instances").isArray())
|
||||||
|
{
|
||||||
|
qWarning() << QString("Group '%1' in the group list is invalid. "
|
||||||
|
"It should contain an array "
|
||||||
|
"called 'instances'.")
|
||||||
|
.arg(groupName)
|
||||||
|
.toUtf8();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep a list/set of groups for choosing
|
||||||
|
groupSet.insert(groupName);
|
||||||
|
|
||||||
|
// Iterate through the list of instances in the group.
|
||||||
|
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||||
|
|
||||||
|
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end();
|
||||||
|
iter2++)
|
||||||
|
{
|
||||||
|
groupMap[(*iter2).toString()] = groupName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_groupsLoaded = true;
|
||||||
|
emit groupsChanged(groupSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderInstanceProvider::groupChanged()
|
||||||
|
{
|
||||||
|
// save the groups. save all of them.
|
||||||
|
auto instance = (BaseInstance *) QObject::sender();
|
||||||
|
auto id = instance->id();
|
||||||
|
groupMap[id] = instance->group();
|
||||||
|
emit groupsChanged({instance->group()});
|
||||||
|
saveGroupList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FolderInstanceProvider::instanceDirContentsChanged(const QString& path)
|
||||||
|
{
|
||||||
|
Q_UNUSED(path);
|
||||||
|
emit instancesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderInstanceProvider::on_InstFolderChanged(const Setting &setting, QVariant value)
|
||||||
|
{
|
||||||
|
QString newInstDir = value.toString();
|
||||||
|
if(newInstDir != m_instDir)
|
||||||
|
{
|
||||||
|
if(m_groupsLoaded)
|
||||||
|
{
|
||||||
|
saveGroupList();
|
||||||
|
}
|
||||||
|
m_instDir = newInstDir;
|
||||||
|
m_groupsLoaded = false;
|
||||||
|
emit instancesChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FolderInstanceProvider::getStagedInstancePath()
|
||||||
|
{
|
||||||
|
QString key = QUuid::createUuid().toString();
|
||||||
|
QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
|
||||||
|
QDir rootPath(m_instDir);
|
||||||
|
auto path = FS::PathCombine(m_instDir, relPath);
|
||||||
|
if(!rootPath.mkpath(relPath))
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FolderInstanceProvider::commitStagedInstance(const QString& keyPath, const QString& path, const QString& instanceName,
|
||||||
|
const QString& groupName)
|
||||||
|
{
|
||||||
|
if(!path.contains(keyPath))
|
||||||
|
{
|
||||||
|
qWarning() << "It is not possible to commit" << path << "because it is not in" << keyPath;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QDir dir;
|
||||||
|
QString instID = FS::DirNameFromString(instanceName, m_instDir);
|
||||||
|
{
|
||||||
|
WatchLock lock(m_watcher, m_instDir);
|
||||||
|
if(!dir.rename(path, FS::PathCombine(m_instDir, instID)))
|
||||||
|
{
|
||||||
|
destroyStagingPath(keyPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
groupMap[instID] = groupName;
|
||||||
|
emit groupsChanged({groupName});
|
||||||
|
emit instancesChanged();
|
||||||
|
}
|
||||||
|
saveGroupList();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FolderInstanceProvider::destroyStagingPath(const QString& keyPath)
|
||||||
|
{
|
||||||
|
return FS::deletePath(keyPath);
|
||||||
|
}
|
||||||
|
|
||||||
63
api/logic/FolderInstanceProvider.h
Normal file
63
api/logic/FolderInstanceProvider.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BaseInstanceProvider.h"
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
class QFileSystemWatcher;
|
||||||
|
|
||||||
|
class MULTIMC_LOGIC_EXPORT FolderInstanceProvider : public BaseInstanceProvider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
FolderInstanceProvider(SettingsObjectPtr settings, const QString & instDir);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// used by InstanceList to @return a list of plausible IDs to probe for
|
||||||
|
QList<InstanceId> discoverInstances() override;
|
||||||
|
|
||||||
|
/// used by InstanceList to (re)load an instance with the given @id.
|
||||||
|
InstancePtr loadInstance(const InstanceId& id) override;
|
||||||
|
|
||||||
|
|
||||||
|
// create instance in this provider
|
||||||
|
Task * creationTask(BaseVersionPtr version, const QString &instName, const QString &instGroup, const QString &instIcon);
|
||||||
|
|
||||||
|
// copy instance to this provider
|
||||||
|
Task * copyTask(const InstancePtr &oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves);
|
||||||
|
|
||||||
|
// import zipped instance into this provider
|
||||||
|
Task * zipImportTask(const QUrl sourceUrl, const QString &instName, const QString &instGroup, const QString &instIcon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
|
||||||
|
* Used by instance manipulation tasks.
|
||||||
|
*/
|
||||||
|
QString getStagedInstancePath() override;
|
||||||
|
/**
|
||||||
|
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
||||||
|
* Used by instance manipulation tasks.
|
||||||
|
*/
|
||||||
|
bool commitStagedInstance(const QString & keyPath, const QString & path, const QString& instanceName, const QString & groupName) override;
|
||||||
|
/**
|
||||||
|
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
||||||
|
* Used by instance manipulation tasks.
|
||||||
|
*/
|
||||||
|
bool destroyStagingPath(const QString & keyPath) override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void on_InstFolderChanged(const Setting &setting, QVariant value);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void instanceDirContentsChanged(const QString &path);
|
||||||
|
void groupChanged();
|
||||||
|
|
||||||
|
private: /* methods */
|
||||||
|
void loadGroupList() override;
|
||||||
|
void saveGroupList() override;
|
||||||
|
|
||||||
|
private: /* data */
|
||||||
|
QString m_instDir;
|
||||||
|
QFileSystemWatcher * m_watcher;
|
||||||
|
QMap<QString, QString> groupMap;
|
||||||
|
bool m_groupsLoaded = false;
|
||||||
|
};
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
#include "InstanceCopyTask.h"
|
#include "InstanceCopyTask.h"
|
||||||
|
#include "BaseInstanceProvider.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "pathmatcher/RegexpMatcher.h"
|
#include "pathmatcher/RegexpMatcher.h"
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
|
InstanceCopyTask::InstanceCopyTask(SettingsObjectPtr settings, BaseInstanceProvider* target, InstancePtr origInstance, const QString& instName, const QString& instIcon, const QString& instGroup, bool copySaves)
|
||||||
{
|
{
|
||||||
|
m_globalSettings = settings;
|
||||||
|
m_target = target;
|
||||||
m_origInstance = origInstance;
|
m_origInstance = origInstance;
|
||||||
|
m_instName = instName;
|
||||||
|
m_instIcon = instIcon;
|
||||||
|
m_instGroup = instGroup;
|
||||||
|
|
||||||
if(!copySaves)
|
if(!copySaves)
|
||||||
{
|
{
|
||||||
@@ -21,7 +27,7 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
|
|||||||
void InstanceCopyTask::executeTask()
|
void InstanceCopyTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||||
|
m_stagingPath = m_target->getStagedInstancePath();
|
||||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
|
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
|
||||||
|
|
||||||
@@ -36,6 +42,7 @@ void InstanceCopyTask::copyFinished()
|
|||||||
auto successful = m_copyFuture.result();
|
auto successful = m_copyFuture.result();
|
||||||
if(!successful)
|
if(!successful)
|
||||||
{
|
{
|
||||||
|
m_target->destroyStagingPath(m_stagingPath);
|
||||||
emitFailed(tr("Instance folder copy failed."));
|
emitFailed(tr("Instance folder copy failed."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -43,14 +50,19 @@ void InstanceCopyTask::copyFinished()
|
|||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||||
|
|
||||||
|
// FIXME: and this too? errors???
|
||||||
|
m_origInstance->copy(instanceSettings, m_stagingPath);
|
||||||
|
|
||||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||||
inst->setName(m_instName);
|
inst->setName(m_instName);
|
||||||
inst->setIconKey(m_instIcon);
|
inst->setIconKey(m_instIcon);
|
||||||
|
m_target->commitStagedInstance(m_stagingPath, m_stagingPath, m_instName, m_instGroup);
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceCopyTask::copyAborted()
|
void InstanceCopyTask::copyAborted()
|
||||||
{
|
{
|
||||||
|
m_target->destroyStagingPath(m_stagingPath);
|
||||||
emitFailed(tr("Instance folder copy has been aborted."));
|
emitFailed(tr("Instance folder copy has been aborted."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,16 @@
|
|||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "InstanceTask.h"
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
|
|
||||||
|
class BaseInstanceProvider;
|
||||||
|
|
||||||
|
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public Task
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
|
explicit InstanceCopyTask(SettingsObjectPtr settings, BaseInstanceProvider * target, InstancePtr origInstance, const QString &instName,
|
||||||
|
const QString &instIcon, const QString &instGroup, bool copySaves);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
@@ -24,8 +27,16 @@ protected:
|
|||||||
void copyAborted();
|
void copyAborted();
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
|
SettingsObjectPtr m_globalSettings;
|
||||||
|
BaseInstanceProvider * m_target = nullptr;
|
||||||
InstancePtr m_origInstance;
|
InstancePtr m_origInstance;
|
||||||
|
QString m_instName;
|
||||||
|
QString m_instIcon;
|
||||||
|
QString m_instGroup;
|
||||||
|
QString m_stagingPath;
|
||||||
QFuture<bool> m_copyFuture;
|
QFuture<bool> m_copyFuture;
|
||||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||||
std::unique_ptr<IPathMatcher> m_matcher;
|
std::unique_ptr<IPathMatcher> m_matcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,46 @@
|
|||||||
#include "InstanceCreationTask.h"
|
#include "InstanceCreationTask.h"
|
||||||
|
#include "BaseInstanceProvider.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
//FIXME: remove this
|
//FIXME: remove this
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftVersion.h"
|
||||||
#include "minecraft/ComponentList.h"
|
#include "minecraft/onesix/OneSixInstance.h"
|
||||||
|
|
||||||
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
|
InstanceCreationTask::InstanceCreationTask(SettingsObjectPtr settings, BaseInstanceProvider* target, BaseVersionPtr version,
|
||||||
|
const QString& instName, const QString& instIcon, const QString& instGroup)
|
||||||
{
|
{
|
||||||
|
m_globalSettings = settings;
|
||||||
|
m_target = target;
|
||||||
|
m_instName = instName;
|
||||||
|
m_instIcon = instIcon;
|
||||||
|
m_instGroup = instGroup;
|
||||||
m_version = version;
|
m_version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceCreationTask::executeTask()
|
void InstanceCreationTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
||||||
|
auto minecraftVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_version);
|
||||||
|
if(!minecraftVersion)
|
||||||
{
|
{
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
emitFailed(tr("The supplied version is not a Minecraft version."));
|
||||||
instanceSettings->suspendSave();
|
return ;
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
|
||||||
auto components = inst.getComponentList();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
|
||||||
inst.setName(m_instName);
|
|
||||||
inst.setIconKey(m_instIcon);
|
|
||||||
instanceSettings->resumeSave();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString stagingPath = m_target->getStagedInstancePath();
|
||||||
|
QDir rootDir(stagingPath);
|
||||||
|
|
||||||
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(stagingPath, "instance.cfg"));
|
||||||
|
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||||
|
|
||||||
|
auto mcVer = std::dynamic_pointer_cast<MinecraftVersion>(m_version);
|
||||||
|
instanceSettings->set("InstanceType", "OneSix");
|
||||||
|
InstancePtr inst(new OneSixInstance(m_globalSettings, instanceSettings, stagingPath));
|
||||||
|
inst->setIntendedVersionId(m_version->descriptor());
|
||||||
|
inst->setName(m_instName);
|
||||||
|
inst->setIconKey(m_instIcon);
|
||||||
|
inst->init();
|
||||||
|
m_target->commitStagedInstance(stagingPath, stagingPath, m_instName, m_instGroup);
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,26 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "InstanceTask.h"
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask
|
class BaseInstanceProvider;
|
||||||
|
|
||||||
|
class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public Task
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCreationTask(BaseVersionPtr version);
|
explicit InstanceCreationTask(SettingsObjectPtr settings, BaseInstanceProvider * target, BaseVersionPtr version, const QString &instName,
|
||||||
|
const QString &instIcon, const QString &instGroup);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
|
SettingsObjectPtr m_globalSettings;
|
||||||
|
BaseInstanceProvider * m_target;
|
||||||
BaseVersionPtr m_version;
|
BaseVersionPtr m_version;
|
||||||
|
QString m_instName;
|
||||||
|
QString m_instIcon;
|
||||||
|
QString m_instGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
|
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
#include "BaseInstanceProvider.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "Env.h"
|
#include "Env.h"
|
||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "icons/IIconList.h"
|
#include "icons/IIconList.h"
|
||||||
#include "icons/IconUtils.h"
|
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
// FIXME: this does not belong here, it's Minecraft/Flame specific
|
InstanceImportTask::InstanceImportTask(SettingsObjectPtr settings, const QUrl sourceUrl, BaseInstanceProvider * target,
|
||||||
#include "minecraft/MinecraftInstance.h"
|
const QString &instName, const QString &instIcon, const QString &instGroup)
|
||||||
#include "minecraft/ComponentList.h"
|
|
||||||
#include "modplatform/flame/FileResolvingTask.h"
|
|
||||||
#include "modplatform/flame/PackManifest.h"
|
|
||||||
#include "Json.h"
|
|
||||||
|
|
||||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
|
|
||||||
{
|
{
|
||||||
|
m_globalSettings = settings;
|
||||||
m_sourceUrl = sourceUrl;
|
m_sourceUrl = sourceUrl;
|
||||||
|
m_target = target;
|
||||||
|
m_instName = instName;
|
||||||
|
m_instIcon = instIcon;
|
||||||
|
m_instGroup = instGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::executeTask()
|
void InstanceImportTask::executeTask()
|
||||||
@@ -28,7 +28,7 @@ void InstanceImportTask::executeTask()
|
|||||||
if (m_sourceUrl.isLocalFile())
|
if (m_sourceUrl.isLocalFile())
|
||||||
{
|
{
|
||||||
m_archivePath = m_sourceUrl.toLocalFile();
|
m_archivePath = m_sourceUrl.toLocalFile();
|
||||||
processZipPack();
|
extractAndTweak();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -51,14 +51,12 @@ void InstanceImportTask::executeTask()
|
|||||||
|
|
||||||
void InstanceImportTask::downloadSucceeded()
|
void InstanceImportTask::downloadSucceeded()
|
||||||
{
|
{
|
||||||
processZipPack();
|
extractAndTweak();
|
||||||
m_filesNetJob.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::downloadFailed(QString reason)
|
void InstanceImportTask::downloadFailed(QString reason)
|
||||||
{
|
{
|
||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
m_filesNetJob.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||||
@@ -66,47 +64,34 @@ void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
|||||||
setProgress(current / 2, total);
|
setProgress(current / 2, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processZipPack()
|
static QFileInfo findRecursive(const QString &dir, const QString &name)
|
||||||
|
{
|
||||||
|
for (const auto info : QDir(dir).entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files, QDir::DirsLast))
|
||||||
|
{
|
||||||
|
if (info.isFile() && info.fileName() == name)
|
||||||
|
{
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
else if (info.isDir())
|
||||||
|
{
|
||||||
|
const QFileInfo res = findRecursive(info.absoluteFilePath(), name);
|
||||||
|
if (res.isFile() && res.exists())
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QFileInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::extractAndTweak()
|
||||||
{
|
{
|
||||||
setStatus(tr("Extracting modpack"));
|
setStatus(tr("Extracting modpack"));
|
||||||
|
m_stagingPath = m_target->getStagedInstancePath();
|
||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
qDebug() << "Attempting to create instance from" << m_archivePath;
|
qDebug() << "Attempting to create instance from" << m_archivePath;
|
||||||
|
|
||||||
// open the zip and find relevant files in it
|
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, m_archivePath, extractDir.absolutePath());
|
||||||
m_packZip.reset(new QuaZip(m_archivePath));
|
|
||||||
if (!m_packZip->open(QuaZip::mdUnzip))
|
|
||||||
{
|
|
||||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList blacklist = {"instance.cfg", "manifest.json"};
|
|
||||||
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
|
|
||||||
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
|
|
||||||
QString root;
|
|
||||||
if(!mmcFound.isNull())
|
|
||||||
{
|
|
||||||
// process as MultiMC instance/pack
|
|
||||||
qDebug() << "MultiMC:" << mmcFound;
|
|
||||||
root = mmcFound;
|
|
||||||
m_modpackType = ModpackType::MultiMC;
|
|
||||||
}
|
|
||||||
else if(!flameFound.isNull())
|
|
||||||
{
|
|
||||||
// process as Flame pack
|
|
||||||
qDebug() << "Flame:" << flameFound;
|
|
||||||
root = flameFound;
|
|
||||||
m_modpackType = ModpackType::Flame;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_modpackType == ModpackType::Unknown)
|
|
||||||
{
|
|
||||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we extract just the pack
|
|
||||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
|
||||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||||
@@ -114,271 +99,27 @@ void InstanceImportTask::processZipPack()
|
|||||||
|
|
||||||
void InstanceImportTask::extractFinished()
|
void InstanceImportTask::extractFinished()
|
||||||
{
|
{
|
||||||
m_packZip.reset();
|
|
||||||
if (m_extractFuture.result().isEmpty())
|
if (m_extractFuture.result().isEmpty())
|
||||||
{
|
{
|
||||||
|
m_target->destroyStagingPath(m_stagingPath);
|
||||||
emitFailed(tr("Failed to extract modpack"));
|
emitFailed(tr("Failed to extract modpack"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
|
const QFileInfo instanceCfgFile = findRecursive(extractDir.absolutePath(), "instance.cfg");
|
||||||
qDebug() << "Fixing permissions for extracted pack files...";
|
if (!instanceCfgFile.isFile() || !instanceCfgFile.exists())
|
||||||
QDirIterator it(extractDir, QDirIterator::Subdirectories);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
{
|
||||||
auto filepath = it.next();
|
m_target->destroyStagingPath(m_stagingPath);
|
||||||
QFileInfo file(filepath);
|
emitFailed(tr("Archive does not contain instance.cfg"));
|
||||||
auto permissions = QFile::permissions(filepath);
|
|
||||||
auto origPermissions = permissions;
|
|
||||||
if(file.isDir())
|
|
||||||
{
|
|
||||||
// Folder +rwx for current user
|
|
||||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// File +rw for current user
|
|
||||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
|
||||||
}
|
|
||||||
if(origPermissions != permissions)
|
|
||||||
{
|
|
||||||
if(!QFile::setPermissions(filepath, permissions))
|
|
||||||
{
|
|
||||||
logWarning(tr("Could not fix permissions for %1").arg(filepath));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qDebug() << "Fixed" << filepath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(m_modpackType)
|
|
||||||
{
|
|
||||||
case ModpackType::Flame:
|
|
||||||
processFlame();
|
|
||||||
return;
|
|
||||||
case ModpackType::MultiMC:
|
|
||||||
processMultiMC();
|
|
||||||
return;
|
|
||||||
case ModpackType::Unknown:
|
|
||||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::extractAborted()
|
|
||||||
{
|
|
||||||
emitFailed(tr("Instance import has been aborted."));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processFlame()
|
|
||||||
{
|
|
||||||
const static QMap<QString,QString> forgemap = {
|
|
||||||
{"1.2.5", "3.4.9.171"},
|
|
||||||
{"1.4.2", "6.0.1.355"},
|
|
||||||
{"1.4.7", "6.6.2.534"},
|
|
||||||
{"1.5.2", "7.8.1.737"}
|
|
||||||
};
|
|
||||||
Flame::Manifest pack;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
|
|
||||||
Flame::loadManifest(pack, configPath);
|
|
||||||
QFile::remove(configPath);
|
|
||||||
}
|
|
||||||
catch (const JSONValidationError &e)
|
|
||||||
{
|
|
||||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!pack.overrides.isEmpty())
|
|
||||||
{
|
|
||||||
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
|
|
||||||
if (QFile::exists(overridePath))
|
|
||||||
{
|
|
||||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
|
||||||
if (!QFile::rename(overridePath, mcPath))
|
|
||||||
{
|
|
||||||
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString forgeVersion;
|
|
||||||
for(auto &loader: pack.minecraft.modLoaders)
|
|
||||||
{
|
|
||||||
auto id = loader.id;
|
|
||||||
if(id.startsWith("forge-"))
|
|
||||||
{
|
|
||||||
id.remove("forge-");
|
|
||||||
forgeVersion = id;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
instanceSettings->set("InstanceType", "OneSix");
|
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
|
||||||
auto mcVersion = pack.minecraft.version;
|
|
||||||
// Hack to correct some 'special sauce'...
|
|
||||||
if(mcVersion.endsWith('.'))
|
|
||||||
{
|
|
||||||
mcVersion.remove(QRegExp("[.]+$"));
|
|
||||||
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
|
||||||
}
|
|
||||||
auto components = instance.getComponentList();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
|
||||||
if(!forgeVersion.isEmpty())
|
|
||||||
{
|
|
||||||
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
|
||||||
if(forgeVersion == "recommended")
|
|
||||||
{
|
|
||||||
if(forgemap.contains(mcVersion))
|
|
||||||
{
|
|
||||||
forgeVersion = forgemap[mcVersion];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
|
||||||
}
|
|
||||||
if (m_instIcon != "default")
|
|
||||||
{
|
|
||||||
instance.setIconKey(m_instIcon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(pack.name.contains("Direwolf20"))
|
|
||||||
{
|
|
||||||
instance.setIconKey("steve");
|
|
||||||
}
|
|
||||||
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
|
|
||||||
{
|
|
||||||
instance.setIconKey("ftb_logo");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// default to something other than the MultiMC default to distinguish these
|
|
||||||
instance.setIconKey("flame");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
|
||||||
QFileInfo jarmodsInfo(jarmodsPath);
|
|
||||||
if(jarmodsInfo.isDir())
|
|
||||||
{
|
|
||||||
// install all the jar mods
|
|
||||||
qDebug() << "Found jarmods:";
|
|
||||||
QDir jarmodsDir(jarmodsPath);
|
|
||||||
QStringList jarMods;
|
|
||||||
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
|
||||||
{
|
|
||||||
qDebug() << info.fileName();
|
|
||||||
jarMods.push_back(info.absoluteFilePath());
|
|
||||||
}
|
|
||||||
auto profile = instance.getComponentList();
|
|
||||||
profile->installJarMods(jarMods);
|
|
||||||
// nuke the original files
|
|
||||||
FS::deletePath(jarmodsPath);
|
|
||||||
}
|
|
||||||
instance.setName(m_instName);
|
|
||||||
m_modIdResolver.reset(new Flame::FileResolvingTask(pack));
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
|
|
||||||
{
|
|
||||||
auto results = m_modIdResolver->getResults();
|
|
||||||
m_filesNetJob.reset(new NetJob(tr("Mod download")));
|
|
||||||
for(auto result: results.files)
|
|
||||||
{
|
|
||||||
QString filename = result.fileName;
|
|
||||||
if(!result.required)
|
|
||||||
{
|
|
||||||
filename += ".disabled";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
|
||||||
auto path = FS::PathCombine(m_stagingPath , relpath);
|
|
||||||
|
|
||||||
switch(result.type)
|
|
||||||
{
|
|
||||||
case Flame::File::Type::Folder:
|
|
||||||
{
|
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
|
||||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
|
||||||
}
|
|
||||||
case Flame::File::Type::SingleFile:
|
|
||||||
case Flame::File::Type::Mod:
|
|
||||||
{
|
|
||||||
qDebug() << "Will download" << result.url << "to" << path;
|
|
||||||
auto dl = Net::Download::makeFile(result.url, path);
|
|
||||||
m_filesNetJob->addNetAction(dl);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Flame::File::Type::Modpack:
|
|
||||||
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
case Flame::File::Type::Cmod2:
|
|
||||||
case Flame::File::Type::Ctoc:
|
|
||||||
case Flame::File::Type::Unknown:
|
|
||||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_modIdResolver.reset();
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
|
|
||||||
{
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
|
|
||||||
{
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitFailed(reason);
|
|
||||||
});
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
|
||||||
{
|
|
||||||
setProgress(current, total);
|
|
||||||
});
|
|
||||||
setStatus(tr("Downloading mods..."));
|
|
||||||
m_filesNetJob->start();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
|
|
||||||
{
|
|
||||||
m_modIdResolver.reset();
|
|
||||||
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
|
||||||
});
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
|
|
||||||
{
|
|
||||||
setProgress(current, total);
|
|
||||||
});
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
|
|
||||||
{
|
|
||||||
setStatus(status);
|
|
||||||
});
|
|
||||||
m_modIdResolver->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::processMultiMC()
|
|
||||||
{
|
|
||||||
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
|
// FIXME: copy from FolderInstanceProvider!!! FIX IT!!!
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
auto instanceSettings = std::make_shared<INISettingsObject>(instanceCfgFile.absoluteFilePath());
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||||
|
|
||||||
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
QString actualDir = instanceCfgFile.absolutePath();
|
||||||
|
NullInstance instance(m_globalSettings, instanceSettings, actualDir);
|
||||||
|
|
||||||
// reset time played on import... because packs.
|
// reset time played on import... because packs.
|
||||||
instance.resetTimePlayed();
|
instance.resetTimePlayed();
|
||||||
@@ -394,9 +135,8 @@ void InstanceImportTask::processMultiMC()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_instIcon = instance.iconKey();
|
m_instIcon = instance.iconKey();
|
||||||
|
auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png");
|
||||||
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
if (QFile::exists(importIconPath))
|
||||||
if (!importIconPath.isNull() && QFile::exists(importIconPath))
|
|
||||||
{
|
{
|
||||||
// import icon
|
// import icon
|
||||||
auto iconList = ENV.icons();
|
auto iconList = ENV.icons();
|
||||||
@@ -407,5 +147,18 @@ void InstanceImportTask::processMultiMC()
|
|||||||
iconList->installIcons({importIconPath});
|
iconList->installIcons({importIconPath});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!m_target->commitStagedInstance(m_stagingPath, actualDir, m_instName, m_instGroup))
|
||||||
|
{
|
||||||
|
m_target->destroyStagingPath(m_stagingPath);
|
||||||
|
emitFailed(tr("Unable to commit instance"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::extractAborted()
|
||||||
|
{
|
||||||
|
m_target->destroyStagingPath(m_stagingPath);
|
||||||
|
emitFailed(tr("Instance import has been aborted."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,34 +1,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "InstanceTask.h"
|
#include "tasks/Task.h"
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
class QuaZip;
|
class BaseInstanceProvider;
|
||||||
namespace Flame
|
|
||||||
{
|
|
||||||
class FileResolvingTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask
|
class MULTIMC_LOGIC_EXPORT InstanceImportTask : public Task
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceImportTask(const QUrl sourceUrl);
|
explicit InstanceImportTask(SettingsObjectPtr settings, const QUrl sourceUrl, BaseInstanceProvider * target, const QString &instName,
|
||||||
|
const QString &instIcon, const QString &instGroup);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void processZipPack();
|
void extractAndTweak();
|
||||||
void processMultiMC();
|
|
||||||
void processFlame();
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void downloadSucceeded();
|
void downloadSucceeded();
|
||||||
@@ -38,17 +32,16 @@ private slots:
|
|||||||
void extractAborted();
|
void extractAborted();
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
|
SettingsObjectPtr m_globalSettings;
|
||||||
NetJobPtr m_filesNetJob;
|
NetJobPtr m_filesNetJob;
|
||||||
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
|
|
||||||
QUrl m_sourceUrl;
|
QUrl m_sourceUrl;
|
||||||
|
BaseInstanceProvider * m_target;
|
||||||
QString m_archivePath;
|
QString m_archivePath;
|
||||||
bool m_downloadRequired = false;
|
bool m_downloadRequired = false;
|
||||||
std::unique_ptr<QuaZip> m_packZip;
|
QString m_instName;
|
||||||
|
QString m_instIcon;
|
||||||
|
QString m_instGroup;
|
||||||
|
QString m_stagingPath;
|
||||||
QFuture<QStringList> m_extractFuture;
|
QFuture<QStringList> m_extractFuture;
|
||||||
QFutureWatcher<QStringList> m_extractFutureWatcher;
|
QFutureWatcher<QStringList> m_extractFutureWatcher;
|
||||||
enum class ModpackType{
|
|
||||||
Unknown,
|
|
||||||
MultiMC,
|
|
||||||
Flame
|
|
||||||
} m_modpackType = ModpackType::Unknown;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,49 +14,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
#include <QTimer>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
#include <QUuid>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
|
|
||||||
#include "InstanceList.h"
|
#include "InstanceList.h"
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "InstanceTask.h"
|
|
||||||
#include "settings/INISettingsObject.h"
|
|
||||||
#include "minecraft/legacy/LegacyInstance.h"
|
|
||||||
#include "NullInstance.h"
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "ExponentialSeries.h"
|
|
||||||
#include "WatchLock.h"
|
|
||||||
|
|
||||||
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
#include "FolderInstanceProvider.h"
|
||||||
|
|
||||||
InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent)
|
InstanceList::InstanceList(SettingsObjectPtr globalSettings, const QString &instDir, QObject *parent)
|
||||||
: QAbstractListModel(parent), m_globalSettings(settings)
|
: QAbstractListModel(parent), m_instDir(instDir)
|
||||||
{
|
{
|
||||||
|
m_globalSettings = globalSettings;
|
||||||
resumeWatch();
|
resumeWatch();
|
||||||
// Create aand normalize path
|
|
||||||
if (!QDir::current().exists(instDir))
|
|
||||||
{
|
|
||||||
QDir::current().mkpath(instDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(this, &InstanceList::instancesChanged, this, &InstanceList::providerUpdated);
|
|
||||||
|
|
||||||
// NOTE: canonicalPath requires the path to exist. Do not move this above the creation block!
|
|
||||||
m_instDir = QDir(instDir).canonicalPath();
|
|
||||||
m_watcher = new QFileSystemWatcher(this);
|
|
||||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &InstanceList::instanceDirContentsChanged);
|
|
||||||
m_watcher->addPath(m_instDir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceList::~InstanceList()
|
InstanceList::~InstanceList()
|
||||||
@@ -95,7 +69,6 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
|
|||||||
{
|
{
|
||||||
return pdata->id();
|
return pdata->id();
|
||||||
}
|
}
|
||||||
case Qt::EditRole:
|
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
{
|
{
|
||||||
return pdata->name();
|
return pdata->name();
|
||||||
@@ -111,7 +84,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
|
|||||||
// HACK: see GroupView.h in gui!
|
// HACK: see GroupView.h in gui!
|
||||||
case GroupRole:
|
case GroupRole:
|
||||||
{
|
{
|
||||||
return getInstanceGroup(pdata->id());
|
return pdata->group();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -119,85 +92,16 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(role != Qt::EditRole)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
|
|
||||||
auto newName = value.toString();
|
|
||||||
if(pdata->name() == newName)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
pdata->setName(newName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
|
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
Qt::ItemFlags f;
|
Qt::ItemFlags f;
|
||||||
if (index.isValid())
|
if (index.isValid())
|
||||||
{
|
{
|
||||||
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||||
}
|
}
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
|
||||||
{
|
|
||||||
auto inst = getInstanceById(id);
|
|
||||||
if(!inst)
|
|
||||||
{
|
|
||||||
return GroupId();
|
|
||||||
}
|
|
||||||
auto iter = m_groupMap.find(inst->id());
|
|
||||||
if(iter != m_groupMap.end())
|
|
||||||
{
|
|
||||||
return *iter;
|
|
||||||
}
|
|
||||||
return GroupId();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
|
||||||
{
|
|
||||||
auto inst = getInstanceById(id);
|
|
||||||
if(!inst)
|
|
||||||
{
|
|
||||||
qDebug() << "Attempt to set a null instance's group";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
auto iter = m_groupMap.find(inst->id());
|
|
||||||
if(iter != m_groupMap.end())
|
|
||||||
{
|
|
||||||
if(*iter != name)
|
|
||||||
{
|
|
||||||
*iter = name;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
changed = true;
|
|
||||||
m_groupMap[id] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(changed)
|
|
||||||
{
|
|
||||||
m_groups.insert(name);
|
|
||||||
auto idx = getInstIndex(inst.get());
|
|
||||||
emit dataChanged(index(idx), index(idx), {GroupRole});
|
|
||||||
saveGroupList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList InstanceList::getGroups()
|
QStringList InstanceList::getGroups()
|
||||||
{
|
{
|
||||||
return m_groups.toList();
|
return m_groups.toList();
|
||||||
@@ -205,53 +109,15 @@ QStringList InstanceList::getGroups()
|
|||||||
|
|
||||||
void InstanceList::deleteGroup(const QString& name)
|
void InstanceList::deleteGroup(const QString& name)
|
||||||
{
|
{
|
||||||
bool removed = false;
|
|
||||||
qDebug() << "Delete group" << name;
|
|
||||||
for(auto & instance: m_instances)
|
for(auto & instance: m_instances)
|
||||||
{
|
{
|
||||||
const auto & instID = instance->id();
|
auto instGroupName = instance->group();
|
||||||
auto instGroupName = getInstanceGroup(instID);
|
|
||||||
if(instGroupName == name)
|
if(instGroupName == name)
|
||||||
{
|
{
|
||||||
m_groupMap.remove(instID);
|
instance->setGroupPost(QString());
|
||||||
qDebug() << "Remove" << instID << "from group" << name;
|
|
||||||
removed = true;
|
|
||||||
auto idx = getInstIndex(instance.get());
|
|
||||||
if(idx > 0)
|
|
||||||
{
|
|
||||||
emit dataChanged(index(idx), index(idx), {GroupRole});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(removed)
|
|
||||||
{
|
|
||||||
saveGroupList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceList::deleteInstance(const InstanceId& id)
|
|
||||||
{
|
|
||||||
auto inst = getInstanceById(id);
|
|
||||||
if(!inst)
|
|
||||||
{
|
|
||||||
qDebug() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?).";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_groupMap.remove(id))
|
|
||||||
{
|
|
||||||
saveGroupList();
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "Will delete instance" << id;
|
|
||||||
if(!FS::deletePath(inst->instanceRoot()))
|
|
||||||
{
|
|
||||||
qWarning() << "Deletion of instance" << id << "has not been completely successful ...";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "Instance" << id << "has been deleted by MultiMC.";
|
|
||||||
}
|
|
||||||
|
|
||||||
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
|
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
|
||||||
{
|
{
|
||||||
@@ -270,60 +136,50 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList< InstanceId > InstanceList::discoverInstances()
|
InstanceList::InstListError InstanceList::loadList(bool complete)
|
||||||
{
|
|
||||||
qDebug() << "Discovering instances in" << m_instDir;
|
|
||||||
QList<InstanceId> out;
|
|
||||||
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
|
|
||||||
while (iter.hasNext())
|
|
||||||
{
|
|
||||||
QString subDir = iter.next();
|
|
||||||
QFileInfo dirInfo(subDir);
|
|
||||||
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
|
|
||||||
continue;
|
|
||||||
// if it is a symlink, ignore it if it goes to the instance folder
|
|
||||||
if(dirInfo.isSymLink())
|
|
||||||
{
|
|
||||||
QFileInfo targetInfo(dirInfo.symLinkTarget());
|
|
||||||
QFileInfo instDirInfo(m_instDir);
|
|
||||||
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
|
|
||||||
{
|
|
||||||
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto id = dirInfo.fileName();
|
|
||||||
out.append(id);
|
|
||||||
qDebug() << "Found instance ID" << id;
|
|
||||||
}
|
|
||||||
instanceSet = out.toSet();
|
|
||||||
m_instancesProbed = true;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceList::InstListError InstanceList::loadList()
|
|
||||||
{
|
{
|
||||||
auto existingIds = getIdMapping(m_instances);
|
auto existingIds = getIdMapping(m_instances);
|
||||||
|
|
||||||
QList<InstancePtr> newList;
|
QList<InstancePtr> newList;
|
||||||
|
|
||||||
for(auto & id: discoverInstances())
|
auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids)
|
||||||
|
{
|
||||||
|
for(auto & id: ids)
|
||||||
{
|
{
|
||||||
if(existingIds.contains(id))
|
if(existingIds.contains(id))
|
||||||
{
|
{
|
||||||
auto instPair = existingIds[id];
|
auto instPair = existingIds[id];
|
||||||
|
/*
|
||||||
|
auto & instPtr = instPair.first;
|
||||||
|
auto & instIdx = instPair.second;
|
||||||
|
*/
|
||||||
existingIds.remove(id);
|
existingIds.remove(id);
|
||||||
qDebug() << "Should keep and soft-reload" << id;
|
qDebug() << "Should keep and soft-reload" << id;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InstancePtr instPtr = loadInstance(id);
|
InstancePtr instPtr = provider->loadInstance(id);
|
||||||
if(instPtr)
|
if(instPtr)
|
||||||
{
|
{
|
||||||
newList.append(instPtr);
|
newList.append(instPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if(complete)
|
||||||
|
{
|
||||||
|
for(auto & item: m_providers)
|
||||||
|
{
|
||||||
|
processIds(item.get(), item->discoverInstances());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto & item: m_updatedProviders)
|
||||||
|
{
|
||||||
|
processIds(item, item->discoverInstances());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
|
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
|
||||||
if(!existingIds.isEmpty())
|
if(!existingIds.isEmpty())
|
||||||
@@ -350,6 +206,10 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
for(auto & removedItem: deadList)
|
for(auto & removedItem: deadList)
|
||||||
{
|
{
|
||||||
auto instPtr = removedItem.first;
|
auto instPtr = removedItem.first;
|
||||||
|
if(!complete && !m_updatedProviders.contains(instPtr->provider()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
instPtr->invalidate();
|
instPtr->invalidate();
|
||||||
currentItem = removedItem.second;
|
currentItem = removedItem.second;
|
||||||
if(back_bookmark == -1)
|
if(back_bookmark == -1)
|
||||||
@@ -377,18 +237,10 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
{
|
{
|
||||||
add(newList);
|
add(newList);
|
||||||
}
|
}
|
||||||
m_dirty = false;
|
m_updatedProviders.clear();
|
||||||
return NoError;
|
return NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceList::saveNow()
|
|
||||||
{
|
|
||||||
for(auto & item: m_instances)
|
|
||||||
{
|
|
||||||
item->saveNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceList::add(const QList<InstancePtr> &t)
|
void InstanceList::add(const QList<InstancePtr> &t)
|
||||||
{
|
{
|
||||||
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
|
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
|
||||||
@@ -408,7 +260,7 @@ void InstanceList::resumeWatch()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_watchLevel++;
|
m_watchLevel++;
|
||||||
if(m_watchLevel > 0 && m_dirty)
|
if(m_watchLevel > 0 && !m_updatedProviders.isEmpty())
|
||||||
{
|
{
|
||||||
loadList();
|
loadList();
|
||||||
}
|
}
|
||||||
@@ -421,13 +273,31 @@ void InstanceList::suspendWatch()
|
|||||||
|
|
||||||
void InstanceList::providerUpdated()
|
void InstanceList::providerUpdated()
|
||||||
{
|
{
|
||||||
m_dirty = true;
|
auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender());
|
||||||
|
if(!provider)
|
||||||
|
{
|
||||||
|
qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_updatedProviders.insert(provider);
|
||||||
if(m_watchLevel == 1)
|
if(m_watchLevel == 1)
|
||||||
{
|
{
|
||||||
loadList();
|
loadList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceList::groupsPublished(QSet<QString> newGroups)
|
||||||
|
{
|
||||||
|
m_groups.unite(newGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceList::addInstanceProvider(BaseInstanceProvider* provider)
|
||||||
|
{
|
||||||
|
connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
|
||||||
|
connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
|
||||||
|
m_providers.append(provider);
|
||||||
|
}
|
||||||
|
|
||||||
InstancePtr InstanceList::getInstanceById(QString instId) const
|
InstancePtr InstanceList::getInstanceById(QString instId) const
|
||||||
{
|
{
|
||||||
if(instId.isEmpty())
|
if(instId.isEmpty())
|
||||||
@@ -469,365 +339,4 @@ void InstanceList::propertiesChanged(BaseInstance *inst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
|
||||||
{
|
|
||||||
if(!m_groupsLoaded)
|
|
||||||
{
|
|
||||||
loadGroupList();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto instanceRoot = FS::PathCombine(m_instDir, id);
|
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
|
|
||||||
InstancePtr inst;
|
|
||||||
|
|
||||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
|
||||||
|
|
||||||
QString inst_type = instanceSettings->get("InstanceType").toString();
|
|
||||||
|
|
||||||
if (inst_type == "OneSix" || inst_type == "Nostalgia")
|
|
||||||
{
|
|
||||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
|
||||||
}
|
|
||||||
else if (inst_type == "Legacy")
|
|
||||||
{
|
|
||||||
inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instanceRoot));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
|
|
||||||
}
|
|
||||||
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
|
|
||||||
return inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceList::saveGroupList()
|
|
||||||
{
|
|
||||||
qDebug() << "Will save group list now.";
|
|
||||||
if(!m_instancesProbed)
|
|
||||||
{
|
|
||||||
qDebug() << "Group saving prevented because we don't know the full list of instances yet.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
WatchLock foo(m_watcher, m_instDir);
|
|
||||||
QString groupFileName = m_instDir + "/instgroups.json";
|
|
||||||
QMap<QString, QSet<QString>> reverseGroupMap;
|
|
||||||
for (auto iter = m_groupMap.begin(); iter != m_groupMap.end(); iter++)
|
|
||||||
{
|
|
||||||
QString id = iter.key();
|
|
||||||
QString group = iter.value();
|
|
||||||
if (group.isEmpty())
|
|
||||||
continue;
|
|
||||||
if(!instanceSet.contains(id))
|
|
||||||
{
|
|
||||||
qDebug() << "Skipping saving missing instance" << id << "to groups list.";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!reverseGroupMap.count(group))
|
|
||||||
{
|
|
||||||
QSet<QString> set;
|
|
||||||
set.insert(id);
|
|
||||||
reverseGroupMap[group] = set;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QSet<QString> &set = reverseGroupMap[group];
|
|
||||||
set.insert(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QJsonObject toplevel;
|
|
||||||
toplevel.insert("formatVersion", QJsonValue(QString("1")));
|
|
||||||
QJsonObject groupsArr;
|
|
||||||
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
|
|
||||||
{
|
|
||||||
auto list = iter.value();
|
|
||||||
auto name = iter.key();
|
|
||||||
QJsonObject groupObj;
|
|
||||||
QJsonArray instanceArr;
|
|
||||||
groupObj.insert("hidden", QJsonValue(QString("false")));
|
|
||||||
for (auto item : list)
|
|
||||||
{
|
|
||||||
instanceArr.append(QJsonValue(item));
|
|
||||||
}
|
|
||||||
groupObj.insert("instances", instanceArr);
|
|
||||||
groupsArr.insert(name, groupObj);
|
|
||||||
}
|
|
||||||
toplevel.insert("groups", groupsArr);
|
|
||||||
QJsonDocument doc(toplevel);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FS::write(groupFileName, doc.toJson());
|
|
||||||
qDebug() << "Group list saved.";
|
|
||||||
}
|
|
||||||
catch (const FS::FileSystemException &e)
|
|
||||||
{
|
|
||||||
qCritical() << "Failed to write instance group file :" << e.cause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceList::loadGroupList()
|
|
||||||
{
|
|
||||||
qDebug() << "Will load group list now.";
|
|
||||||
QSet<QString> groupSet;
|
|
||||||
|
|
||||||
QString groupFileName = m_instDir + "/instgroups.json";
|
|
||||||
|
|
||||||
// if there's no group file, fail
|
|
||||||
if (!QFileInfo(groupFileName).exists())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QByteArray jsonData;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
jsonData = FS::read(groupFileName);
|
|
||||||
}
|
|
||||||
catch (const FS::FileSystemException &e)
|
|
||||||
{
|
|
||||||
qCritical() << "Failed to read instance group file :" << e.cause();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
|
|
||||||
|
|
||||||
// if the json was bad, fail
|
|
||||||
if (error.error != QJsonParseError::NoError)
|
|
||||||
{
|
|
||||||
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
|
|
||||||
.arg(error.errorString(), QString::number(error.offset))
|
|
||||||
.toUtf8();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the root of the json wasn't an object, fail
|
|
||||||
if (!jsonDoc.isObject())
|
|
||||||
{
|
|
||||||
qWarning() << "Invalid group file. Root entry should be an object.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject rootObj = jsonDoc.object();
|
|
||||||
|
|
||||||
// Make sure the format version matches, otherwise fail.
|
|
||||||
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get the groups. if it's not an object, fail
|
|
||||||
if (!rootObj.value("groups").isObject())
|
|
||||||
{
|
|
||||||
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_groupMap.clear();
|
|
||||||
|
|
||||||
// Iterate through all the groups.
|
|
||||||
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
|
||||||
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
|
|
||||||
{
|
|
||||||
QString groupName = iter.key();
|
|
||||||
|
|
||||||
// If not an object, complain and skip to the next one.
|
|
||||||
if (!iter.value().isObject())
|
|
||||||
{
|
|
||||||
qWarning() << QString("Group '%1' in the group list should "
|
|
||||||
"be an object.")
|
|
||||||
.arg(groupName)
|
|
||||||
.toUtf8();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject groupObj = iter.value().toObject();
|
|
||||||
if (!groupObj.value("instances").isArray())
|
|
||||||
{
|
|
||||||
qWarning() << QString("Group '%1' in the group list is invalid. "
|
|
||||||
"It should contain an array "
|
|
||||||
"called 'instances'.")
|
|
||||||
.arg(groupName)
|
|
||||||
.toUtf8();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep a list/set of groups for choosing
|
|
||||||
groupSet.insert(groupName);
|
|
||||||
|
|
||||||
// Iterate through the list of instances in the group.
|
|
||||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
|
||||||
|
|
||||||
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++)
|
|
||||||
{
|
|
||||||
m_groupMap[(*iter2).toString()] = groupName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_groupsLoaded = true;
|
|
||||||
m_groups.unite(groupSet);
|
|
||||||
qDebug() << "Group list loaded.";
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceList::instanceDirContentsChanged(const QString& path)
|
|
||||||
{
|
|
||||||
Q_UNUSED(path);
|
|
||||||
emit instancesChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value)
|
|
||||||
{
|
|
||||||
QString newInstDir = QDir(value.toString()).canonicalPath();
|
|
||||||
if(newInstDir != m_instDir)
|
|
||||||
{
|
|
||||||
if(m_groupsLoaded)
|
|
||||||
{
|
|
||||||
saveGroupList();
|
|
||||||
}
|
|
||||||
m_instDir = newInstDir;
|
|
||||||
m_groupsLoaded = false;
|
|
||||||
emit instancesChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InstanceStaging : public Task
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
const unsigned minBackoff = 1;
|
|
||||||
const unsigned maxBackoff = 16;
|
|
||||||
public:
|
|
||||||
InstanceStaging (
|
|
||||||
InstanceList * parent,
|
|
||||||
Task * child,
|
|
||||||
const QString & stagingPath,
|
|
||||||
const QString& instanceName,
|
|
||||||
const QString& groupName )
|
|
||||||
: backoff(minBackoff, maxBackoff)
|
|
||||||
{
|
|
||||||
m_parent = parent;
|
|
||||||
m_child.reset(child);
|
|
||||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
|
|
||||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
|
||||||
connect(child, &Task::status, this, &InstanceStaging::setStatus);
|
|
||||||
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
|
||||||
m_instanceName = instanceName;
|
|
||||||
m_groupName = groupName;
|
|
||||||
m_stagingPath = stagingPath;
|
|
||||||
m_backoffTimer.setSingleShot(true);
|
|
||||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~InstanceStaging() {};
|
|
||||||
|
|
||||||
|
|
||||||
// FIXME/TODO: add ability to abort during instance commit retries
|
|
||||||
bool abort() override
|
|
||||||
{
|
|
||||||
if(m_child && m_child->canAbort())
|
|
||||||
{
|
|
||||||
return m_child->abort();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool canAbort() const override
|
|
||||||
{
|
|
||||||
if(m_child && m_child->canAbort())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void executeTask() override
|
|
||||||
{
|
|
||||||
m_child->start();
|
|
||||||
}
|
|
||||||
QStringList warnings() const override
|
|
||||||
{
|
|
||||||
return m_child->warnings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void childSucceded()
|
|
||||||
{
|
|
||||||
unsigned sleepTime = backoff();
|
|
||||||
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
|
|
||||||
{
|
|
||||||
emitSucceeded();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// we actually failed, retry?
|
|
||||||
if(sleepTime == maxBackoff)
|
|
||||||
{
|
|
||||||
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
|
|
||||||
m_backoffTimer.start(sleepTime * 500);
|
|
||||||
}
|
|
||||||
void childFailed(const QString & reason)
|
|
||||||
{
|
|
||||||
m_parent->destroyStagingPath(m_stagingPath);
|
|
||||||
emitFailed(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*
|
|
||||||
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
|
|
||||||
* Basically, it starts messing things up while MultiMC is extracting/creating instances
|
|
||||||
* and causes that horrible failure that is NTFS to lock files in place because they are open.
|
|
||||||
*/
|
|
||||||
ExponentialSeries backoff;
|
|
||||||
QString m_stagingPath;
|
|
||||||
InstanceList * m_parent;
|
|
||||||
unique_qobject_ptr<Task> m_child;
|
|
||||||
QString m_instanceName;
|
|
||||||
QString m_groupName;
|
|
||||||
QTimer m_backoffTimer;
|
|
||||||
};
|
|
||||||
|
|
||||||
Task * InstanceList::wrapInstanceTask(InstanceTask * task)
|
|
||||||
{
|
|
||||||
auto stagingPath = getStagedInstancePath();
|
|
||||||
task->setStagingPath(stagingPath);
|
|
||||||
task->setParentSettings(m_globalSettings);
|
|
||||||
return new InstanceStaging(this, task, stagingPath, task->name(), task->group());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString InstanceList::getStagedInstancePath()
|
|
||||||
{
|
|
||||||
QString key = QUuid::createUuid().toString();
|
|
||||||
QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
|
|
||||||
QDir rootPath(m_instDir);
|
|
||||||
auto path = FS::PathCombine(m_instDir, relPath);
|
|
||||||
if(!rootPath.mkpath(relPath))
|
|
||||||
{
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceList::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName)
|
|
||||||
{
|
|
||||||
QDir dir;
|
|
||||||
QString instID = FS::DirNameFromString(instanceName, m_instDir);
|
|
||||||
{
|
|
||||||
WatchLock lock(m_watcher, m_instDir);
|
|
||||||
QString destination = FS::PathCombine(m_instDir, instID);
|
|
||||||
if(!dir.rename(path, destination))
|
|
||||||
{
|
|
||||||
qWarning() << "Failed to move" << path << "to" << destination;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_groupMap[instID] = groupName;
|
|
||||||
instanceSet.insert(instID);
|
|
||||||
m_groups.insert(groupName);
|
|
||||||
emit instancesChanged();
|
|
||||||
emit instanceSelectRequest(instID);
|
|
||||||
}
|
|
||||||
saveGroupList();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InstanceList::destroyStagingPath(const QString& keyPath)
|
|
||||||
{
|
|
||||||
return FS::deletePath(keyPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "InstanceList.moc"
|
#include "InstanceList.moc"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -21,49 +21,29 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
#include "BaseInstanceProvider.h"
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
class QFileSystemWatcher;
|
||||||
class InstanceTask;
|
class BaseInstance;
|
||||||
using InstanceId = QString;
|
class QDir;
|
||||||
using GroupId = QString;
|
|
||||||
using InstanceLocator = std::pair<InstancePtr, int>;
|
|
||||||
|
|
||||||
enum class InstCreateError
|
|
||||||
{
|
|
||||||
NoCreateError = 0,
|
|
||||||
NoSuchVersion,
|
|
||||||
UnknownCreateError,
|
|
||||||
InstExists,
|
|
||||||
CantCreateDir
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GroupsState
|
|
||||||
{
|
|
||||||
NotLoaded,
|
|
||||||
Steady,
|
|
||||||
Dirty
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel
|
class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0);
|
explicit InstanceList(SettingsObjectPtr globalSettings, const QString &instDir, QObject *parent = 0);
|
||||||
virtual ~InstanceList();
|
virtual ~InstanceList();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
|
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
|
||||||
bool setData(const QModelIndex & index, const QVariant & value, int role) override;
|
|
||||||
|
|
||||||
enum AdditionalRoles
|
enum AdditionalRoles
|
||||||
{
|
{
|
||||||
@@ -92,75 +72,37 @@ public:
|
|||||||
return m_instances.count();
|
return m_instances.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
InstListError loadList();
|
InstListError loadList(bool complete = false);
|
||||||
void saveNow();
|
|
||||||
|
|
||||||
|
/// Add an instance provider. Takes ownership of it. Should only be done before the first load.
|
||||||
|
void addInstanceProvider(BaseInstanceProvider * provider);
|
||||||
|
|
||||||
InstancePtr getInstanceById(QString id) const;
|
InstancePtr getInstanceById(QString id) const;
|
||||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||||
QStringList getGroups();
|
QStringList getGroups();
|
||||||
GroupId getInstanceGroup(const InstanceId & id) const;
|
|
||||||
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
|
||||||
|
|
||||||
void deleteGroup(const GroupId & name);
|
void deleteGroup(const QString & name);
|
||||||
void deleteInstance(const InstanceId & id);
|
|
||||||
|
|
||||||
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
|
||||||
Task * wrapInstanceTask(InstanceTask * task);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
|
|
||||||
* Used by instance manipulation tasks.
|
|
||||||
*/
|
|
||||||
QString getStagedInstancePath();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
|
||||||
* Used by instance manipulation tasks.
|
|
||||||
*/
|
|
||||||
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
|
||||||
* Used by instance manipulation tasks.
|
|
||||||
*/
|
|
||||||
bool destroyStagingPath(const QString & keyPath);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dataIsInvalid();
|
void dataIsInvalid();
|
||||||
void instancesChanged();
|
|
||||||
void instanceSelectRequest(QString instanceId);
|
|
||||||
void groupsChanged(QSet<QString> groups);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void on_InstFolderChanged(const Setting &setting, QVariant value);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void propertiesChanged(BaseInstance *inst);
|
void propertiesChanged(BaseInstance *inst);
|
||||||
|
void groupsPublished(QSet<QString>);
|
||||||
void providerUpdated();
|
void providerUpdated();
|
||||||
void instanceDirContentsChanged(const QString &path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int getInstIndex(BaseInstance *inst) const;
|
int getInstIndex(BaseInstance *inst) const;
|
||||||
void suspendWatch();
|
void suspendWatch();
|
||||||
void resumeWatch();
|
void resumeWatch();
|
||||||
void add(const QList<InstancePtr> &list);
|
void add(const QList<InstancePtr> &list);
|
||||||
void loadGroupList();
|
|
||||||
void saveGroupList();
|
|
||||||
QList<InstanceId> discoverInstances();
|
|
||||||
InstancePtr loadInstance(const InstanceId& id);
|
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
int m_watchLevel = 0;
|
int m_watchLevel = 0;
|
||||||
bool m_dirty = false;
|
QSet<BaseInstanceProvider *> m_updatedProviders;
|
||||||
|
QString m_instDir;
|
||||||
QList<InstancePtr> m_instances;
|
QList<InstancePtr> m_instances;
|
||||||
QSet<QString> m_groups;
|
QSet<QString> m_groups;
|
||||||
|
|
||||||
SettingsObjectPtr m_globalSettings;
|
SettingsObjectPtr m_globalSettings;
|
||||||
QString m_instDir;
|
QVector<shared_qobject_ptr<BaseInstanceProvider>> m_providers;
|
||||||
QFileSystemWatcher * m_watcher;
|
|
||||||
QMap<InstanceId, GroupId> m_groupMap;
|
|
||||||
QSet<InstanceId> instanceSet;
|
|
||||||
bool m_groupsLoaded = false;
|
|
||||||
bool m_instancesProbed = false;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
#include "InstanceTask.h"
|
|
||||||
|
|
||||||
InstanceTask::InstanceTask()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceTask::~InstanceTask()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "multimc_logic_export.h"
|
|
||||||
#include "settings/SettingsObject.h"
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT InstanceTask : public Task
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit InstanceTask();
|
|
||||||
virtual ~InstanceTask();
|
|
||||||
|
|
||||||
void setParentSettings(SettingsObjectPtr settings)
|
|
||||||
{
|
|
||||||
m_globalSettings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setStagingPath(const QString &stagingPath)
|
|
||||||
{
|
|
||||||
m_stagingPath = stagingPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setName(const QString &name)
|
|
||||||
{
|
|
||||||
m_instName = name;
|
|
||||||
}
|
|
||||||
QString name() const
|
|
||||||
{
|
|
||||||
return m_instName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setIcon(const QString &icon)
|
|
||||||
{
|
|
||||||
m_instIcon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGroup(const QString &group)
|
|
||||||
{
|
|
||||||
m_instGroup = group;
|
|
||||||
}
|
|
||||||
QString group() const
|
|
||||||
{
|
|
||||||
return m_instGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected: /* data */
|
|
||||||
SettingsObjectPtr m_globalSettings;
|
|
||||||
QString m_instName;
|
|
||||||
QString m_instIcon;
|
|
||||||
QString m_instGroup;
|
|
||||||
QString m_stagingPath;
|
|
||||||
};
|
|
||||||
@@ -123,7 +123,7 @@ T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &w
|
|||||||
{
|
{
|
||||||
return requireIsType<T>(value, what);
|
return requireIsType<T>(value, what);
|
||||||
}
|
}
|
||||||
catch (const JsonException &)
|
catch (JsonException &)
|
||||||
{
|
{
|
||||||
return default_;
|
return default_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,29 +1,172 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/*
|
||||||
*
|
Copyright (C) 2010 Roberto Pompermaier
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
Copyright (C) 2005-2014 Sergey A. Tachenov
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
Parts of this file were part of QuaZIP.
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
QuaZIP is free software: you can redistribute it and/or modify
|
||||||
*
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
the Free Software Foundation, either version 2.1 of the License, or
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
(at your option) any later version.
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
QuaZIP is distributed in the hope that it will be useful,
|
||||||
* limitations under the License.
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with QuaZIP. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
See COPYING file for the full LGPL text.
|
||||||
|
|
||||||
|
Original ZIP package is copyrighted by Gilles Vollant and contributors,
|
||||||
|
see quazip/(un)MMCZip.h files for details. Basically it's the zlib license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <quazip.h>
|
#include <quazip.h>
|
||||||
#include <quazipdir.h>
|
|
||||||
#include <quazipfile.h>
|
|
||||||
#include <JlCompress.h>
|
#include <JlCompress.h>
|
||||||
|
#include <quazipdir.h>
|
||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
// ours
|
bool copyData(QIODevice &inFile, QIODevice &outFile)
|
||||||
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const JlCompress::FilterFunction filter)
|
{
|
||||||
|
while (!inFile.atEnd())
|
||||||
|
{
|
||||||
|
char buf[4096];
|
||||||
|
qint64 readLen = inFile.read(buf, 4096);
|
||||||
|
if (readLen <= 0)
|
||||||
|
return false;
|
||||||
|
if (outFile.write(buf, readLen) != readLen)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MMCZip::extractDir(QString fileCompressed, QString dir)
|
||||||
|
{
|
||||||
|
return JlCompress::extractDir(fileCompressed, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compressFile(QuaZip *zip, QString fileName, QString fileDest)
|
||||||
|
{
|
||||||
|
if (!zip)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend &&
|
||||||
|
zip->getMode() != QuaZip::mdAdd)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile inFile;
|
||||||
|
inFile.setFileName(fileName);
|
||||||
|
if (!inFile.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QuaZipFile outFile(zip);
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copyData(inFile, outFile) || outFile.getZipError() != UNZ_OK)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile.close();
|
||||||
|
if (outFile.getZipError() != UNZ_OK)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inFile.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMCZip::compressSubDir(QuaZip* zip, QString dir, QString origDir, QSet<QString>& added, QString prefix, const SeparatorPrefixTree <'/'> * blacklist)
|
||||||
|
{
|
||||||
|
if (!zip) return false;
|
||||||
|
if (zip->getMode()!=QuaZip::mdCreate && zip->getMode()!=QuaZip::mdAppend && zip->getMode()!=QuaZip::mdAdd)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir directory(dir);
|
||||||
|
if (!directory.exists())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir origDirectory(origDir);
|
||||||
|
if (dir != origDir)
|
||||||
|
{
|
||||||
|
QString internalDirName = origDirectory.relativeFilePath(dir);
|
||||||
|
if(!blacklist || !blacklist->covers(internalDirName))
|
||||||
|
{
|
||||||
|
QuaZipFile dirZipFile(zip);
|
||||||
|
auto dirPrefix = FS::PathCombine(prefix, origDirectory.relativeFilePath(dir)) + "/";
|
||||||
|
if (!dirZipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(dirPrefix, dir), 0, 0, 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dirZipFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfoList files = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden);
|
||||||
|
for (auto file: files)
|
||||||
|
{
|
||||||
|
if(!file.isDir())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!compressSubDir(zip,file.absoluteFilePath(),origDir, added, prefix, blacklist))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files = directory.entryInfoList(QDir::Files);
|
||||||
|
for (auto file: files)
|
||||||
|
{
|
||||||
|
if(!file.isFile())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file.absoluteFilePath()==zip->getZipName())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString filename = origDirectory.relativeFilePath(file.absoluteFilePath());
|
||||||
|
if(blacklist && blacklist->covers(filename))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(prefix.size())
|
||||||
|
{
|
||||||
|
filename = FS::PathCombine(prefix, filename);
|
||||||
|
}
|
||||||
|
added.insert(filename);
|
||||||
|
if (!compressFile(zip,file.absoluteFilePath(),filename))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
|
||||||
|
std::function<bool(QString)> filter)
|
||||||
{
|
{
|
||||||
QuaZip modZip(from.filePath());
|
QuaZip modZip(from.filePath());
|
||||||
modZip.open(QuaZip::mdUnzip);
|
modZip.open(QuaZip::mdUnzip);
|
||||||
@@ -33,7 +176,7 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
|
|||||||
for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
|
for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
|
||||||
{
|
{
|
||||||
QString filename = modZip.getCurrentFileName();
|
QString filename = modZip.getCurrentFileName();
|
||||||
if (filter && !filter(filename))
|
if (!filter(filename))
|
||||||
{
|
{
|
||||||
qDebug() << "Skipping file " << filename << " from "
|
qDebug() << "Skipping file " << filename << " from "
|
||||||
<< from.fileName() << " - filtered";
|
<< from.fileName() << " - filtered";
|
||||||
@@ -61,7 +204,7 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
|
|||||||
fileInsideMod.close();
|
fileInsideMod.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!JlCompress::copyData(fileInsideMod, zipOutFile))
|
if (!copyData(fileInsideMod, zipOutFile))
|
||||||
{
|
{
|
||||||
zipOutFile.close();
|
zipOutFile.close();
|
||||||
fileInsideMod.close();
|
fileInsideMod.close();
|
||||||
@@ -74,7 +217,6 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
|
||||||
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
|
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
|
||||||
{
|
{
|
||||||
QuaZip zipOut(targetJarPath);
|
QuaZip zipOut(targetJarPath);
|
||||||
@@ -99,7 +241,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
continue;
|
continue;
|
||||||
if (mod.type() == Mod::MOD_ZIPFILE)
|
if (mod.type() == Mod::MOD_ZIPFILE)
|
||||||
{
|
{
|
||||||
if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
|
if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles, noFilter))
|
||||||
{
|
{
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
QFile::remove(targetJarPath);
|
||||||
@@ -109,9 +251,9 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
}
|
}
|
||||||
else if (mod.type() == Mod::MOD_SINGLEFILE)
|
else if (mod.type() == Mod::MOD_SINGLEFILE)
|
||||||
{
|
{
|
||||||
// FIXME: buggy - does not work with addedFiles
|
|
||||||
auto filename = mod.filename();
|
auto filename = mod.filename();
|
||||||
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
|
if (!compressFile(&zipOut, filename.absoluteFilePath(),
|
||||||
|
filename.fileName()))
|
||||||
{
|
{
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
QFile::remove(targetJarPath);
|
||||||
@@ -122,13 +264,12 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
}
|
}
|
||||||
else if (mod.type() == Mod::MOD_FOLDER)
|
else if (mod.type() == Mod::MOD_FOLDER)
|
||||||
{
|
{
|
||||||
// FIXME: buggy - does not work with addedFiles
|
|
||||||
auto filename = mod.filename();
|
auto filename = mod.filename();
|
||||||
QString what_to_zip = filename.absoluteFilePath();
|
QString what_to_zip = filename.absoluteFilePath();
|
||||||
QDir dir(what_to_zip);
|
QDir dir(what_to_zip);
|
||||||
dir.cdUp();
|
dir.cdUp();
|
||||||
QString parent_dir = dir.absolutePath();
|
QString parent_dir = dir.absolutePath();
|
||||||
if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
|
if (!compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles))
|
||||||
{
|
{
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
QFile::remove(targetJarPath);
|
||||||
@@ -138,17 +279,9 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
qDebug() << "Adding folder " << filename.fileName() << " from "
|
qDebug() << "Adding folder " << filename.fileName() << " from "
|
||||||
<< filename.absoluteFilePath();
|
<< filename.absoluteFilePath();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// Make sure we do not continue launching when something is missing or undefined...
|
|
||||||
zipOut.close();
|
|
||||||
QFile::remove(targetJarPath);
|
|
||||||
qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");}))
|
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, metaInfFilter))
|
||||||
{
|
{
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
QFile::remove(targetJarPath);
|
QFile::remove(targetJarPath);
|
||||||
@@ -167,8 +300,46 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
bool MMCZip::noFilter(QString)
|
||||||
QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root)
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMCZip::metaInfFilter(QString key)
|
||||||
|
{
|
||||||
|
if(key.contains("META-INF"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMCZip::compressDir(QString zipFile, QString dir, QString prefix, const SeparatorPrefixTree <'/'> * blacklist)
|
||||||
|
{
|
||||||
|
QuaZip zip(zipFile);
|
||||||
|
QDir().mkpath(QFileInfo(zipFile).absolutePath());
|
||||||
|
if(!zip.open(QuaZip::mdCreate))
|
||||||
|
{
|
||||||
|
QFile::remove(zipFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> added;
|
||||||
|
if (!compressSubDir(&zip, dir, dir, added, prefix, blacklist))
|
||||||
|
{
|
||||||
|
QFile::remove(zipFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
zip.close();
|
||||||
|
if(zip.getZipError()!=0)
|
||||||
|
{
|
||||||
|
QFile::remove(zipFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MMCZip::findFileInZip(QuaZip * zip, const QString & what, const QString &root)
|
||||||
{
|
{
|
||||||
QuaZipDir rootDir(zip, root);
|
QuaZipDir rootDir(zip, root);
|
||||||
for(auto fileName: rootDir.entryList(QDir::Files))
|
for(auto fileName: rootDir.entryList(QDir::Files))
|
||||||
@@ -178,7 +349,7 @@ QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const
|
|||||||
}
|
}
|
||||||
for(auto fileName: rootDir.entryList(QDir::Dirs))
|
for(auto fileName: rootDir.entryList(QDir::Dirs))
|
||||||
{
|
{
|
||||||
QString result = findFolderOfFileInZip(zip, what, root + fileName);
|
QString result = findFileInZip(zip, what, root + fileName);
|
||||||
if(!result.isEmpty())
|
if(!result.isEmpty())
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
@@ -187,7 +358,6 @@ QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
|
||||||
bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root)
|
bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root)
|
||||||
{
|
{
|
||||||
QuaZipDir rootDir(zip, root);
|
QuaZipDir rootDir(zip, root);
|
||||||
@@ -206,16 +376,95 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re
|
|||||||
return !result.isEmpty();
|
return !result.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool removeFile(QStringList listFile)
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
for (int i = 0; i < listFile.count(); i++)
|
||||||
|
{
|
||||||
|
ret &= QFile::remove(listFile.at(i));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MMCZip::extractFile(QuaZip *zip, const QString &fileName, const QString &fileDest)
|
||||||
|
{
|
||||||
|
if(!zip)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (zip->getMode() != QuaZip::mdUnzip)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!fileName.isEmpty())
|
||||||
|
zip->setCurrentFile(fileName);
|
||||||
|
|
||||||
|
QuaZipFile inFile(zip);
|
||||||
|
if (!inFile.open(QIODevice::ReadOnly) || inFile.getZipError() != UNZ_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Controllo esistenza cartella file risultato
|
||||||
|
QDir curDir;
|
||||||
|
if (fileDest.endsWith('/'))
|
||||||
|
{
|
||||||
|
if (!curDir.mkpath(fileDest))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!curDir.mkpath(QFileInfo(fileDest).absolutePath()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QuaZipFileInfo64 info;
|
||||||
|
if (!zip->getCurrentFileInfo(&info))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QFile::Permissions srcPerm = info.getPermissions();
|
||||||
|
if (fileDest.endsWith('/') && QFileInfo(fileDest).isDir())
|
||||||
|
{
|
||||||
|
if (srcPerm != 0)
|
||||||
|
{
|
||||||
|
QFile(fileDest).setPermissions(srcPerm);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile outFile;
|
||||||
|
outFile.setFileName(fileDest);
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!copyData(inFile, outFile) || inFile.getZipError() != UNZ_OK)
|
||||||
|
{
|
||||||
|
outFile.close();
|
||||||
|
removeFile(QStringList(fileDest));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outFile.close();
|
||||||
|
|
||||||
|
inFile.close();
|
||||||
|
if (inFile.getZipError() != UNZ_OK)
|
||||||
|
{
|
||||||
|
removeFile(QStringList(fileDest));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcPerm != 0)
|
||||||
|
{
|
||||||
|
outFile.setPermissions(srcPerm);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// ours
|
|
||||||
QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
|
QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
|
||||||
{
|
{
|
||||||
QDir directory(target);
|
QDir directory(target);
|
||||||
QStringList extracted;
|
QStringList extracted;
|
||||||
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
|
|
||||||
if (!zip->goToFirstFile())
|
if (!zip->goToFirstFile())
|
||||||
{
|
{
|
||||||
qWarning() << "Failed to seek to first file in zip";
|
|
||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
@@ -231,25 +480,12 @@ QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QSt
|
|||||||
{
|
{
|
||||||
absFilePath += "/";
|
absFilePath += "/";
|
||||||
}
|
}
|
||||||
if (!JlCompress::extractFile(zip, "", absFilePath))
|
if (!extractFile(zip, "", absFilePath))
|
||||||
{
|
{
|
||||||
qWarning() << "Failed to extract file" << name << "to" << absFilePath;
|
removeFile(extracted);
|
||||||
JlCompress::removeFile(extracted);
|
|
||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
extracted.append(absFilePath);
|
extracted.append(absFilePath);
|
||||||
qDebug() << "Extracted file" << name;
|
|
||||||
} while (zip->goToNextFile());
|
} while (zip->goToNextFile());
|
||||||
return extracted;
|
return extracted;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
|
||||||
QStringList MMCZip::extractDir(QString fileCompressed, QString dir)
|
|
||||||
{
|
|
||||||
QuaZip zip(fileCompressed);
|
|
||||||
if (!zip.open(QuaZip::mdUnzip))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return MMCZip::extractSubDir(&zip, "", dir);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,50 +1,70 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include "minecraft/Mod.h"
|
#include "minecraft/Mod.h"
|
||||||
|
#include "SeparatorPrefixTree.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
#include <JlCompress.h>
|
class QuaZip;
|
||||||
|
|
||||||
namespace MMCZip
|
namespace MMCZip
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Compress a subdirectory.
|
||||||
|
* \param parentZip Opened zip containing the parent directory.
|
||||||
|
* \param dir The full path to the directory to pack.
|
||||||
|
* \param parentDir The full path to the directory corresponding to the root of the ZIP.
|
||||||
|
* \param recursive Whether to pack sub-directories as well or only files.
|
||||||
|
* \return true if success, false otherwise.
|
||||||
|
*/
|
||||||
|
bool MULTIMC_LOGIC_EXPORT compressSubDir(QuaZip *zip, QString dir, QString origDir, QSet<QString> &added,
|
||||||
|
QString prefix = QString(), const SeparatorPrefixTree <'/'> * blacklist = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress a whole directory.
|
||||||
|
* \param fileCompressed The name of the archive.
|
||||||
|
* \param dir The directory to compress.
|
||||||
|
* \param recursive Whether to pack the subdirectories as well, or just regular files.
|
||||||
|
* \return true if success, false otherwise.
|
||||||
|
*/
|
||||||
|
bool MULTIMC_LOGIC_EXPORT compressDir(QString zipFile, QString dir, QString prefix = QString(), const SeparatorPrefixTree <'/'> * blacklist = nullptr);
|
||||||
|
|
||||||
|
/// filter function for @mergeZipFiles - passthrough
|
||||||
|
bool MULTIMC_LOGIC_EXPORT noFilter(QString key);
|
||||||
|
|
||||||
|
/// filter function for @mergeZipFiles - ignores METAINF
|
||||||
|
bool MULTIMC_LOGIC_EXPORT metaInfFilter(QString key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge two zip files, using a filter function
|
* Merge two zip files, using a filter function
|
||||||
*/
|
*/
|
||||||
bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
|
bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, std::function<bool(QString)> filter);
|
||||||
const JlCompress::FilterFunction filter = nullptr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* take a source jar, add mods to it, resulting in target jar
|
* take a source jar, add mods to it, resulting in target jar
|
||||||
*/
|
*/
|
||||||
bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
|
bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a whole archive.
|
||||||
|
*
|
||||||
|
* \param fileCompressed The name of the archive.
|
||||||
|
* \param dir The directory to extract to, the current directory if
|
||||||
|
* left empty.
|
||||||
|
* \return The list of the full paths of the files extracted, empty on failure.
|
||||||
|
*/
|
||||||
|
QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir = QString());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a single file in archive by file name (not path)
|
* Find a single file in archive by file name (not path)
|
||||||
*
|
*
|
||||||
* \return the path prefix where the file is
|
* \return the path prefix where the file is
|
||||||
*/
|
*/
|
||||||
QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
|
QString MULTIMC_LOGIC_EXPORT findFileInZip(QuaZip * zip, const QString & what, const QString &root = QString());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a multiple files of the same name in archive by file name
|
* Find a multiple files of the same name in archive by file name
|
||||||
@@ -54,18 +74,15 @@ namespace MMCZip
|
|||||||
*/
|
*/
|
||||||
bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
|
bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a single file to a destination
|
||||||
|
*
|
||||||
|
* \return true if it succeeds
|
||||||
|
*/
|
||||||
|
bool MULTIMC_LOGIC_EXPORT extractFile(QuaZip *zip, const QString &fileName, const QString &fileDest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a subdirectory from an archive
|
* Extract a subdirectory from an archive
|
||||||
*/
|
*/
|
||||||
QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
|
QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract a whole archive.
|
|
||||||
*
|
|
||||||
* \param fileCompressed The name of the archive.
|
|
||||||
* \param dir The directory to extract to, the current directory if left empty.
|
|
||||||
* \return The list of the full paths of the files extracted, empty on failure.
|
|
||||||
*/
|
|
||||||
QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "launch/LaunchTask.h"
|
|
||||||
|
|
||||||
class NullInstance: public BaseInstance
|
class NullInstance: public BaseInstance
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
|
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
|
||||||
:BaseInstance(globalSettings, settings, rootDir)
|
:BaseInstance(globalSettings, settings, rootDir)
|
||||||
@@ -12,46 +10,73 @@ public:
|
|||||||
setVersionBroken(true);
|
setVersionBroken(true);
|
||||||
}
|
}
|
||||||
virtual ~NullInstance() {};
|
virtual ~NullInstance() {};
|
||||||
void saveNow() override
|
virtual bool setIntendedVersionId(QString) override
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
QString getStatusbarDescription() override
|
virtual QString currentVersionId() const override
|
||||||
|
{
|
||||||
|
return "Null";
|
||||||
|
};
|
||||||
|
virtual QString intendedVersionId() const override
|
||||||
|
{
|
||||||
|
return "Null";
|
||||||
|
};
|
||||||
|
virtual void init() override
|
||||||
|
{
|
||||||
|
};
|
||||||
|
virtual QString getStatusbarDescription() override
|
||||||
{
|
{
|
||||||
return tr("Unknown instance type");
|
return tr("Unknown instance type");
|
||||||
};
|
};
|
||||||
QSet< QString > traits() const override
|
virtual bool shouldUpdate() const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
virtual QSet< QString > traits() override
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
QString instanceConfigFolder() const override
|
virtual QString instanceConfigFolder() const override
|
||||||
{
|
{
|
||||||
return instanceRoot();
|
return instanceRoot();
|
||||||
};
|
};
|
||||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
|
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
|
virtual shared_qobject_ptr< Task > createUpdateTask() override
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
QProcessEnvironment createEnvironment() override
|
virtual std::shared_ptr<Task> createJarModdingTask() override
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
virtual void setShouldUpdate(bool) override
|
||||||
|
{
|
||||||
|
};
|
||||||
|
virtual std::shared_ptr< BaseVersionList > versionList() const override
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
virtual QProcessEnvironment createEnvironment() override
|
||||||
{
|
{
|
||||||
return QProcessEnvironment();
|
return QProcessEnvironment();
|
||||||
}
|
}
|
||||||
QMap<QString, QString> getVariables() const override
|
virtual QMap<QString, QString> getVariables() const override
|
||||||
{
|
{
|
||||||
return QMap<QString, QString>();
|
return QMap<QString, QString>();
|
||||||
}
|
}
|
||||||
IPathMatcher::Ptr getLogFileMatcher() override
|
virtual IPathMatcher::Ptr getLogFileMatcher() override
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
QString getLogFileRoot() override
|
virtual QString getLogFileRoot() override
|
||||||
{
|
{
|
||||||
return instanceRoot();
|
return instanceRoot();
|
||||||
}
|
}
|
||||||
QString typeName() const override
|
virtual QString typeName() const override
|
||||||
{
|
{
|
||||||
return "Null";
|
return "Null";
|
||||||
}
|
}
|
||||||
@@ -59,14 +84,6 @@ public:
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool canEdit() const override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool canLaunch() const override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QStringList verboseDescription(AuthSessionPtr session) override
|
QStringList verboseDescription(AuthSessionPtr session) override
|
||||||
{
|
{
|
||||||
QStringList out;
|
QStringList out;
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
|
||||||
|
|
||||||
enum class ProblemSeverity
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Warning,
|
|
||||||
Error
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PatchProblem
|
|
||||||
{
|
|
||||||
ProblemSeverity m_severity;
|
|
||||||
QString m_description;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT ProblemProvider
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~ProblemProvider() {};
|
|
||||||
virtual const QList<PatchProblem> getProblems() const = 0;
|
|
||||||
virtual ProblemSeverity getProblemSeverity() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT ProblemContainer : public ProblemProvider
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const QList<PatchProblem> getProblems() const override
|
|
||||||
{
|
|
||||||
return m_problems;
|
|
||||||
}
|
|
||||||
ProblemSeverity getProblemSeverity() const override
|
|
||||||
{
|
|
||||||
return m_problemSeverity;
|
|
||||||
}
|
|
||||||
virtual void addProblem(ProblemSeverity severity, const QString &description)
|
|
||||||
{
|
|
||||||
if(severity > m_problemSeverity)
|
|
||||||
{
|
|
||||||
m_problemSeverity = severity;
|
|
||||||
}
|
|
||||||
m_problems.append({severity, description});
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<PatchProblem> m_problems;
|
|
||||||
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace details
|
namespace details
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QWriteLocker>
|
|
||||||
#include <QReadLocker>
|
|
||||||
template <typename K, typename V>
|
template <typename K, typename V>
|
||||||
class RWStorage
|
class RWStorage
|
||||||
{
|
{
|
||||||
@@ -44,7 +42,7 @@ public:
|
|||||||
}
|
}
|
||||||
void setStale(K key)
|
void setStale(K key)
|
||||||
{
|
{
|
||||||
QWriteLocker l(&lock);
|
QReadLocker l(&lock);
|
||||||
if(cache.contains(key))
|
if(cache.contains(key))
|
||||||
{
|
{
|
||||||
stale_entries.insert(key);
|
stale_entries.insert(key);
|
||||||
@@ -54,7 +52,6 @@ public:
|
|||||||
{
|
{
|
||||||
QWriteLocker l(&lock);
|
QWriteLocker l(&lock);
|
||||||
cache.clear();
|
cache.clear();
|
||||||
stale_entries.clear();
|
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
QReadWriteLock lock;
|
QReadWriteLock lock;
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ void Version::parse()
|
|||||||
{
|
{
|
||||||
m_sections.clear();
|
m_sections.clear();
|
||||||
|
|
||||||
// FIXME: this is bad. versions can contain a lot more separators...
|
|
||||||
QStringList parts = m_string.split('.');
|
QStringList parts = m_string.split('.');
|
||||||
|
|
||||||
for (const auto part : parts)
|
for (const auto part : parts)
|
||||||
@@ -83,3 +82,59 @@ void Version::parse()
|
|||||||
m_sections.append(Section(part));
|
m_sections.append(Section(part));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool versionIsInInterval(const QString &version, const QString &interval)
|
||||||
|
{
|
||||||
|
return versionIsInInterval(Version(version), interval);
|
||||||
|
}
|
||||||
|
bool versionIsInInterval(const Version &version, const QString &interval)
|
||||||
|
{
|
||||||
|
if (interval.isEmpty() || version.toString() == interval)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interval notation is used
|
||||||
|
QRegularExpression exp(
|
||||||
|
"(?<start>[\\[\\]\\(\\)])(?<bottom>.*?)(,(?<top>.*?))?(?<end>[\\[\\]\\(\\)]),?");
|
||||||
|
QRegularExpressionMatch match = exp.match(interval);
|
||||||
|
if (match.hasMatch())
|
||||||
|
{
|
||||||
|
const QChar start = match.captured("start").at(0);
|
||||||
|
const QChar end = match.captured("end").at(0);
|
||||||
|
const QString bottom = match.captured("bottom");
|
||||||
|
const QString top = match.captured("top");
|
||||||
|
|
||||||
|
// check if in range (bottom)
|
||||||
|
if (!bottom.isEmpty())
|
||||||
|
{
|
||||||
|
const auto bottomVersion = Version(bottom);
|
||||||
|
if ((start == '[') && !(version >= bottomVersion))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ((start == '(') && !(version > bottomVersion))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if in range (top)
|
||||||
|
if (!top.isEmpty())
|
||||||
|
{
|
||||||
|
const auto topVersion = Version(top);
|
||||||
|
if ((end == ']') && !(version <= topVersion))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ((end == ')') && !(version < topVersion))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,9 +7,8 @@
|
|||||||
|
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT Version
|
struct MULTIMC_LOGIC_EXPORT Version
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
Version(const QString &str);
|
Version(const QString &str);
|
||||||
Version() {}
|
Version() {}
|
||||||
|
|
||||||
@@ -105,3 +104,7 @@ private:
|
|||||||
|
|
||||||
void parse();
|
void parse();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MULTIMC_LOGIC_EXPORT bool versionIsInInterval(const QString &version, const QString &interval);
|
||||||
|
MULTIMC_LOGIC_EXPORT bool versionIsInInterval(const Version &version, const QString &interval);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -60,6 +60,44 @@ private slots:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_versionIsInInterval_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("version");
|
||||||
|
QTest::addColumn<QString>("interval");
|
||||||
|
QTest::addColumn<bool>("result");
|
||||||
|
|
||||||
|
QTest::newRow("empty, true") << "1.2.3" << "" << true;
|
||||||
|
QTest::newRow("one version, true") << "1.2.3" << "1.2.3" << true;
|
||||||
|
QTest::newRow("one version, false") << "1.2.3" << "1.2.2" << false;
|
||||||
|
|
||||||
|
QTest::newRow("one version inclusive <-> infinity, true") << "1.2.3" << "[1.2.3,)" << true;
|
||||||
|
QTest::newRow("one version exclusive <-> infinity, true") << "1.2.3" << "(1.2.2,)" << true;
|
||||||
|
QTest::newRow("one version inclusive <-> infitity, false") << "1.2.3" << "[1.2.4,)" << false;
|
||||||
|
QTest::newRow("one version exclusive <-> infinity, false") << "1.2.3" << "(1.2.3,)" << false;
|
||||||
|
|
||||||
|
QTest::newRow("infinity <-> one version inclusive, true") << "1.2.3" << "(,1.2.3]" << true;
|
||||||
|
QTest::newRow("infinity <-> one version exclusive, true") << "1.2.3" << "(,1.2.4)" << true;
|
||||||
|
QTest::newRow("infinity <-> one version inclusive, false") << "1.2.3" << "(,1.2.2]" << false;
|
||||||
|
QTest::newRow("infinity <-> one version exclusive, false") << "1.2.3" << "(,1.2.3)" << false;
|
||||||
|
|
||||||
|
QTest::newRow("inclusive <-> inclusive, true") << "1.2.3" << "[1.2.2,1.2.3]" << true;
|
||||||
|
QTest::newRow("inclusive <-> exclusive, true") << "1.2.3" << "[1.2.3,1.2.4)" << true;
|
||||||
|
QTest::newRow("exclusive <-> inclusive, true") << "1.2.3" << "(1.2.2,1.2.3]" << true;
|
||||||
|
QTest::newRow("exclusive <-> exclusive, true") << "1.2.3" << "(1.2.2,1.2.4)" << true;
|
||||||
|
QTest::newRow("inclusive <-> inclusive, false") << "1.2.3" << "[1.0.0,1.2.2]" << false;
|
||||||
|
QTest::newRow("inclusive <-> exclusive, false") << "1.2.3" << "[1.0.0,1.2.3)" << false;
|
||||||
|
QTest::newRow("exclusive <-> inclusive, false") << "1.2.3" << "(1.2.3,2.0.0]" << false;
|
||||||
|
QTest::newRow("exclusive <-> exclusive, false") << "1.2.3" << "(1.0.0,1.2.3)" << false;
|
||||||
|
}
|
||||||
|
void test_versionIsInInterval()
|
||||||
|
{
|
||||||
|
QFETCH(QString, version);
|
||||||
|
QFETCH(QString, interval);
|
||||||
|
QFETCH(bool, result);
|
||||||
|
|
||||||
|
QCOMPARE(versionIsInInterval(version, interval), result);
|
||||||
|
}
|
||||||
|
|
||||||
void test_versionCompare_data()
|
void test_versionCompare_data()
|
||||||
{
|
{
|
||||||
setupVersions();
|
setupVersions();
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
|
|
||||||
struct WatchLock
|
|
||||||
{
|
|
||||||
WatchLock(QFileSystemWatcher * watcher, const QString& directory)
|
|
||||||
: m_watcher(watcher), m_directory(directory)
|
|
||||||
{
|
|
||||||
m_watcher->removePath(m_directory);
|
|
||||||
}
|
|
||||||
~WatchLock()
|
|
||||||
{
|
|
||||||
m_watcher->addPath(m_directory);
|
|
||||||
}
|
|
||||||
QFileSystemWatcher * m_watcher;
|
|
||||||
QString m_directory;
|
|
||||||
};
|
|
||||||
@@ -22,5 +22,4 @@ public:
|
|||||||
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
|
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
|
||||||
virtual bool iconFileExists(const QString &key) const = 0;
|
virtual bool iconFileExists(const QString &key) const = 0;
|
||||||
virtual void installIcons(const QStringList &iconFiles) = 0;
|
virtual void installIcons(const QStringList &iconFiles) = 0;
|
||||||
virtual void installIcon(const QString &file, const QString &name) = 0;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
#include "IconUtils.h"
|
|
||||||
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include <QDirIterator>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::array<const char *, 6> validIconExtensions = {{
|
|
||||||
"svg",
|
|
||||||
"png",
|
|
||||||
"ico",
|
|
||||||
"gif",
|
|
||||||
"jpg",
|
|
||||||
"jpeg"
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace IconUtils{
|
|
||||||
|
|
||||||
QString findBestIconIn(const QString &folder, const QString & iconKey) {
|
|
||||||
int best_found = validIconExtensions.size();
|
|
||||||
QString best_filename;
|
|
||||||
|
|
||||||
QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags);
|
|
||||||
while (it.hasNext()) {
|
|
||||||
it.next();
|
|
||||||
auto fileInfo = it.fileInfo();
|
|
||||||
|
|
||||||
if(fileInfo.completeBaseName() != iconKey)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto extension = fileInfo.suffix();
|
|
||||||
|
|
||||||
for(int i = 0; i < best_found; i++) {
|
|
||||||
if(extension == validIconExtensions[i]) {
|
|
||||||
best_found = i;
|
|
||||||
qDebug() << i << " : " << fileInfo.fileName();
|
|
||||||
best_filename = fileInfo.fileName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FS::PathCombine(folder, best_filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getIconFilter() {
|
|
||||||
QString out;
|
|
||||||
QTextStream stream(&out);
|
|
||||||
stream << '(';
|
|
||||||
for(size_t i = 0; i < validIconExtensions.size() - 1; i++) {
|
|
||||||
if(i > 0) {
|
|
||||||
stream << " ";
|
|
||||||
}
|
|
||||||
stream << "*." << validIconExtensions[i];
|
|
||||||
}
|
|
||||||
stream << " *." << validIconExtensions[validIconExtensions.size() - 1];
|
|
||||||
stream << ')';
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include "multimc_logic_export.h"
|
|
||||||
|
|
||||||
namespace IconUtils {
|
|
||||||
|
|
||||||
// Given a folder and an icon key, find 'best' of the icons with the given key in there and return its path
|
|
||||||
MULTIMC_LOGIC_EXPORT QString findBestIconIn(const QString &folder, const QString & iconKey);
|
|
||||||
|
|
||||||
// Get icon file type filter for file browser dialogs
|
|
||||||
MULTIMC_LOGIC_EXPORT QString getIconFilter();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "JavaChecker.h"
|
#include "JavaChecker.h"
|
||||||
#include "JavaUtils.h"
|
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <Commandline.h>
|
#include <Commandline.h>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -8,15 +7,13 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "Env.h"
|
|
||||||
|
|
||||||
JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
|
JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaChecker::performCheck()
|
void JavaChecker::performCheck()
|
||||||
{
|
{
|
||||||
QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar");
|
QString checkerJar = FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "JavaCheck.jar");
|
||||||
|
|
||||||
QStringList args;
|
QStringList args;
|
||||||
|
|
||||||
@@ -43,7 +40,6 @@ void JavaChecker::performCheck()
|
|||||||
process->setArguments(args);
|
process->setArguments(args);
|
||||||
process->setProgram(m_path);
|
process->setProgram(m_path);
|
||||||
process->setProcessChannelMode(QProcess::SeparateChannels);
|
process->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
process->setProcessEnvironment(CleanEnviroment());
|
|
||||||
qDebug() << "Running java checker: " + m_path + args.join(" ");;
|
qDebug() << "Running java checker: " + m_path + args.join(" ");;
|
||||||
|
|
||||||
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
|
connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
|
||||||
@@ -75,8 +71,8 @@ void JavaChecker::stderrReady()
|
|||||||
void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||||
{
|
{
|
||||||
killTimer.stop();
|
killTimer.stop();
|
||||||
QProcessPtr _process = process;
|
QProcessPtr _process;
|
||||||
process.reset();
|
_process.swap(process);
|
||||||
|
|
||||||
JavaCheckResult result;
|
JavaCheckResult result;
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
#include "JavaVersion.h"
|
#include "JavaVersion.h"
|
||||||
@@ -29,8 +27,8 @@ struct MULTIMC_LOGIC_EXPORT JavaCheckResult
|
|||||||
} validity = Validity::Errored;
|
} validity = Validity::Errored;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef shared_qobject_ptr<QProcess> QProcessPtr;
|
typedef std::shared_ptr<QProcess> QProcessPtr;
|
||||||
typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr;
|
typedef std::shared_ptr<JavaChecker> JavaCheckerPtr;
|
||||||
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
|
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,19 +22,20 @@ void JavaCheckerJob::partFinished(JavaCheckResult result)
|
|||||||
num_finished++;
|
num_finished++;
|
||||||
qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
|
qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/"
|
||||||
<< javacheckers.size();
|
<< javacheckers.size();
|
||||||
setProgress(num_finished, javacheckers.size());
|
emit progress(num_finished, javacheckers.size());
|
||||||
|
|
||||||
javaresults.replace(result.id, result);
|
javaresults.replace(result.id, result);
|
||||||
|
|
||||||
if (num_finished == javacheckers.size())
|
if (num_finished == javacheckers.size())
|
||||||
{
|
{
|
||||||
emitSucceeded();
|
emit finished(javaresults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaCheckerJob::executeTask()
|
void JavaCheckerJob::executeTask()
|
||||||
{
|
{
|
||||||
qDebug() << m_job_name.toLocal8Bit() << " started.";
|
qDebug() << m_job_name.toLocal8Bit() << " started.";
|
||||||
|
m_running = true;
|
||||||
for (auto iter : javacheckers)
|
for (auto iter : javacheckers)
|
||||||
{
|
{
|
||||||
javaresults.append(JavaCheckResult());
|
javaresults.append(JavaCheckResult());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,32 +20,52 @@
|
|||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class JavaCheckerJob;
|
class JavaCheckerJob;
|
||||||
typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr;
|
typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr;
|
||||||
|
|
||||||
// FIXME: this just seems horribly redundant
|
|
||||||
class JavaCheckerJob : public Task
|
class JavaCheckerJob : public Task
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
|
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
|
||||||
virtual ~JavaCheckerJob() {};
|
|
||||||
|
|
||||||
bool addJavaCheckerAction(JavaCheckerPtr base)
|
bool addJavaCheckerAction(JavaCheckerPtr base)
|
||||||
{
|
{
|
||||||
javacheckers.append(base);
|
javacheckers.append(base);
|
||||||
|
total_progress++;
|
||||||
// if this is already running, the action needs to be started right away!
|
// if this is already running, the action needs to be started right away!
|
||||||
if (isRunning())
|
if (isRunning())
|
||||||
{
|
{
|
||||||
setProgress(num_finished, javacheckers.size());
|
setProgress(current_progress, total_progress);
|
||||||
connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished);
|
connect(base.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult)));
|
||||||
|
|
||||||
base->performCheck();
|
base->performCheck();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
QList<JavaCheckResult> getResults()
|
|
||||||
|
JavaCheckerPtr operator[](int index)
|
||||||
{
|
{
|
||||||
return javaresults;
|
return javacheckers[index];
|
||||||
}
|
}
|
||||||
|
;
|
||||||
|
JavaCheckerPtr first()
|
||||||
|
{
|
||||||
|
if (javacheckers.size())
|
||||||
|
return javacheckers[0];
|
||||||
|
return JavaCheckerPtr();
|
||||||
|
}
|
||||||
|
int size() const
|
||||||
|
{
|
||||||
|
return javacheckers.size();
|
||||||
|
}
|
||||||
|
virtual bool isRunning() const override
|
||||||
|
{
|
||||||
|
return m_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void started();
|
||||||
|
void finished(QList<JavaCheckResult>);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void partFinished(JavaCheckResult result);
|
void partFinished(JavaCheckResult result);
|
||||||
@@ -57,5 +77,8 @@ private:
|
|||||||
QString m_job_name;
|
QString m_job_name;
|
||||||
QList<JavaCheckerPtr> javacheckers;
|
QList<JavaCheckerPtr> javacheckers;
|
||||||
QList<JavaCheckResult> javaresults;
|
QList<JavaCheckResult> javaresults;
|
||||||
|
qint64 current_progress = 0;
|
||||||
|
qint64 total_progress = 0;
|
||||||
int num_finished = 0;
|
int num_finished = 0;
|
||||||
|
bool m_running = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -29,29 +29,9 @@ JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
|
Task *JavaInstallList::getLoadTask()
|
||||||
{
|
{
|
||||||
load();
|
return new JavaListLoadTask(this);
|
||||||
return getCurrentTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_qobject_ptr<Task> JavaInstallList::getCurrentTask()
|
|
||||||
{
|
|
||||||
if(m_status == Status::InProgress)
|
|
||||||
{
|
|
||||||
return m_loadTask;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JavaInstallList::load()
|
|
||||||
{
|
|
||||||
if(m_status != Status::InProgress)
|
|
||||||
{
|
|
||||||
m_status = Status::InProgress;
|
|
||||||
m_loadTask = new JavaListLoadTask(this);
|
|
||||||
m_loadTask->start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseVersionPtr JavaInstallList::at(int i) const
|
const BaseVersionPtr JavaInstallList::at(int i) const
|
||||||
@@ -61,7 +41,7 @@ const BaseVersionPtr JavaInstallList::at(int i) const
|
|||||||
|
|
||||||
bool JavaInstallList::isLoaded()
|
bool JavaInstallList::isLoaded()
|
||||||
{
|
{
|
||||||
return m_status == JavaInstallList::Status::Done;
|
return m_loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JavaInstallList::count() const
|
int JavaInstallList::count() const
|
||||||
@@ -107,6 +87,7 @@ void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
|
|||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_vlist = versions;
|
m_vlist = versions;
|
||||||
|
m_loaded = true;
|
||||||
sortVersions();
|
sortVersions();
|
||||||
if(m_vlist.size())
|
if(m_vlist.size())
|
||||||
{
|
{
|
||||||
@@ -114,8 +95,6 @@ void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)
|
|||||||
best->recommended = true;
|
best->recommended = true;
|
||||||
}
|
}
|
||||||
endResetModel();
|
endResetModel();
|
||||||
m_status = Status::Done;
|
|
||||||
m_loadTask.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
|
bool sortJavas(BaseVersionPtr left, BaseVersionPtr right)
|
||||||
@@ -149,8 +128,8 @@ void JavaListLoadTask::executeTask()
|
|||||||
JavaUtils ju;
|
JavaUtils ju;
|
||||||
QList<QString> candidate_paths = ju.FindJavaPaths();
|
QList<QString> candidate_paths = ju.FindJavaPaths();
|
||||||
|
|
||||||
m_job = new JavaCheckerJob("Java detection");
|
m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
|
||||||
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
|
connect(m_job.get(), SIGNAL(finished(QList<JavaCheckResult>)), this, SLOT(javaCheckerFinished(QList<JavaCheckResult>)));
|
||||||
connect(m_job.get(), &Task::progress, this, &Task::setProgress);
|
connect(m_job.get(), &Task::progress, this, &Task::setProgress);
|
||||||
|
|
||||||
qDebug() << "Probing the following Java paths: ";
|
qDebug() << "Probing the following Java paths: ";
|
||||||
@@ -170,10 +149,9 @@ void JavaListLoadTask::executeTask()
|
|||||||
m_job->start();
|
m_job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JavaListLoadTask::javaCheckerFinished()
|
void JavaListLoadTask::javaCheckerFinished(QList<JavaCheckResult> results)
|
||||||
{
|
{
|
||||||
QList<JavaInstallPtr> candidates;
|
QList<JavaInstallPtr> candidates;
|
||||||
auto results = m_job->getResults();
|
|
||||||
|
|
||||||
qDebug() << "Found the following valid Java installations:";
|
qDebug() << "Found the following valid Java installations:";
|
||||||
for(JavaCheckResult result : results)
|
for(JavaCheckResult result : results)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
#include "JavaCheckerJob.h"
|
#include "JavaCheckerJob.h"
|
||||||
#include "JavaInstall.h"
|
#include "JavaInstall.h"
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
class JavaListLoadTask;
|
class JavaListLoadTask;
|
||||||
@@ -33,35 +31,25 @@ class JavaListLoadTask;
|
|||||||
class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList
|
class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
enum class Status
|
|
||||||
{
|
|
||||||
NotDone,
|
|
||||||
InProgress,
|
|
||||||
Done
|
|
||||||
};
|
|
||||||
public:
|
public:
|
||||||
explicit JavaInstallList(QObject *parent = 0);
|
explicit JavaInstallList(QObject *parent = 0);
|
||||||
|
|
||||||
shared_qobject_ptr<Task> getLoadTask() override;
|
virtual Task *getLoadTask() override;
|
||||||
bool isLoaded() override;
|
virtual bool isLoaded() override;
|
||||||
const BaseVersionPtr at(int i) const override;
|
virtual const BaseVersionPtr at(int i) const override;
|
||||||
int count() const override;
|
virtual int count() const override;
|
||||||
void sortVersions() override;
|
virtual void sortVersions() override;
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
virtual QVariant data(const QModelIndex &index, int role) const override;
|
||||||
RoleList providesRoles() const override;
|
virtual RoleList providesRoles() const override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateListData(QList<BaseVersionPtr> versions) override;
|
virtual void updateListData(QList<BaseVersionPtr> versions) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void load();
|
|
||||||
shared_qobject_ptr<Task> getCurrentTask();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Status m_status = Status::NotDone;
|
|
||||||
shared_qobject_ptr<JavaListLoadTask> m_loadTask;
|
|
||||||
QList<BaseVersionPtr> m_vlist;
|
QList<BaseVersionPtr> m_vlist;
|
||||||
|
|
||||||
|
bool m_loaded = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class JavaListLoadTask : public Task
|
class JavaListLoadTask : public Task
|
||||||
@@ -70,14 +58,14 @@ class JavaListLoadTask : public Task
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit JavaListLoadTask(JavaInstallList *vlist);
|
explicit JavaListLoadTask(JavaInstallList *vlist);
|
||||||
virtual ~JavaListLoadTask();
|
~JavaListLoadTask();
|
||||||
|
|
||||||
void executeTask() override;
|
virtual void executeTask();
|
||||||
public slots:
|
public slots:
|
||||||
void javaCheckerFinished();
|
void javaCheckerFinished(QList<JavaCheckResult> results);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
shared_qobject_ptr<JavaCheckerJob> m_job;
|
std::shared_ptr<JavaCheckerJob> m_job;
|
||||||
JavaInstallList *m_list;
|
JavaInstallList *m_list;
|
||||||
JavaInstall *m_currentRecommended;
|
JavaInstall *m_currentRecommended;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,107 +22,14 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "java/JavaUtils.h"
|
#include "java/JavaUtils.h"
|
||||||
|
#include "java/JavaCheckerJob.h"
|
||||||
#include "java/JavaInstallList.h"
|
#include "java/JavaInstallList.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
#define IBUS "@im=ibus"
|
|
||||||
|
|
||||||
JavaUtils::JavaUtils()
|
JavaUtils::JavaUtils()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
|
|
||||||
{
|
|
||||||
QDir mmcBin(QCoreApplication::applicationDirPath());
|
|
||||||
auto items = LD_LIBRARY_PATH.split(':');
|
|
||||||
QStringList final;
|
|
||||||
for(auto & item: items)
|
|
||||||
{
|
|
||||||
QDir test(item);
|
|
||||||
if(test == mmcBin)
|
|
||||||
{
|
|
||||||
qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final.append(item);
|
|
||||||
}
|
|
||||||
return final.join(':');
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QProcessEnvironment CleanEnviroment()
|
|
||||||
{
|
|
||||||
// prepare the process environment
|
|
||||||
QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
|
|
||||||
QProcessEnvironment env;
|
|
||||||
|
|
||||||
QStringList ignored =
|
|
||||||
{
|
|
||||||
"JAVA_ARGS",
|
|
||||||
"CLASSPATH",
|
|
||||||
"CONFIGPATH",
|
|
||||||
"JAVA_HOME",
|
|
||||||
"JRE_HOME",
|
|
||||||
"_JAVA_OPTIONS",
|
|
||||||
"JAVA_OPTIONS",
|
|
||||||
"JAVA_TOOL_OPTIONS"
|
|
||||||
};
|
|
||||||
for(auto key: rawenv.keys())
|
|
||||||
{
|
|
||||||
auto value = rawenv.value(key);
|
|
||||||
// filter out dangerous java crap
|
|
||||||
if(ignored.contains(key))
|
|
||||||
{
|
|
||||||
qDebug() << "Env: ignoring" << key << value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// filter MultiMC-related things
|
|
||||||
if(key.startsWith("QT_"))
|
|
||||||
{
|
|
||||||
qDebug() << "Env: ignoring" << key << value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
// Do not pass LD_* variables to java. They were intended for MultiMC
|
|
||||||
if(key.startsWith("LD_"))
|
|
||||||
{
|
|
||||||
qDebug() << "Env: ignoring" << key << value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Strip IBus
|
|
||||||
// IBus is a Linux IME framework. For some reason, it breaks MC?
|
|
||||||
if (key == "XMODIFIERS" && value.contains(IBUS))
|
|
||||||
{
|
|
||||||
QString save = value;
|
|
||||||
value.replace(IBUS, "");
|
|
||||||
qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
|
|
||||||
}
|
|
||||||
if(key == "GAME_PRELOAD")
|
|
||||||
{
|
|
||||||
env.insert("LD_PRELOAD", value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(key == "GAME_LIBRARY_PATH")
|
|
||||||
{
|
|
||||||
env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// qDebug() << "Env: " << key << value;
|
|
||||||
env.insert(key, value);
|
|
||||||
}
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
// HACK: Workaround for QTBUG42500
|
|
||||||
if(!env.contains("LD_LIBRARY_PATH"))
|
|
||||||
{
|
|
||||||
env.insert("LD_LIBRARY_PATH", "");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
|
JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
|
||||||
{
|
{
|
||||||
JavaInstallPtr javaVersion(new JavaInstall());
|
JavaInstallPtr javaVersion(new JavaInstall());
|
||||||
@@ -187,7 +94,7 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
|
|||||||
// Iterate until RegEnumKeyEx fails
|
// Iterate until RegEnumKeyEx fails
|
||||||
if (numSubKeys > 0)
|
if (numSubKeys > 0)
|
||||||
{
|
{
|
||||||
for (DWORD i = 0; i < numSubKeys; i++)
|
for (int i = 0; i < numSubKeys; i++)
|
||||||
{
|
{
|
||||||
subKeyNameSize = 255;
|
subKeyNameSize = 255;
|
||||||
retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
|
retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "JavaCheckerJob.h"
|
||||||
#include "JavaChecker.h"
|
#include "JavaChecker.h"
|
||||||
#include "JavaInstallList.h"
|
#include "JavaInstallList.h"
|
||||||
|
|
||||||
@@ -26,8 +27,6 @@
|
|||||||
|
|
||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
|
|
||||||
QProcessEnvironment CleanEnviroment();
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject
|
class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|||||||
@@ -60,18 +60,9 @@ bool JavaVersion::operator<(const JavaVersion &rhs)
|
|||||||
{
|
{
|
||||||
if(m_parseable && rhs.m_parseable)
|
if(m_parseable && rhs.m_parseable)
|
||||||
{
|
{
|
||||||
auto major = m_major;
|
if(m_major < rhs.m_major)
|
||||||
auto rmajor = rhs.m_major;
|
|
||||||
|
|
||||||
// HACK: discourage using java 9
|
|
||||||
if(major > 8)
|
|
||||||
major = -major;
|
|
||||||
if(rmajor > 8)
|
|
||||||
rmajor = -rmajor;
|
|
||||||
|
|
||||||
if(major < rmajor)
|
|
||||||
return true;
|
return true;
|
||||||
if(major > rmajor)
|
if(m_major > rhs.m_major)
|
||||||
return false;
|
return false;
|
||||||
if(m_minor < rhs.m_minor)
|
if(m_minor < rhs.m_minor)
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,14 +3,6 @@
|
|||||||
#include "multimc_logic_export.h"
|
#include "multimc_logic_export.h"
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
// NOTE: apparently the GNU C library pollutes the global namespace with these... undef them.
|
|
||||||
#ifdef major
|
|
||||||
#undef major
|
|
||||||
#endif
|
|
||||||
#ifdef minor
|
|
||||||
#undef minor
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT JavaVersion
|
class MULTIMC_LOGIC_EXPORT JavaVersion
|
||||||
{
|
{
|
||||||
friend class JavaVersionTest;
|
friend class JavaVersionTest;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -60,7 +60,7 @@ void CheckJava::executeTask()
|
|||||||
// if timestamps are not the same, or something is missing, check!
|
// if timestamps are not the same, or something is missing, check!
|
||||||
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
|
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
|
||||||
{
|
{
|
||||||
m_JavaChecker = new JavaChecker();
|
m_JavaChecker = std::make_shared<JavaChecker>();
|
||||||
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
|
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
|
||||||
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
|
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
|
||||||
m_JavaChecker->m_path = realJavaPath;
|
m_JavaChecker->m_path = realJavaPath;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -31,8 +31,8 @@ public: /* methods */
|
|||||||
};
|
};
|
||||||
virtual ~LaunchStep() {};
|
virtual ~LaunchStep() {};
|
||||||
|
|
||||||
private: /* methods */
|
protected: /* methods */
|
||||||
void bind(LaunchTask *parent);
|
virtual void bind(LaunchTask *parent);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void logLines(QStringList lines, MessageLevel::Enum level);
|
void logLines(QStringList lines, MessageLevel::Enum level);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
*
|
*
|
||||||
@@ -33,9 +33,9 @@ void LaunchTask::init()
|
|||||||
m_instance->setRunning(true);
|
m_instance->setRunning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_qobject_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
|
std::shared_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
|
||||||
{
|
{
|
||||||
shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
|
std::shared_ptr<LaunchTask> proc(new LaunchTask(inst));
|
||||||
proc->init();
|
proc->init();
|
||||||
return proc;
|
return proc;
|
||||||
}
|
}
|
||||||
@@ -44,12 +44,12 @@ LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchTask::appendStep(shared_qobject_ptr<LaunchStep> step)
|
void LaunchTask::appendStep(std::shared_ptr<LaunchStep> step)
|
||||||
{
|
{
|
||||||
m_steps.append(step);
|
m_steps.append(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaunchTask::prependStep(shared_qobject_ptr<LaunchStep> step)
|
void LaunchTask::prependStep(std::shared_ptr<LaunchStep> step)
|
||||||
{
|
{
|
||||||
m_steps.prepend(step);
|
m_steps.prepend(step);
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ void LaunchTask::onStepFinished()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto step = m_steps[currentStep];
|
auto step = m_steps[currentStep];
|
||||||
if(step->wasSuccessful())
|
if(step->successful())
|
||||||
{
|
{
|
||||||
// end?
|
// end?
|
||||||
if(currentStep == m_steps.size() - 1)
|
if(currentStep == m_steps.size() - 1)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
*
|
*
|
||||||
@@ -45,11 +45,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public: /* methods */
|
public: /* methods */
|
||||||
static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
|
static std::shared_ptr<LaunchTask> create(InstancePtr inst);
|
||||||
virtual ~LaunchTask() {};
|
virtual ~LaunchTask() {};
|
||||||
|
|
||||||
void appendStep(shared_qobject_ptr<LaunchStep> step);
|
void appendStep(std::shared_ptr<LaunchStep> step);
|
||||||
void prependStep(shared_qobject_ptr<LaunchStep> step);
|
void prependStep(std::shared_ptr<LaunchStep> step);
|
||||||
void setCensorFilter(QMap<QString, QString> filter);
|
void setCensorFilter(QMap<QString, QString> filter);
|
||||||
|
|
||||||
InstancePtr instance()
|
InstancePtr instance()
|
||||||
@@ -117,7 +117,7 @@ private: /*methods */
|
|||||||
protected: /* data */
|
protected: /* data */
|
||||||
InstancePtr m_instance;
|
InstancePtr m_instance;
|
||||||
shared_qobject_ptr<LogModel> m_logModel;
|
shared_qobject_ptr<LogModel> m_logModel;
|
||||||
QList <shared_qobject_ptr<LaunchStep>> m_steps;
|
QList <std::shared_ptr<LaunchStep>> m_steps;
|
||||||
QMap<QString, QString> m_censorFilter;
|
QMap<QString, QString> m_censorFilter;
|
||||||
int currentStep = -1;
|
int currentStep = -1;
|
||||||
State state = NotStarted;
|
State state = NotStarted;
|
||||||
|
|||||||
@@ -69,11 +69,6 @@ void LogModel::suspend(bool suspend)
|
|||||||
m_suspended = suspend;
|
m_suspended = suspend;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LogModel::suspended()
|
|
||||||
{
|
|
||||||
return m_suspended;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogModel::clear()
|
void LogModel::clear()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
@@ -152,16 +147,3 @@ void LogModel::setOverflowMessage(const QString& overflowMessage)
|
|||||||
{
|
{
|
||||||
m_overflowMessage = overflowMessage;
|
m_overflowMessage = overflowMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogModel::setLineWrap(bool state)
|
|
||||||
{
|
|
||||||
if(m_lineWrap != state)
|
|
||||||
{
|
|
||||||
m_lineWrap = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LogModel::wrapLines() const
|
|
||||||
{
|
|
||||||
return m_lineWrap;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ public:
|
|||||||
|
|
||||||
void append(MessageLevel::Enum, QString line);
|
void append(MessageLevel::Enum, QString line);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void suspend(bool suspend);
|
void suspend(bool suspend);
|
||||||
bool suspended();
|
|
||||||
|
|
||||||
QString toPlainText();
|
QString toPlainText();
|
||||||
|
|
||||||
@@ -28,9 +26,6 @@ public:
|
|||||||
void setStopOnOverflow(bool stop);
|
void setStopOnOverflow(bool stop);
|
||||||
void setOverflowMessage(const QString & overflowMessage);
|
void setOverflowMessage(const QString & overflowMessage);
|
||||||
|
|
||||||
void setLineWrap(bool state);
|
|
||||||
bool wrapLines() const;
|
|
||||||
|
|
||||||
enum Roles
|
enum Roles
|
||||||
{
|
{
|
||||||
LevelRole = Qt::UserRole
|
LevelRole = Qt::UserRole
|
||||||
@@ -53,7 +48,6 @@ private: /* data */
|
|||||||
bool m_stopOnOverflow = false;
|
bool m_stopOnOverflow = false;
|
||||||
QString m_overflowMessage = "OVERFLOW";
|
QString m_overflowMessage = "OVERFLOW";
|
||||||
bool m_suspended = false;
|
bool m_suspended = false;
|
||||||
bool m_lineWrap = true;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(LogModel)
|
Q_DISABLE_COPY(LogModel)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,8 +23,6 @@ class PostLaunchCommand: public LaunchStep
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit PostLaunchCommand(LaunchTask *parent);
|
explicit PostLaunchCommand(LaunchTask *parent);
|
||||||
virtual ~PostLaunchCommand() {};
|
|
||||||
|
|
||||||
virtual void executeTask();
|
virtual void executeTask();
|
||||||
virtual bool abort();
|
virtual bool abort();
|
||||||
virtual bool canAbort() const
|
virtual bool canAbort() const
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,8 +23,6 @@ class PreLaunchCommand: public LaunchStep
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit PreLaunchCommand(LaunchTask *parent);
|
explicit PreLaunchCommand(LaunchTask *parent);
|
||||||
virtual ~PreLaunchCommand() {};
|
|
||||||
|
|
||||||
virtual void executeTask();
|
virtual void executeTask();
|
||||||
virtual bool abort();
|
virtual bool abort();
|
||||||
virtual bool canAbort() const
|
virtual bool canAbort() const
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,7 +23,7 @@ void Update::executeTask()
|
|||||||
emitFailed(tr("Task aborted."));
|
emitFailed(tr("Task aborted."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
|
m_updateTask.reset(m_parent->instance()->createUpdateTask());
|
||||||
if(m_updateTask)
|
if(m_updateTask)
|
||||||
{
|
{
|
||||||
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
|
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
|
||||||
@@ -42,14 +42,14 @@ void Update::proceed()
|
|||||||
|
|
||||||
void Update::updateFinished()
|
void Update::updateFinished()
|
||||||
{
|
{
|
||||||
if(m_updateTask->wasSuccessful())
|
if(m_updateTask->successful())
|
||||||
{
|
{
|
||||||
m_updateTask.reset();
|
m_updateTask.reset();
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QString reason = tr("Instance update failed because: %1\n\n").arg(m_updateTask->failReason());
|
QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason());
|
||||||
m_updateTask.reset();
|
m_updateTask.reset();
|
||||||
emit logLine(reason, MessageLevel::Fatal);
|
emit logLine(reason, MessageLevel::Fatal);
|
||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,14 +19,13 @@
|
|||||||
#include <QObjectPtr.h>
|
#include <QObjectPtr.h>
|
||||||
#include <LoggedProcess.h>
|
#include <LoggedProcess.h>
|
||||||
#include <java/JavaChecker.h>
|
#include <java/JavaChecker.h>
|
||||||
#include <net/Mode.h>
|
|
||||||
|
|
||||||
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
|
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
|
||||||
class Update: public LaunchStep
|
class Update: public LaunchStep
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
|
explicit Update(LaunchTask *parent):LaunchStep(parent) {};
|
||||||
virtual ~Update() {};
|
virtual ~Update() {};
|
||||||
|
|
||||||
void executeTask() override;
|
void executeTask() override;
|
||||||
@@ -41,5 +40,4 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
shared_qobject_ptr<Task> m_updateTask;
|
shared_qobject_ptr<Task> m_updateTask;
|
||||||
bool m_aborted = false;
|
bool m_aborted = false;
|
||||||
Net::Mode m_mode = Net::Mode::Offline;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "BaseEntity.h"
|
|
||||||
|
|
||||||
#include "Json.h"
|
|
||||||
|
|
||||||
#include "net/Download.h"
|
|
||||||
#include "net/HttpMetaCache.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
|
|
||||||
#include "Env.h"
|
|
||||||
#include "Json.h"
|
|
||||||
|
|
||||||
class ParsingValidator : public Net::Validator
|
|
||||||
{
|
|
||||||
public: /* con/des */
|
|
||||||
ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
virtual ~ParsingValidator()
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
public: /* methods */
|
|
||||||
bool init(QNetworkRequest &) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool write(QByteArray & data) override
|
|
||||||
{
|
|
||||||
this->data.append(data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool abort() override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool validate(QNetworkReply &) override
|
|
||||||
{
|
|
||||||
auto fname = m_entity->localFilename();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
qWarning() << "Unable to parse response:" << e.cause();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private: /* data */
|
|
||||||
QByteArray data;
|
|
||||||
Meta::BaseEntity *m_entity;
|
|
||||||
};
|
|
||||||
|
|
||||||
Meta::BaseEntity::~BaseEntity()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl Meta::BaseEntity::url() const
|
|
||||||
{
|
|
||||||
return QUrl("https://meta.multimc.org/v1/").resolved(localFilename());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Meta::BaseEntity::loadLocalFile()
|
|
||||||
{
|
|
||||||
const QString fname = QDir("meta").absoluteFilePath(localFilename());
|
|
||||||
if (!QFile::exists(fname))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// TODO: check if the file has the expected checksum
|
|
||||||
try
|
|
||||||
{
|
|
||||||
parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
|
|
||||||
// just make sure it's gone and we never consider it again.
|
|
||||||
QFile::remove(fname);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::BaseEntity::load(Net::Mode loadType)
|
|
||||||
{
|
|
||||||
// load local file if nothing is loaded yet
|
|
||||||
if(!isLoaded())
|
|
||||||
{
|
|
||||||
if(loadLocalFile())
|
|
||||||
{
|
|
||||||
m_loadStatus = LoadStatus::Local;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we need remote update, run the update task
|
|
||||||
if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()));
|
|
||||||
auto url = this->url();
|
|
||||||
auto entry = ENV.metacache()->resolveEntry("meta", localFilename());
|
|
||||||
entry->setStale(true);
|
|
||||||
auto dl = Net::Download::makeCached(url, entry);
|
|
||||||
/*
|
|
||||||
* The validator parses the file and loads it into the object.
|
|
||||||
* If that fails, the file is not written to storage.
|
|
||||||
*/
|
|
||||||
dl->addValidator(new ParsingValidator(this));
|
|
||||||
job->addNetAction(dl);
|
|
||||||
m_updateStatus = UpdateStatus::InProgress;
|
|
||||||
m_updateTask.reset(job);
|
|
||||||
QObject::connect(job, &NetJob::succeeded, [&]()
|
|
||||||
{
|
|
||||||
m_loadStatus = LoadStatus::Remote;
|
|
||||||
m_updateStatus = UpdateStatus::Succeeded;
|
|
||||||
m_updateTask.reset();
|
|
||||||
});
|
|
||||||
QObject::connect(job, &NetJob::failed, [&]()
|
|
||||||
{
|
|
||||||
m_updateStatus = UpdateStatus::Failed;
|
|
||||||
m_updateTask.reset();
|
|
||||||
});
|
|
||||||
m_updateTask->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Meta::BaseEntity::isLoaded() const
|
|
||||||
{
|
|
||||||
return m_loadStatus > LoadStatus::NotLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Meta::BaseEntity::shouldStartRemoteUpdate() const
|
|
||||||
{
|
|
||||||
// TODO: version-locks and offline mode?
|
|
||||||
return m_updateStatus != UpdateStatus::InProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_qobject_ptr<Task> Meta::BaseEntity::getCurrentTask()
|
|
||||||
{
|
|
||||||
if(m_updateStatus == UpdateStatus::InProgress)
|
|
||||||
{
|
|
||||||
return m_updateTask;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QObject>
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
|
||||||
#include "net/Mode.h"
|
|
||||||
|
|
||||||
class Task;
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
class MULTIMC_LOGIC_EXPORT BaseEntity
|
|
||||||
{
|
|
||||||
public: /* types */
|
|
||||||
using Ptr = std::shared_ptr<BaseEntity>;
|
|
||||||
enum class LoadStatus
|
|
||||||
{
|
|
||||||
NotLoaded,
|
|
||||||
Local,
|
|
||||||
Remote
|
|
||||||
};
|
|
||||||
enum class UpdateStatus
|
|
||||||
{
|
|
||||||
NotDone,
|
|
||||||
InProgress,
|
|
||||||
Failed,
|
|
||||||
Succeeded
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~BaseEntity();
|
|
||||||
|
|
||||||
virtual void parse(const QJsonObject &obj) = 0;
|
|
||||||
|
|
||||||
virtual QString localFilename() const = 0;
|
|
||||||
virtual QUrl url() const;
|
|
||||||
|
|
||||||
bool isLoaded() const;
|
|
||||||
bool shouldStartRemoteUpdate() const;
|
|
||||||
|
|
||||||
void load(Net::Mode loadType);
|
|
||||||
shared_qobject_ptr<Task> getCurrentTask();
|
|
||||||
|
|
||||||
protected: /* methods */
|
|
||||||
bool loadLocalFile();
|
|
||||||
|
|
||||||
private:
|
|
||||||
LoadStatus m_loadStatus = LoadStatus::NotLoaded;
|
|
||||||
UpdateStatus m_updateStatus = UpdateStatus::NotDone;
|
|
||||||
shared_qobject_ptr<Task> m_updateTask;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Index.h"
|
|
||||||
|
|
||||||
#include "VersionList.h"
|
|
||||||
#include "JsonFormat.h"
|
|
||||||
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
Index::Index(QObject *parent)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
Index::Index(const QVector<VersionListPtr> &lists, QObject *parent)
|
|
||||||
: QAbstractListModel(parent), m_lists(lists)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < m_lists.size(); ++i)
|
|
||||||
{
|
|
||||||
m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i));
|
|
||||||
connectVersionList(i, m_lists.at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant Index::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size())
|
|
||||||
{
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
VersionListPtr list = m_lists.at(index.row());
|
|
||||||
switch (role)
|
|
||||||
{
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
switch (index.column())
|
|
||||||
{
|
|
||||||
case 0: return list->humanReadable();
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
case UidRole: return list->uid();
|
|
||||||
case NameRole: return list->name();
|
|
||||||
case ListPtrRole: return QVariant::fromValue(list);
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
int Index::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
return m_lists.size();
|
|
||||||
}
|
|
||||||
int Index::columnCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const
|
|
||||||
{
|
|
||||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0)
|
|
||||||
{
|
|
||||||
return tr("Name");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Index::hasUid(const QString &uid) const
|
|
||||||
{
|
|
||||||
return m_uids.contains(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
VersionListPtr Index::get(const QString &uid)
|
|
||||||
{
|
|
||||||
VersionListPtr out = m_uids.value(uid, nullptr);
|
|
||||||
if(!out)
|
|
||||||
{
|
|
||||||
out = std::make_shared<VersionList>(uid);
|
|
||||||
m_uids[uid] = out;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
VersionPtr Index::get(const QString &uid, const QString &version)
|
|
||||||
{
|
|
||||||
auto list = get(uid);
|
|
||||||
return list->getVersion(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Index::parse(const QJsonObject& obj)
|
|
||||||
{
|
|
||||||
parseIndex(obj, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Index::merge(const std::shared_ptr<Index> &other)
|
|
||||||
{
|
|
||||||
const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
|
|
||||||
// initial load, no need to merge
|
|
||||||
if (m_lists.isEmpty())
|
|
||||||
{
|
|
||||||
beginResetModel();
|
|
||||||
m_lists = lists;
|
|
||||||
for (int i = 0; i < lists.size(); ++i)
|
|
||||||
{
|
|
||||||
m_uids.insert(lists.at(i)->uid(), lists.at(i));
|
|
||||||
connectVersionList(i, lists.at(i));
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const VersionListPtr &list : lists)
|
|
||||||
{
|
|
||||||
if (m_uids.contains(list->uid()))
|
|
||||||
{
|
|
||||||
m_uids[list->uid()]->mergeFromIndex(list);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
beginInsertRows(QModelIndex(), m_lists.size(), m_lists.size());
|
|
||||||
connectVersionList(m_lists.size(), list);
|
|
||||||
m_lists.append(list);
|
|
||||||
m_uids.insert(list->uid(), list);
|
|
||||||
endInsertRows();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Index::connectVersionList(const int row, const VersionListPtr &list)
|
|
||||||
{
|
|
||||||
connect(list.get(), &VersionList::nameChanged, this, [this, row]()
|
|
||||||
{
|
|
||||||
emit dataChanged(index(row), index(row), QVector<int>() << Qt::DisplayRole);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "BaseEntity.h"
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
|
||||||
|
|
||||||
class Task;
|
|
||||||
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
using VersionListPtr = std::shared_ptr<class VersionList>;
|
|
||||||
using VersionPtr = std::shared_ptr<class Version>;
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT Index : public QAbstractListModel, public BaseEntity
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit Index(QObject *parent = nullptr);
|
|
||||||
explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
UidRole = Qt::UserRole,
|
|
||||||
NameRole,
|
|
||||||
ListPtrRole
|
|
||||||
};
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
|
||||||
int rowCount(const QModelIndex &parent) const override;
|
|
||||||
int columnCount(const QModelIndex &parent) const override;
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
|
||||||
|
|
||||||
QString localFilename() const override { return "index.json"; }
|
|
||||||
|
|
||||||
// queries
|
|
||||||
VersionListPtr get(const QString &uid);
|
|
||||||
VersionPtr get(const QString &uid, const QString &version);
|
|
||||||
bool hasUid(const QString &uid) const;
|
|
||||||
|
|
||||||
QVector<VersionListPtr> lists() const { return m_lists; }
|
|
||||||
|
|
||||||
public: // for usage by parsers only
|
|
||||||
void merge(const std::shared_ptr<Index> &other);
|
|
||||||
void parse(const QJsonObject &obj) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QVector<VersionListPtr> m_lists;
|
|
||||||
QHash<QString, VersionListPtr> m_uids;
|
|
||||||
|
|
||||||
void connectVersionList(const int row, const VersionListPtr &list);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#include <QTest>
|
|
||||||
#include "TestUtil.h"
|
|
||||||
|
|
||||||
#include "meta/Index.h"
|
|
||||||
#include "meta/VersionList.h"
|
|
||||||
#include "Env.h"
|
|
||||||
|
|
||||||
class IndexTest : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
private
|
|
||||||
slots:
|
|
||||||
void test_isProvidedByEnv()
|
|
||||||
{
|
|
||||||
QVERIFY(ENV.metadataIndex());
|
|
||||||
QCOMPARE(ENV.metadataIndex(), ENV.metadataIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_hasUid_and_getList()
|
|
||||||
{
|
|
||||||
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
|
|
||||||
QVERIFY(windex.hasUid("list1"));
|
|
||||||
QVERIFY(!windex.hasUid("asdf"));
|
|
||||||
QVERIFY(windex.get("list2") != nullptr);
|
|
||||||
QCOMPARE(windex.get("list2")->uid(), QString("list2"));
|
|
||||||
QVERIFY(windex.get("adsf") != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_merge()
|
|
||||||
{
|
|
||||||
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
|
|
||||||
QCOMPARE(windex.lists().size(), 3);
|
|
||||||
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")})));
|
|
||||||
QCOMPARE(windex.lists().size(), 3);
|
|
||||||
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list4"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list5")})));
|
|
||||||
QCOMPARE(windex.lists().size(), 5);
|
|
||||||
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list6")})));
|
|
||||||
QCOMPARE(windex.lists().size(), 6);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(IndexTest)
|
|
||||||
|
|
||||||
#include "Index_test.moc"
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JsonFormat.h"
|
|
||||||
|
|
||||||
// FIXME: remove this from here... somehow
|
|
||||||
#include "minecraft/OneSixVersionFormat.h"
|
|
||||||
#include "Json.h"
|
|
||||||
|
|
||||||
#include "Index.h"
|
|
||||||
#include "Version.h"
|
|
||||||
#include "VersionList.h"
|
|
||||||
|
|
||||||
using namespace Json;
|
|
||||||
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
|
|
||||||
MetadataVersion currentFormatVersion()
|
|
||||||
{
|
|
||||||
return MetadataVersion::InitialRelease;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index
|
|
||||||
static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj)
|
|
||||||
{
|
|
||||||
const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
|
|
||||||
QVector<VersionListPtr> lists;
|
|
||||||
lists.reserve(objects.size());
|
|
||||||
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
|
|
||||||
{
|
|
||||||
VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid"));
|
|
||||||
list->setName(ensureString(obj, "name", QString()));
|
|
||||||
return list;
|
|
||||||
});
|
|
||||||
return std::make_shared<Index>(lists);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version
|
|
||||||
static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)
|
|
||||||
{
|
|
||||||
VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version"));
|
|
||||||
version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
|
|
||||||
version->setType(ensureString(obj, "type", QString()));
|
|
||||||
version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
|
|
||||||
version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
|
|
||||||
RequireSet requires, conflicts;
|
|
||||||
parseRequires(obj, &requires, "requires");
|
|
||||||
parseRequires(obj, &conflicts, "conflicts");
|
|
||||||
version->setRequires(requires, conflicts);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::shared_ptr<Version> parseVersionInternal(const QJsonObject &obj)
|
|
||||||
{
|
|
||||||
VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
|
|
||||||
|
|
||||||
version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
|
|
||||||
QString("%1/%2.json").arg(version->uid(), version->version()),
|
|
||||||
obj.contains("order")));
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version list / package
|
|
||||||
static std::shared_ptr<VersionList> parseVersionListInternal(const QJsonObject &obj)
|
|
||||||
{
|
|
||||||
const QString uid = requireString(obj, "uid");
|
|
||||||
|
|
||||||
const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
|
|
||||||
QVector<VersionPtr> versions;
|
|
||||||
versions.reserve(versionsRaw.size());
|
|
||||||
std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)
|
|
||||||
{
|
|
||||||
auto version = parseCommonVersion(uid, vObj);
|
|
||||||
version->setProvidesRecommendations();
|
|
||||||
return version;
|
|
||||||
});
|
|
||||||
|
|
||||||
VersionListPtr list = std::make_shared<VersionList>(uid);
|
|
||||||
list->setName(ensureString(obj, "name", QString()));
|
|
||||||
list->setVersions(versions);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required)
|
|
||||||
{
|
|
||||||
if (!obj.contains("formatVersion"))
|
|
||||||
{
|
|
||||||
if(required)
|
|
||||||
{
|
|
||||||
return MetadataVersion::Invalid;
|
|
||||||
}
|
|
||||||
return MetadataVersion::InitialRelease;
|
|
||||||
}
|
|
||||||
if (!obj.value("formatVersion").isDouble())
|
|
||||||
{
|
|
||||||
return MetadataVersion::Invalid;
|
|
||||||
}
|
|
||||||
switch(obj.value("formatVersion").toInt())
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
return MetadataVersion::InitialRelease;
|
|
||||||
default:
|
|
||||||
return MetadataVersion::Invalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void serializeFormatVersion(QJsonObject& obj, Meta::MetadataVersion version)
|
|
||||||
{
|
|
||||||
if(version == MetadataVersion::Invalid)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
obj.insert("formatVersion", int(version));
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseIndex(const QJsonObject &obj, Index *ptr)
|
|
||||||
{
|
|
||||||
const MetadataVersion version = parseFormatVersion(obj);
|
|
||||||
switch (version)
|
|
||||||
{
|
|
||||||
case MetadataVersion::InitialRelease:
|
|
||||||
ptr->merge(parseIndexInternal(obj));
|
|
||||||
break;
|
|
||||||
case MetadataVersion::Invalid:
|
|
||||||
throw ParseException(QObject::tr("Unknown format version!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseVersionList(const QJsonObject &obj, VersionList *ptr)
|
|
||||||
{
|
|
||||||
const MetadataVersion version = parseFormatVersion(obj);
|
|
||||||
switch (version)
|
|
||||||
{
|
|
||||||
case MetadataVersion::InitialRelease:
|
|
||||||
ptr->merge(parseVersionListInternal(obj));
|
|
||||||
break;
|
|
||||||
case MetadataVersion::Invalid:
|
|
||||||
throw ParseException(QObject::tr("Unknown format version!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseVersion(const QJsonObject &obj, Version *ptr)
|
|
||||||
{
|
|
||||||
const MetadataVersion version = parseFormatVersion(obj);
|
|
||||||
switch (version)
|
|
||||||
{
|
|
||||||
case MetadataVersion::InitialRelease:
|
|
||||||
ptr->merge(parseVersionInternal(obj));
|
|
||||||
break;
|
|
||||||
case MetadataVersion::Invalid:
|
|
||||||
throw ParseException(QObject::tr("Unknown format version!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
[
|
|
||||||
{"uid":"foo", "equals":"version"}
|
|
||||||
]
|
|
||||||
*/
|
|
||||||
void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char * keyName)
|
|
||||||
{
|
|
||||||
if(obj.contains(keyName))
|
|
||||||
{
|
|
||||||
QSet<QString> requires;
|
|
||||||
auto reqArray = requireArray(obj, keyName);
|
|
||||||
auto iter = reqArray.begin();
|
|
||||||
while(iter != reqArray.end())
|
|
||||||
{
|
|
||||||
auto reqObject = requireObject(*iter);
|
|
||||||
auto uid = requireString(reqObject, "uid");
|
|
||||||
auto equals = ensureString(reqObject, "equals", QString());
|
|
||||||
auto suggests = ensureString(reqObject, "suggests", QString());
|
|
||||||
ptr->insert({uid, equals, suggests});
|
|
||||||
iter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char * keyName)
|
|
||||||
{
|
|
||||||
if(!ptr || ptr->empty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QJsonArray arrOut;
|
|
||||||
for(auto &iter: *ptr)
|
|
||||||
{
|
|
||||||
QJsonObject reqOut;
|
|
||||||
reqOut.insert("uid", iter.uid);
|
|
||||||
if(!iter.equalsVersion.isEmpty())
|
|
||||||
{
|
|
||||||
reqOut.insert("equals", iter.equalsVersion);
|
|
||||||
}
|
|
||||||
if(!iter.suggests.isEmpty())
|
|
||||||
{
|
|
||||||
reqOut.insert("suggests", iter.suggests);
|
|
||||||
}
|
|
||||||
arrOut.append(reqOut);
|
|
||||||
}
|
|
||||||
obj.insert(keyName, arrOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "Exception.h"
|
|
||||||
#include "meta/BaseEntity.h"
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
class Index;
|
|
||||||
class Version;
|
|
||||||
class VersionList;
|
|
||||||
|
|
||||||
enum class MetadataVersion
|
|
||||||
{
|
|
||||||
Invalid = -1,
|
|
||||||
InitialRelease = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
class ParseException : public Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using Exception::Exception;
|
|
||||||
};
|
|
||||||
struct Require
|
|
||||||
{
|
|
||||||
bool operator==(const Require & rhs) const
|
|
||||||
{
|
|
||||||
return uid == rhs.uid;
|
|
||||||
}
|
|
||||||
bool operator<(const Require & rhs) const
|
|
||||||
{
|
|
||||||
return uid < rhs.uid;
|
|
||||||
}
|
|
||||||
bool deepEquals(const Require & rhs) const
|
|
||||||
{
|
|
||||||
return uid == rhs.uid
|
|
||||||
&& equalsVersion == rhs.equalsVersion
|
|
||||||
&& suggests == rhs.suggests;
|
|
||||||
}
|
|
||||||
QString uid;
|
|
||||||
QString equalsVersion;
|
|
||||||
QString suggests;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Q_DECL_PURE_FUNCTION uint qHash(const Require &key, uint seed = 0) Q_DECL_NOTHROW
|
|
||||||
{
|
|
||||||
return qHash(key.uid, seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
using RequireSet = std::set<Require>;
|
|
||||||
|
|
||||||
void parseIndex(const QJsonObject &obj, Index *ptr);
|
|
||||||
void parseVersion(const QJsonObject &obj, Version *ptr);
|
|
||||||
void parseVersionList(const QJsonObject &obj, VersionList *ptr);
|
|
||||||
|
|
||||||
MetadataVersion parseFormatVersion(const QJsonObject &obj, bool required = true);
|
|
||||||
void serializeFormatVersion(QJsonObject &obj, MetadataVersion version);
|
|
||||||
|
|
||||||
// FIXME: this has a different shape than the others...FIX IT!?
|
|
||||||
void parseRequires(const QJsonObject &obj, RequireSet * ptr, const char * keyName = "requires");
|
|
||||||
void serializeRequires(QJsonObject & objOut, RequireSet* ptr, const char * keyName = "requires");
|
|
||||||
MetadataVersion currentFormatVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::set<Meta::Require>)
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Version.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
|
|
||||||
#include "JsonFormat.h"
|
|
||||||
#include "minecraft/ComponentList.h"
|
|
||||||
|
|
||||||
Meta::Version::Version(const QString &uid, const QString &version)
|
|
||||||
: BaseVersion(), m_uid(uid), m_version(version)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Meta::Version::~Version()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Meta::Version::descriptor()
|
|
||||||
{
|
|
||||||
return m_version;
|
|
||||||
}
|
|
||||||
QString Meta::Version::name()
|
|
||||||
{
|
|
||||||
if(m_data)
|
|
||||||
return m_data->name;
|
|
||||||
return m_uid;
|
|
||||||
}
|
|
||||||
QString Meta::Version::typeString() const
|
|
||||||
{
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDateTime Meta::Version::time() const
|
|
||||||
{
|
|
||||||
return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::parse(const QJsonObject& obj)
|
|
||||||
{
|
|
||||||
parseVersion(obj, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::mergeFromList(const Meta::VersionPtr& other)
|
|
||||||
{
|
|
||||||
if(other->m_providesRecommendations)
|
|
||||||
{
|
|
||||||
if(m_recommended != other->m_recommended)
|
|
||||||
{
|
|
||||||
setRecommended(other->m_recommended);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_type != other->m_type)
|
|
||||||
{
|
|
||||||
setType(other->m_type);
|
|
||||||
}
|
|
||||||
if (m_time != other->m_time)
|
|
||||||
{
|
|
||||||
setTime(other->m_time);
|
|
||||||
}
|
|
||||||
if (m_requires != other->m_requires)
|
|
||||||
{
|
|
||||||
m_requires = other->m_requires;
|
|
||||||
}
|
|
||||||
if (m_conflicts != other->m_conflicts)
|
|
||||||
{
|
|
||||||
m_conflicts = other->m_conflicts;
|
|
||||||
}
|
|
||||||
if(m_volatile != other->m_volatile)
|
|
||||||
{
|
|
||||||
setVolatile(other->m_volatile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::merge(const VersionPtr &other)
|
|
||||||
{
|
|
||||||
mergeFromList(other);
|
|
||||||
if(other->m_data)
|
|
||||||
{
|
|
||||||
setData(other->m_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Meta::Version::localFilename() const
|
|
||||||
{
|
|
||||||
return m_uid + '/' + m_version + ".json";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::setType(const QString &type)
|
|
||||||
{
|
|
||||||
m_type = type;
|
|
||||||
emit typeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::setTime(const qint64 time)
|
|
||||||
{
|
|
||||||
m_time = time;
|
|
||||||
emit timeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts)
|
|
||||||
{
|
|
||||||
m_requires = requires;
|
|
||||||
m_conflicts = conflicts;
|
|
||||||
emit requiresChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::setVolatile(bool volatile_)
|
|
||||||
{
|
|
||||||
m_volatile = volatile_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Meta::Version::setData(const VersionFilePtr &data)
|
|
||||||
{
|
|
||||||
m_data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::setProvidesRecommendations()
|
|
||||||
{
|
|
||||||
m_providesRecommendations = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Meta::Version::setRecommended(bool recommended)
|
|
||||||
{
|
|
||||||
m_recommended = recommended;
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QVector>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "minecraft/VersionFile.h"
|
|
||||||
|
|
||||||
#include "BaseEntity.h"
|
|
||||||
|
|
||||||
#include "multimc_logic_export.h"
|
|
||||||
|
|
||||||
#include "JsonFormat.h"
|
|
||||||
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
using VersionPtr = std::shared_ptr<class Version>;
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT Version : public QObject, public BaseVersion, public BaseEntity
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public: /* con/des */
|
|
||||||
explicit Version(const QString &uid, const QString &version);
|
|
||||||
virtual ~Version();
|
|
||||||
|
|
||||||
QString descriptor() override;
|
|
||||||
QString name() override;
|
|
||||||
QString typeString() const override;
|
|
||||||
|
|
||||||
QString uid() const
|
|
||||||
{
|
|
||||||
return m_uid;
|
|
||||||
}
|
|
||||||
QString version() const
|
|
||||||
{
|
|
||||||
return m_version;
|
|
||||||
}
|
|
||||||
QString type() const
|
|
||||||
{
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
QDateTime time() const;
|
|
||||||
qint64 rawTime() const
|
|
||||||
{
|
|
||||||
return m_time;
|
|
||||||
}
|
|
||||||
const Meta::RequireSet &requires() const
|
|
||||||
{
|
|
||||||
return m_requires;
|
|
||||||
}
|
|
||||||
VersionFilePtr data() const
|
|
||||||
{
|
|
||||||
return m_data;
|
|
||||||
}
|
|
||||||
bool isRecommended() const
|
|
||||||
{
|
|
||||||
return m_recommended;
|
|
||||||
}
|
|
||||||
bool isLoaded() const
|
|
||||||
{
|
|
||||||
return m_data != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void merge(const VersionPtr &other);
|
|
||||||
void mergeFromList(const VersionPtr &other);
|
|
||||||
void parse(const QJsonObject &obj) override;
|
|
||||||
|
|
||||||
QString localFilename() const override;
|
|
||||||
|
|
||||||
public: // for usage by format parsers only
|
|
||||||
void setType(const QString &type);
|
|
||||||
void setTime(const qint64 time);
|
|
||||||
void setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts);
|
|
||||||
void setVolatile(bool volatile_);
|
|
||||||
void setRecommended(bool recommended);
|
|
||||||
void setProvidesRecommendations();
|
|
||||||
void setData(const VersionFilePtr &data);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void typeChanged();
|
|
||||||
void timeChanged();
|
|
||||||
void requiresChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_providesRecommendations = false;
|
|
||||||
bool m_recommended = false;
|
|
||||||
QString m_name;
|
|
||||||
QString m_uid;
|
|
||||||
QString m_version;
|
|
||||||
QString m_type;
|
|
||||||
qint64 m_time = 0;
|
|
||||||
Meta::RequireSet m_requires;
|
|
||||||
Meta::RequireSet m_conflicts;
|
|
||||||
bool m_volatile = false;
|
|
||||||
VersionFilePtr m_data;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Meta::VersionPtr)
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "VersionList.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
|
|
||||||
#include "Version.h"
|
|
||||||
#include "JsonFormat.h"
|
|
||||||
#include "Version.h"
|
|
||||||
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
VersionList::VersionList(const QString &uid, QObject *parent)
|
|
||||||
: BaseVersionList(parent), m_uid(uid)
|
|
||||||
{
|
|
||||||
setObjectName("Version list: " + uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_qobject_ptr<Task> VersionList::getLoadTask()
|
|
||||||
{
|
|
||||||
load(Net::Mode::Online);
|
|
||||||
return getCurrentTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VersionList::isLoaded()
|
|
||||||
{
|
|
||||||
return BaseEntity::isLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
const BaseVersionPtr VersionList::at(int i) const
|
|
||||||
{
|
|
||||||
return m_versions.at(i);
|
|
||||||
}
|
|
||||||
int VersionList::count() const
|
|
||||||
{
|
|
||||||
return m_versions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionList::sortVersions()
|
|
||||||
{
|
|
||||||
beginResetModel();
|
|
||||||
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
|
|
||||||
{
|
|
||||||
return *a.get() < *b.get();
|
|
||||||
});
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant VersionList::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid())
|
|
||||||
{
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
VersionPtr version = m_versions.at(index.row());
|
|
||||||
|
|
||||||
switch (role)
|
|
||||||
{
|
|
||||||
case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast<BaseVersion>(version));
|
|
||||||
case VersionRole:
|
|
||||||
case VersionIdRole:
|
|
||||||
return version->version();
|
|
||||||
case ParentVersionRole:
|
|
||||||
{
|
|
||||||
// FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent uid'.
|
|
||||||
auto & reqs = version->requires();
|
|
||||||
auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require & req)
|
|
||||||
{
|
|
||||||
return req.uid == "net.minecraft";
|
|
||||||
});
|
|
||||||
if (iter != reqs.end())
|
|
||||||
{
|
|
||||||
return (*iter).equalsVersion;
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
case TypeRole: return version->type();
|
|
||||||
|
|
||||||
case UidRole: return version->uid();
|
|
||||||
case TimeRole: return version->time();
|
|
||||||
case RequiresRole: return QVariant::fromValue(version->requires());
|
|
||||||
case SortRole: return version->rawTime();
|
|
||||||
case VersionPtrRole: return QVariant::fromValue(version);
|
|
||||||
case RecommendedRole: return version->isRecommended();
|
|
||||||
// FIXME: this should be determined in whatever view/proxy is used...
|
|
||||||
// case LatestRole: return version == getLatestStable();
|
|
||||||
default: return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseVersionList::RoleList VersionList::providesRoles() const
|
|
||||||
{
|
|
||||||
return {VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole,
|
|
||||||
TypeRole, UidRole, TimeRole, RequiresRole, SortRole,
|
|
||||||
RecommendedRole, LatestRole, VersionPtrRole};
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> VersionList::roleNames() const
|
|
||||||
{
|
|
||||||
QHash<int, QByteArray> roles = BaseVersionList::roleNames();
|
|
||||||
roles.insert(UidRole, "uid");
|
|
||||||
roles.insert(TimeRole, "time");
|
|
||||||
roles.insert(SortRole, "sort");
|
|
||||||
roles.insert(RequiresRole, "requires");
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString VersionList::localFilename() const
|
|
||||||
{
|
|
||||||
return m_uid + "/index.json";
|
|
||||||
}
|
|
||||||
|
|
||||||
QString VersionList::humanReadable() const
|
|
||||||
{
|
|
||||||
return m_name.isEmpty() ? m_uid : m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
VersionPtr VersionList::getVersion(const QString &version)
|
|
||||||
{
|
|
||||||
VersionPtr out = m_lookup.value(version, nullptr);
|
|
||||||
if(!out)
|
|
||||||
{
|
|
||||||
out = std::make_shared<Version>(m_uid, version);
|
|
||||||
m_lookup[version] = out;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionList::setName(const QString &name)
|
|
||||||
{
|
|
||||||
m_name = name;
|
|
||||||
emit nameChanged(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionList::setVersions(const QVector<VersionPtr> &versions)
|
|
||||||
{
|
|
||||||
beginResetModel();
|
|
||||||
m_versions = versions;
|
|
||||||
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
|
|
||||||
{
|
|
||||||
return a->rawTime() > b->rawTime();
|
|
||||||
});
|
|
||||||
for (int i = 0; i < m_versions.size(); ++i)
|
|
||||||
{
|
|
||||||
m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i));
|
|
||||||
setupAddedVersion(i, m_versions.at(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
|
|
||||||
auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; });
|
|
||||||
m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionList::parse(const QJsonObject& obj)
|
|
||||||
{
|
|
||||||
parseVersionList(obj, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
|
|
||||||
static const Meta::VersionPtr &getBetterVersion(const Meta::VersionPtr &a, const Meta::VersionPtr &b)
|
|
||||||
{
|
|
||||||
if(!a)
|
|
||||||
return b;
|
|
||||||
if(!b)
|
|
||||||
return a;
|
|
||||||
if(a->type() == b->type())
|
|
||||||
{
|
|
||||||
// newer of same type wins
|
|
||||||
return (a->rawTime() > b->rawTime() ? a : b);
|
|
||||||
}
|
|
||||||
// 'release' type wins
|
|
||||||
return (a->type() == "release" ? a : b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionList::mergeFromIndex(const VersionListPtr &other)
|
|
||||||
{
|
|
||||||
if (m_name != other->m_name)
|
|
||||||
{
|
|
||||||
setName(other->m_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionList::merge(const VersionListPtr &other)
|
|
||||||
{
|
|
||||||
if (m_name != other->m_name)
|
|
||||||
{
|
|
||||||
setName(other->m_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: do not reset the whole model. maybe?
|
|
||||||
beginResetModel();
|
|
||||||
m_versions.clear();
|
|
||||||
if(other->m_versions.isEmpty())
|
|
||||||
{
|
|
||||||
qWarning() << "Empty list loaded ...";
|
|
||||||
}
|
|
||||||
for (const VersionPtr &version : other->m_versions)
|
|
||||||
{
|
|
||||||
// we already have the version. merge the contents
|
|
||||||
if (m_lookup.contains(version->version()))
|
|
||||||
{
|
|
||||||
m_lookup.value(version->version())->mergeFromList(version);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_lookup.insert(version->uid(), version);
|
|
||||||
}
|
|
||||||
// connect it.
|
|
||||||
setupAddedVersion(m_versions.size(), version);
|
|
||||||
m_versions.append(version);
|
|
||||||
m_recommended = getBetterVersion(m_recommended, version);
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionList::setupAddedVersion(const int row, const VersionPtr &version)
|
|
||||||
{
|
|
||||||
// FIXME: do not disconnect from everythin, disconnect only the lambdas here
|
|
||||||
version->disconnect();
|
|
||||||
connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
|
|
||||||
connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
|
|
||||||
connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseVersionPtr VersionList::getRecommended() const
|
|
||||||
{
|
|
||||||
return m_recommended;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
/* Copyright 2015-2019 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BaseEntity.h"
|
|
||||||
#include "BaseVersionList.h"
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace Meta
|
|
||||||
{
|
|
||||||
using VersionPtr = std::shared_ptr<class Version>;
|
|
||||||
using VersionListPtr = std::shared_ptr<class VersionList>;
|
|
||||||
|
|
||||||
class MULTIMC_LOGIC_EXPORT VersionList : public BaseVersionList, public BaseEntity
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QString uid READ uid CONSTANT)
|
|
||||||
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
|
||||||
public:
|
|
||||||
explicit VersionList(const QString &uid, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
enum Roles
|
|
||||||
{
|
|
||||||
UidRole = Qt::UserRole + 100,
|
|
||||||
TimeRole,
|
|
||||||
RequiresRole,
|
|
||||||
VersionPtrRole
|
|
||||||
};
|
|
||||||
|
|
||||||
shared_qobject_ptr<Task> getLoadTask() override;
|
|
||||||
bool isLoaded() override;
|
|
||||||
const BaseVersionPtr at(int i) const override;
|
|
||||||
int count() const override;
|
|
||||||
void sortVersions() override;
|
|
||||||
|
|
||||||
BaseVersionPtr getRecommended() const override;
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
|
||||||
RoleList providesRoles() const override;
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
|
||||||
|
|
||||||
QString localFilename() const override;
|
|
||||||
|
|
||||||
QString uid() const
|
|
||||||
{
|
|
||||||
return m_uid;
|
|
||||||
}
|
|
||||||
QString name() const
|
|
||||||
{
|
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
QString humanReadable() const;
|
|
||||||
|
|
||||||
VersionPtr getVersion(const QString &version);
|
|
||||||
|
|
||||||
QVector<VersionPtr> versions() const
|
|
||||||
{
|
|
||||||
return m_versions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public: // for usage only by parsers
|
|
||||||
void setName(const QString &name);
|
|
||||||
void setVersions(const QVector<VersionPtr> &versions);
|
|
||||||
void merge(const VersionListPtr &other);
|
|
||||||
void mergeFromIndex(const VersionListPtr &other);
|
|
||||||
void parse(const QJsonObject &obj) override;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void nameChanged(const QString &name);
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void updateListData(QList<BaseVersionPtr>) override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QVector<VersionPtr> m_versions;
|
|
||||||
QHash<QString, VersionPtr> m_lookup;
|
|
||||||
QString m_uid;
|
|
||||||
QString m_name;
|
|
||||||
|
|
||||||
VersionPtr m_recommended;
|
|
||||||
|
|
||||||
void setupAddedVersion(const int row, const VersionPtr &version);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Q_DECLARE_METATYPE(Meta::VersionListPtr)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,34 +27,6 @@
|
|||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "net/Download.h"
|
#include "net/Download.h"
|
||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
#include "net/URLConstants.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
QSet<QString> collectPathsFromDir(QString dirPath)
|
|
||||||
{
|
|
||||||
QFileInfo dirInfo(dirPath);
|
|
||||||
|
|
||||||
if (!dirInfo.exists())
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QSet<QString> out;
|
|
||||||
|
|
||||||
QDirIterator iter(dirPath, QDirIterator::Subdirectories);
|
|
||||||
while (iter.hasNext())
|
|
||||||
{
|
|
||||||
QString value = iter.next();
|
|
||||||
QFileInfo info(value);
|
|
||||||
if(info.isFile())
|
|
||||||
{
|
|
||||||
out.insert(value);
|
|
||||||
qDebug() << value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace AssetsUtils
|
namespace AssetsUtils
|
||||||
@@ -64,7 +36,7 @@ namespace AssetsUtils
|
|||||||
* Returns true on success, with index populated
|
* Returns true on success, with index populated
|
||||||
* index is undefined otherwise
|
* index is undefined otherwise
|
||||||
*/
|
*/
|
||||||
bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsIndex& index)
|
bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
@@ -88,7 +60,7 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd
|
|||||||
qCritical() << "Failed to read assets index file" << path;
|
qCritical() << "Failed to read assets index file" << path;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
index.id = assetsId;
|
index->id = assetsId;
|
||||||
|
|
||||||
// Read the file and close it.
|
// Read the file and close it.
|
||||||
QByteArray jsonData = file.readAll();
|
QByteArray jsonData = file.readAll();
|
||||||
@@ -117,13 +89,7 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd
|
|||||||
QJsonValue isVirtual = root.value("virtual");
|
QJsonValue isVirtual = root.value("virtual");
|
||||||
if (!isVirtual.isUndefined())
|
if (!isVirtual.isUndefined())
|
||||||
{
|
{
|
||||||
index.isVirtual = isVirtual.toBool(false);
|
index->isVirtual = isVirtual.toBool(false);
|
||||||
}
|
|
||||||
|
|
||||||
QJsonValue mapToResources = root.value("map_to_resources");
|
|
||||||
if (!mapToResources.isUndefined())
|
|
||||||
{
|
|
||||||
index.mapToResources = mapToResources.toBool(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonValue objects = root.value("objects");
|
QJsonValue objects = root.value("objects");
|
||||||
@@ -155,14 +121,13 @@ bool loadAssetsIndexJson(const QString &assetsId, const QString &path, AssetsInd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
index.objects.insert(iter.key(), object);
|
index->objects.insert(iter.key(), object);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: ugly code duplication
|
QDir reconstructAssets(QString assetsId)
|
||||||
QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder)
|
|
||||||
{
|
{
|
||||||
QDir assetsDir = QDir("assets/");
|
QDir assetsDir = QDir("assets/");
|
||||||
QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
|
QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
|
||||||
@@ -175,77 +140,24 @@ QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder)
|
|||||||
|
|
||||||
if (!indexFile.exists())
|
if (!indexFile.exists())
|
||||||
{
|
{
|
||||||
qCritical() << "No assets index file" << indexPath << "; can't determine assets path!";
|
qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets";
|
||||||
return virtualRoot;
|
return virtualRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path()
|
||||||
|
<< objectDir.path() << virtualDir.path() << virtualRoot.path();
|
||||||
|
|
||||||
AssetsIndex index;
|
AssetsIndex index;
|
||||||
if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index))
|
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index);
|
||||||
{
|
|
||||||
qCritical() << "Failed to load asset index file" << indexPath << "; can't determine assets path!";
|
|
||||||
return virtualRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString targetPath;
|
if (loadAssetsIndex && index.isVirtual)
|
||||||
if(index.isVirtual)
|
|
||||||
{
|
{
|
||||||
return virtualRoot;
|
qDebug() << "Reconstructing virtual assets folder at" << virtualRoot.path();
|
||||||
}
|
|
||||||
else if(index.mapToResources)
|
|
||||||
{
|
|
||||||
return QDir(resourcesFolder);
|
|
||||||
}
|
|
||||||
return virtualRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: ugly code duplication
|
|
||||||
bool reconstructAssets(QString assetsId, QString resourcesFolder)
|
|
||||||
{
|
|
||||||
QDir assetsDir = QDir("assets/");
|
|
||||||
QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes"));
|
|
||||||
QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects"));
|
|
||||||
QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual"));
|
|
||||||
|
|
||||||
QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json");
|
|
||||||
QFile indexFile(indexPath);
|
|
||||||
QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId));
|
|
||||||
|
|
||||||
if (!indexFile.exists())
|
|
||||||
{
|
|
||||||
qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets!";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path();
|
|
||||||
|
|
||||||
AssetsIndex index;
|
|
||||||
if(!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index))
|
|
||||||
{
|
|
||||||
qCritical() << "Failed to load asset index file" << indexPath << "; can't reconstruct assets!";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString targetPath;
|
|
||||||
bool removeLeftovers = false;
|
|
||||||
if(index.isVirtual)
|
|
||||||
{
|
|
||||||
targetPath = virtualRoot.path();
|
|
||||||
removeLeftovers = true;
|
|
||||||
qDebug() << "Reconstructing virtual assets folder at" << targetPath;
|
|
||||||
}
|
|
||||||
else if(index.mapToResources)
|
|
||||||
{
|
|
||||||
targetPath = resourcesFolder;
|
|
||||||
qDebug() << "Reconstructing resources folder at" << targetPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetPath.isNull())
|
|
||||||
{
|
|
||||||
auto presentFiles = collectPathsFromDir(targetPath);
|
|
||||||
for (QString map : index.objects.keys())
|
for (QString map : index.objects.keys())
|
||||||
{
|
{
|
||||||
AssetObject asset_object = index.objects.value(map);
|
AssetObject asset_object = index.objects.value(map);
|
||||||
QString target_path = FS::PathCombine(targetPath, map);
|
QString target_path = FS::PathCombine(virtualRoot.path(), map);
|
||||||
QFile target(target_path);
|
QFile target(target_path);
|
||||||
|
|
||||||
QString tlk = asset_object.hash.left(2);
|
QString tlk = asset_object.hash.left(2);
|
||||||
@@ -254,32 +166,24 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder)
|
|||||||
QFile original(original_path);
|
QFile original(original_path);
|
||||||
if (!original.exists())
|
if (!original.exists())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
presentFiles.remove(target_path);
|
|
||||||
|
|
||||||
if (!target.exists())
|
if (!target.exists())
|
||||||
{
|
{
|
||||||
QFileInfo info(target_path);
|
QFileInfo info(target_path);
|
||||||
QDir target_dir = info.dir();
|
QDir target_dir = info.dir();
|
||||||
|
// qDebug() << target_dir;
|
||||||
qDebug() << target_dir.path();
|
if (!target_dir.exists())
|
||||||
FS::ensureFolderPathExists(target_dir.path());
|
QDir("").mkpath(target_dir.path());
|
||||||
|
|
||||||
bool couldCopy = original.copy(target_path);
|
bool couldCopy = original.copy(target_path);
|
||||||
qDebug() << " Copying" << original_path << "to" << target_path << QString::number(couldCopy);
|
qDebug() << " Copying" << original_path << "to" << target_path
|
||||||
|
<< QString::number(couldCopy); // << original.errorString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write last used time to virtualRoot/.lastused
|
// TODO: Write last used time to virtualRoot/.lastused
|
||||||
if(removeLeftovers)
|
|
||||||
{
|
|
||||||
for(auto & file: presentFiles)
|
|
||||||
{
|
|
||||||
qDebug() << "Would remove" << file;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
return virtualRoot;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -308,7 +212,7 @@ QString AssetObject::getLocalPath()
|
|||||||
|
|
||||||
QUrl AssetObject::getUrl()
|
QUrl AssetObject::getUrl()
|
||||||
{
|
{
|
||||||
return URLConstants::RESOURCE_BASE + getRelPath();
|
return QUrl("http://resources.download.minecraft.net/" + getRelPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AssetObject::getRelPath()
|
QString AssetObject::getRelPath()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2013-2019 MultiMC Contributors
|
/* Copyright 2013-2017 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -38,16 +38,11 @@ struct AssetsIndex
|
|||||||
QString id;
|
QString id;
|
||||||
QMap<QString, AssetObject> objects;
|
QMap<QString, AssetObject> objects;
|
||||||
bool isVirtual = false;
|
bool isVirtual = false;
|
||||||
bool mapToResources = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// FIXME: this is absolutely horrendous. REDO!!!!
|
|
||||||
namespace AssetsUtils
|
namespace AssetsUtils
|
||||||
{
|
{
|
||||||
bool loadAssetsIndexJson(const QString &id, const QString &file, AssetsIndex& index);
|
bool loadAssetsIndexJson(QString id, QString file, AssetsIndex* index);
|
||||||
|
|
||||||
QDir getAssetsDir(const QString &assetsId, const QString &resourcesFolder);
|
|
||||||
|
|
||||||
/// Reconstruct a virtual assets folder for the given assets ID and return the folder
|
/// Reconstruct a virtual assets folder for the given assets ID and return the folder
|
||||||
bool reconstructAssets(QString assetsId, QString resourcesFolder);
|
QDir reconstructAssets(QString assetsId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,439 +0,0 @@
|
|||||||
#include <meta/VersionList.h>
|
|
||||||
#include <meta/Index.h>
|
|
||||||
#include <Env.h>
|
|
||||||
#include "Component.h"
|
|
||||||
|
|
||||||
#include "meta/Version.h"
|
|
||||||
#include "VersionFile.h"
|
|
||||||
#include "minecraft/ComponentList.h"
|
|
||||||
#include <FileSystem.h>
|
|
||||||
#include <QSaveFile>
|
|
||||||
#include "OneSixVersionFormat.h"
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
Component::Component(ComponentList * parent, const QString& uid)
|
|
||||||
{
|
|
||||||
assert(parent);
|
|
||||||
m_parent = parent;
|
|
||||||
|
|
||||||
m_uid = uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component::Component(ComponentList * parent, std::shared_ptr<Meta::Version> version)
|
|
||||||
{
|
|
||||||
assert(parent);
|
|
||||||
m_parent = parent;
|
|
||||||
|
|
||||||
m_metaVersion = version;
|
|
||||||
m_uid = version->uid();
|
|
||||||
m_version = m_cachedVersion = version->version();
|
|
||||||
m_cachedName = version->name();
|
|
||||||
m_loaded = version->isLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
Component::Component(ComponentList * parent, const QString& uid, std::shared_ptr<VersionFile> file)
|
|
||||||
{
|
|
||||||
assert(parent);
|
|
||||||
m_parent = parent;
|
|
||||||
|
|
||||||
m_file = file;
|
|
||||||
m_uid = uid;
|
|
||||||
m_cachedVersion = m_file->version;
|
|
||||||
m_cachedName = m_file->name;
|
|
||||||
m_loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Meta::Version> Component::getMeta()
|
|
||||||
{
|
|
||||||
return m_metaVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Component::applyTo(LaunchProfile* profile)
|
|
||||||
{
|
|
||||||
// do not apply disabled components
|
|
||||||
if(!isEnabled())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto vfile = getVersionFile();
|
|
||||||
if(vfile)
|
|
||||||
{
|
|
||||||
vfile->applyTo(profile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
profile->applyProblemSeverity(getProblemSeverity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<class VersionFile> Component::getVersionFile() const
|
|
||||||
{
|
|
||||||
if(m_metaVersion)
|
|
||||||
{
|
|
||||||
if(!m_metaVersion->isLoaded())
|
|
||||||
{
|
|
||||||
m_metaVersion->load(Net::Mode::Online);
|
|
||||||
}
|
|
||||||
return m_metaVersion->data();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return m_file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<class Meta::VersionList> Component::getVersionList() const
|
|
||||||
{
|
|
||||||
// FIXME: what if the metadata index isn't loaded yet?
|
|
||||||
if(ENV.metadataIndex()->hasUid(m_uid))
|
|
||||||
{
|
|
||||||
return ENV.metadataIndex()->get(m_uid);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Component::getOrder()
|
|
||||||
{
|
|
||||||
if(m_orderOverride)
|
|
||||||
return m_order;
|
|
||||||
|
|
||||||
auto vfile = getVersionFile();
|
|
||||||
if(vfile)
|
|
||||||
{
|
|
||||||
return vfile->order;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
void Component::setOrder(int order)
|
|
||||||
{
|
|
||||||
m_orderOverride = true;
|
|
||||||
m_order = order;
|
|
||||||
}
|
|
||||||
QString Component::getID()
|
|
||||||
{
|
|
||||||
return m_uid;
|
|
||||||
}
|
|
||||||
QString Component::getName()
|
|
||||||
{
|
|
||||||
if (!m_cachedName.isEmpty())
|
|
||||||
return m_cachedName;
|
|
||||||
return m_uid;
|
|
||||||
}
|
|
||||||
QString Component::getVersion()
|
|
||||||
{
|
|
||||||
return m_cachedVersion;
|
|
||||||
}
|
|
||||||
QString Component::getFilename()
|
|
||||||
{
|
|
||||||
return m_parent->patchFilePathForUid(m_uid);
|
|
||||||
}
|
|
||||||
QDateTime Component::getReleaseDateTime()
|
|
||||||
{
|
|
||||||
if(m_metaVersion)
|
|
||||||
{
|
|
||||||
return m_metaVersion->time();
|
|
||||||
}
|
|
||||||
auto vfile = getVersionFile();
|
|
||||||
if(vfile)
|
|
||||||
{
|
|
||||||
return vfile->releaseTime;
|
|
||||||
}
|
|
||||||
// FIXME: fake
|
|
||||||
return QDateTime::currentDateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Component::isEnabled()
|
|
||||||
{
|
|
||||||
return !canBeDisabled() || !m_disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Component::canBeDisabled()
|
|
||||||
{
|
|
||||||
return isRemovable() && !m_dependencyOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Component::setEnabled(bool state)
|
|
||||||
{
|
|
||||||
bool intendedDisabled = !state;
|
|
||||||
if (!canBeDisabled())
|
|
||||||
{
|
|
||||||
intendedDisabled = false;
|
|
||||||
}
|
|
||||||
if(intendedDisabled != m_disabled)
|
|
||||||
{
|
|
||||||
m_disabled = intendedDisabled;
|
|
||||||
emit dataChanged();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Component::isCustom()
|
|
||||||
{
|
|
||||||
return m_file != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Component::isCustomizable()
|
|
||||||
{
|
|
||||||
if(m_metaVersion)
|
|
||||||
{
|
|
||||||
if(getVersionFile())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool Component::isRemovable()
|
|
||||||
{
|
|
||||||
return !m_important;
|
|
||||||
}
|
|
||||||
bool Component::isRevertible()
|
|
||||||
{
|
|
||||||
if (isCustom())
|
|
||||||
{
|
|
||||||
if(ENV.metadataIndex()->hasUid(m_uid))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool Component::isMoveable()
|
|
||||||
{
|
|
||||||
// HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool Component::isVersionChangeable()
|
|
||||||
{
|
|
||||||
auto list = getVersionList();
|
|
||||||
if(list)
|
|
||||||
{
|
|
||||||
if(!list->isLoaded())
|
|
||||||
{
|
|
||||||
list->load(Net::Mode::Online);
|
|
||||||
}
|
|
||||||
return list->count() != 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Component::setImportant(bool state)
|
|
||||||
{
|
|
||||||
if(m_important != state)
|
|
||||||
{
|
|
||||||
m_important = state;
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProblemSeverity Component::getProblemSeverity() const
|
|
||||||
{
|
|
||||||
auto file = getVersionFile();
|
|
||||||
if(file)
|
|
||||||
{
|
|
||||||
return file->getProblemSeverity();
|
|
||||||
}
|
|
||||||
return ProblemSeverity::Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<PatchProblem> Component::getProblems() const
|
|
||||||
{
|
|
||||||
auto file = getVersionFile();
|
|
||||||
if(file)
|
|
||||||
{
|
|
||||||
return file->getProblems();
|
|
||||||
}
|
|
||||||
return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Component::setVersion(const QString& version)
|
|
||||||
{
|
|
||||||
if(version == m_version)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_version = version;
|
|
||||||
if(m_loaded)
|
|
||||||
{
|
|
||||||
// we are loaded and potentially have state to invalidate
|
|
||||||
if(m_file)
|
|
||||||
{
|
|
||||||
// we have a file... explicit version has been changed and there is nothing else to do.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we don't have a file, therefore we are loaded with metadata
|
|
||||||
m_cachedVersion = version;
|
|
||||||
// see if the meta version is loaded
|
|
||||||
auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
|
|
||||||
if(metaVersion->isLoaded())
|
|
||||||
{
|
|
||||||
// if yes, we can continue with that.
|
|
||||||
m_metaVersion = metaVersion;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if not, we need loading
|
|
||||||
m_metaVersion.reset();
|
|
||||||
m_loaded = false;
|
|
||||||
}
|
|
||||||
updateCachedData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// not loaded... assume it will be sorted out later by the update task
|
|
||||||
}
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Component::customize()
|
|
||||||
{
|
|
||||||
if(isCustom())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto filename = getFilename();
|
|
||||||
if(!FS::ensureFilePathExists(filename))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// FIXME: get rid of this try-catch.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
QSaveFile jsonFile(filename);
|
|
||||||
if(!jsonFile.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto vfile = getVersionFile();
|
|
||||||
if(!vfile)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto document = OneSixVersionFormat::versionFileToJson(vfile);
|
|
||||||
jsonFile.write(document.toJson());
|
|
||||||
if(!jsonFile.commit())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_file = vfile;
|
|
||||||
m_metaVersion.reset();
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
catch (const Exception &error)
|
|
||||||
{
|
|
||||||
qWarning() << "Version could not be loaded:" << error.cause();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Component::revert()
|
|
||||||
{
|
|
||||||
if(!isCustom())
|
|
||||||
{
|
|
||||||
// already not custom
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
auto filename = getFilename();
|
|
||||||
bool result = true;
|
|
||||||
// just kill the file and reload
|
|
||||||
if(QFile::exists(filename))
|
|
||||||
{
|
|
||||||
result = QFile::remove(filename);
|
|
||||||
}
|
|
||||||
if(result)
|
|
||||||
{
|
|
||||||
// file gone...
|
|
||||||
m_file.reset();
|
|
||||||
|
|
||||||
// check local cache for metadata...
|
|
||||||
auto version = ENV.metadataIndex()->get(m_uid, m_version);
|
|
||||||
if(version->isLoaded())
|
|
||||||
{
|
|
||||||
m_metaVersion = version;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_metaVersion.reset();
|
|
||||||
m_loaded = false;
|
|
||||||
}
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* deep inspecting compare for requirement sets
|
|
||||||
* By default, only uids are compared for set operations.
|
|
||||||
* This compares all fields of the Require structs in the sets.
|
|
||||||
*/
|
|
||||||
static bool deepCompare(const std::set<Meta::Require> & a, const std::set<Meta::Require> & b)
|
|
||||||
{
|
|
||||||
// NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
|
|
||||||
if(a.size() != b.size())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for(const auto & reqA :a)
|
|
||||||
{
|
|
||||||
const auto &iter2 = b.find(reqA);
|
|
||||||
if(iter2 == b.cend())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto & reqB = *iter2;
|
|
||||||
if(!reqA.deepEquals(reqB))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Component::updateCachedData()
|
|
||||||
{
|
|
||||||
auto file = getVersionFile();
|
|
||||||
if(file)
|
|
||||||
{
|
|
||||||
bool changed = false;
|
|
||||||
if(m_cachedName != file->name)
|
|
||||||
{
|
|
||||||
m_cachedName = file->name;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if(m_cachedVersion != file->version)
|
|
||||||
{
|
|
||||||
m_cachedVersion = file->version;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if(m_cachedVolatile != file->m_volatile)
|
|
||||||
{
|
|
||||||
m_cachedVolatile = file->m_volatile;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if(!deepCompare(m_cachedRequires, file->requires))
|
|
||||||
{
|
|
||||||
m_cachedRequires = file->requires;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if(!deepCompare(m_cachedConflicts, file->conflicts))
|
|
||||||
{
|
|
||||||
m_cachedConflicts = file->conflicts;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if(changed)
|
|
||||||
{
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// in case we removed all the metadata
|
|
||||||
m_cachedRequires.clear();
|
|
||||||
m_cachedConflicts.clear();
|
|
||||||
emit dataChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user