Compare commits

..

1 Commits

Author SHA1 Message Date
Petr Mrázek
19ab1251c2 SCRATCH pass initial parameters to the JavaSettingsWidget from outside 2019-07-13 23:40:28 +02:00
1349 changed files with 24681 additions and 36515 deletions

5
.arcconfig Normal file
View File

@@ -0,0 +1,5 @@
{
"project_id": "MultiMC5",
"conduit_uri": "http://ph.multimc.org"
}

25
.clang-format Normal file
View File

@@ -0,0 +1,25 @@
UseTab: false
IndentWidth: 4
TabWidth: 4
ConstructorInitializerIndentWidth: 4
AccessModifierOffset: -4
IndentCaseLabels: false
IndentFunctionDeclarationAfterType: false
NamespaceIndentation: None
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
ColumnLimit: 160
MaxEmptyLinesToKeep: 1
Standard: Cpp11
Cpp11BracedListStyle: true
SpacesInParentheses: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterControlStatementKeyword: true
AlignTrailingComments: true
SpacesBeforeTrailingComments: 1

51
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,51 @@
<!--
Before submitting this issue, please make sure you have:
1. Filled out this form completely, the only optional field is "additional info".
- Use as many details as possible and state the problem clearly.
2. Proof-read your ENTIRE issue report.
- Grammar and spelling mistakes make issue reports harder to understand.
3. Made sure your problem is not caused by an issue in your own modpack.
- We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored.
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?!".
Use of UPPERCASE is discouraged, as it reads like someone is screaming.
5. Place all information below the ---- of lines.
- It makes the issue look pretty
If you believe your issue to be a bug, please make sure you check the wiki page: https://github.com/MultiMC/MultiMC5/wiki/Report-a-Bug
-->
System Information
-----------------------------
MultiMC version:
Operating System:
Summary of the issue or suggestion:
----------------------------------------------
What should happen:
------------------------------
Steps to reproduce the issue (Add more if needed):
-------------------------------------------------------------
1.
2.
3.
Suspected cause:
---------------------------
Logs/Screenshots:
----------------------------
[//]: # (Please refer to https://github.com/MultiMC/MultiMC5/wiki/Log-Upload for instructions on how to attach your logs.)
Additional Info:
---------------------------

View File

@@ -1,51 +0,0 @@
name: Bug Report
description: File a bug report
labels: [bug, needs-triage]
body:
- type: markdown
attributes:
value: |
If you need help with running Minecraft, please visit us [on our Discord](https://discord.gg/multimc) before making a bug report.
Before submitting a bug report, please make sure you have read this *entire* form, and that:
* You have read the [FAQ](https://github.com/MultiMC/Launcher/wiki/FAQ) and it has not answered your question
* Your bug is not caused by Minecraft or any mods you have installed.
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/MultiMC/Launcher/issues)
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
- type: dropdown
attributes:
label: Operating System
description: If you know this bug occurs on multiple operating systems, select all you have tested.
multiple: true
options:
- Windows
- macOS
- Linux
- Other
- type: textarea
attributes:
label: Description of bug
description: What did you expect to happen, what happened, and why is it incorrect?
placeholder: The cat button should show a cat, but it showed a dog instead!
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: A bulleted list, or an exported instance if relevant.
placeholder: "* Press the cat button"
validations:
required: true
- type: textarea
attributes:
label: Suspected cause
description: If you know what could be causing this bug, describe it here.
validations:
required: false
- type: checkboxes
attributes:
label: This issue is unique
options:
- label: I have searched the issue tracker and did not find an issue describing my bug.
required: true

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: MultiMC Discord
url: https://discord.gg/multimc
about: Please ask for support here before opening an issue.

View File

@@ -1,38 +0,0 @@
name: Suggestion
description: Make a suggestion
labels: [idea, needs-triage]
body:
- type: markdown
attributes:
value: |
### Use this form to suggest a feature for MultiMC.
- type: input
attributes:
label: Role
description: In what way do you use MultiMC that needs this feature?
placeholder: I play modded Minecraft.
validations:
required: true
- type: input
attributes:
label: Suggestion
description: What do you want MultiMC to do?
placeholder: I want the cat button to meow.
validations:
required: true
- type: input
attributes:
label: Benefit
description: Why do you need MultiMC to do this?
placeholder: so that I can always hear a cat when I need to.
validations:
required: true
- type: checkboxes
attributes:
label: This suggestion is unique
options:
- label: I have searched the issue tracker and did not find an issue describing my suggestion, especially not one that has been rejected.
required: true
- type: textarea
attributes:
label: You may use the editor below to elaborate further.

16
.gitignore vendored
View File

@@ -1,31 +1,27 @@
Thumbs.db
*.kdev4
.kdev4
.user
.directory
resources/CMakeFiles
resources/MultiMCLauncher.jar
*~
*.swp
html/
# Project Files
*.pro.user
MultiMC5.kdev4
MultiMC.pro.user
CMakeLists.txt.user
CMakeLists.txt.user.*
/.project
/.settings
/.idea
cmake-build-*/
Debug
.cache
# Build dirs
build
/build-*
# Install dirs
install
/install-*
# Ctags File
tags
@@ -34,7 +30,3 @@ tags
#OSX Stuff
.DS_Store
branding/
secrets/
run/

2
.gitmodules vendored
View File

@@ -1,8 +1,6 @@
[submodule "depends/libnbtplusplus"]
path = libraries/libnbtplusplus
url = https://github.com/MultiMC/libnbtplusplus.git
pushurl = git@github.com:MultiMC/libnbtplusplus.git
[submodule "libraries/quazip"]
path = libraries/quazip
url = https://github.com/MultiMC/quazip.git
pushurl = git@github.com:MultiMC/quazip.git

38
.travis.yml Normal file
View File

@@ -0,0 +1,38 @@
# General set up
language: cpp
cache: apt
matrix:
include:
- os: linux
dist: precise
sudo: required
compiler: gcc
env: TRAVIS_DIST=precise QT_VERSION=5.4.2
- os: linux
dist: precise
sudo: required
compiler: gcc
env: TRAVIS_DIST=precise QT_VERSION=5.6.2
- os: linux
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:
- source travis/prepare.sh # installs qt and cmake. need to source because some env vars are set from there
# Actual work
before_script:
- mkdir build
- cd build
- cmake -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH ..
script:
- make -j4 && make test ARGS="-V"

106
BUILD.md
View File

@@ -7,29 +7,22 @@ Build Instructions
* [Getting the source](#source)
* [Linux](#linux)
* [Windows](#windows)
* [macOS](#macos)
* [OS X](#os-x)
# Note
MultiMC is a portable application and is not supposed to be installed into any system folders.
That would be anything outside your home folder. Before running `make install`, make sure
That would be anything outside your home folder. Before runing `make install`, make sure
you set the install path to something you have write access to. Never build this under
an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
Also note that this guide is for development purposes only.
**No support is given for building your own fork or special build for any reason whatsoever**.
# Branding, identifying marks and API keys
The logo and related assets are All Rights Reserved and may only be used in official builds of MultiMC hosted on multimc.org, and as such, are not, and will not be included in this repository. The source is only provided for the purpose of collaboration.
API keys are necessary for Microsoft account functionality. More info in [(Not) Secrets](https://github.com/MultiMC/Launcher/tree/develop/notsecrets)
# Getting the source
Clone the source code using git and grab all the submodules:
```
git clone https://github.com/MultiMC/Launcher.git
git clone git@github.com:MultiMC/MultiMC5.git
git submodule init
git submodule update
```
@@ -40,7 +33,7 @@ Getting the project to build and run on Linux is easy if you use any modern and
## Build dependencies
* A C++ compiler capable of building C++11 code.
* 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. (for example, `qttools5-dev`)
* 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.
* cmake 3.1 or newer
* zlib (for example, `zlib1g-dev`)
* Java JDK 8 (for example, `openjdk-8-jdk`)
@@ -57,7 +50,7 @@ mkdir ~/MultiMC && cd ~/MultiMC
mkdir build
mkdir install
# clone the complete source
git clone --recursive https://github.com/MultiMC/Launcher.git src
git clone --recursive git@github.com:MultiMC/MultiMC5.git src
# configure the project
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install ../src
@@ -81,7 +74,7 @@ You can use IDEs like KDevelop or QtCreator to open the CMake project if you wan
### Loading the project in Qt Creator (optional)
1. Open Qt Creator.
2. Choose `File->Open File or Project`.
3. Navigate to the Launcher 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.
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)".
@@ -89,7 +82,7 @@ You can use IDEs like KDevelop or QtCreator to open the CMake project if you wan
- 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.
6. Cross your fingers and press the Run button (bottom left of Qt Creator).
- If the project builds successfully it will run and the Launcher window will pop up.
- If the project builds successfully it will run and the MultiMC5 window will pop up.
**If this doesn't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
@@ -99,19 +92,14 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
## Dependencies
* [Qt 5.6+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
- http://download.qt.io/new_archive/qt/5.6/5.6.0/qt-opensource-windows-x86-mingw492-5.6.0.exe
- Download the MinGW version (MSVC version does not work).
* [OpenSSL](https://github.com/IndySockets/OpenSSL-Binaries/tree/master/Archive/) -- Win32 OpenSSL, version 1.0.2g (from 2016)
- https://github.com/IndySockets/OpenSSL-Binaries/raw/master/Archive/openssl-1.0.2g-i386-win32.zip
- the usual OpenSSL for Windows (http://slproweb.com/products/Win32OpenSSL.html) only provides the newest version of OpenSSL, and we need the 1.0.2g version
- **Download the 32-bit version, not 64-bit.**
* [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.
- 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
* [Java JDK 8](https://adoptium.net/releases.html?variant=openjdk8) - Use the MSI installer.
* [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)
Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
Put it somewhere on the `PATH`, so that it is accessible from the console.
## Getting set up
@@ -127,8 +115,9 @@ Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
- Installation can take a very long time, go grab a cup of tea or something and let it work.
### Installing OpenSSL
1. Download .zip file from the link above.
2. Unzip and add the directory to PATH, so CMake can find it.
1. Run the OpenSSL installer,
2. It's best to choose the option to copy OpenSSL DLLs to the `/bin` directory
- If you do this you'll need to add that directory (the default being `C:\OpenSSL-Win32\bin`) to your PATH system variable (Google how to do this, or use this guide for Java: http://www.java.com/en/download/help/path.xml).
### Installing CMake
1. Run the CMake installer,
@@ -138,7 +127,7 @@ Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
### Loading the project
1. Open Qt Creator,
2. Choose File->Open File or Project,
3. Navigate to the Launcher 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,
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.
@@ -148,51 +137,30 @@ Ensure that OpenSSL, zlib, Java and CMake are on `PATH`.
- 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.
7. Cross your fingers and press the Run button (bottom left of Qt Creator)!
- If the project builds successfully it will run and the Launcher window will pop up,
- If the project builds successfully it will run and the MultiMC5 window will pop up,
- Test OpenSSL by making an instance and trying to log in. If Qt Creator couldn't find OpenSSL during the CMake stage, login will fail and you'll get an error.
The following .dlls are needed for the app to run (copy them to build directory if you want to be able to move the build to another pc):
```
platforms/qwindows.dll
libeay32.dll
libgcc_s_dw2-1.dll
libssp-0.dll
libstdc++-6.dll
libwinpthread-1.dll
Qt5Core.dll
Qt5Gui.dll
Qt5Network.dll
Qt5Svg.dll
Qt5Widgets.dll
Qt5Xml.dll
ssleay32.dll
zlib1.dll
```
**These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
### Compile from command line on Windows
1. If you installed Qt with the web installer, there should be a shortcut called `Qt 5.4 for Desktop (MinGW 4.9 32-bit)` in the Start menu on Windows 7 and 10. Best way to find it is to search for it. Do note you cannot just use cmd.exe, you have to use the shortcut, otherwise the proper MinGW software will not be on the PATH.
2. Once that is open, change into your user directory, and clone MultiMC by doing `git clone --recursive https://github.com/MultiMC/Launcher.git`, and change directory to the folder you cloned to.
2. Once that is open, change into your user directory, and clone MultiMC by doing `git clone --recursive https://github.com/MultiMC/MultiMC5.git`, and change directory to the folder you cloned to.
3. Make a build directory, and change directory to the directory and do `cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:\Path\that\makes\sense\for\you`. By default, it will install to C:\Program Files (x86), which you might not want, if you want a local installation. If you want to install it to that directory, make sure to run the command window as administrator.
3. Do `mingw32-make -jX`, where X is the number of cores your CPU has plus one.
4. Now to wait for it to compile. This could take some time. Hopefully it compiles properly.
5. Run the command `mingw32-make install`, and it should install MultiMC, to whatever the `-DCMAKE_INSTALL_PREFIX` was.
6. In most cases, whenever compiling, the OpenSSL dll's aren't put into the directory to where MultiMC installs, meaning you cannot log in. The best way to fix this is just to do `copy C:\OpenSSL-Win32\*.dll C:\Where\you\installed\MultiMC\to`. This should copy the required OpenSSL dll's to log in.
# macOS
# OS X
### Install prerequisites:
- Install XCode Command Line tools
- Install the official build of CMake (https://cmake.org/download/)
- Install JDK 8 (https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html)
- Get Qt 5.6 and install it (https://download.qt.io/new_archive/qt/5.6/5.6.3/)
* install homebrew
* then:
### XCode Command Line tools
If you don't have XCode CommandLine tools installed, you can install them by using this command in the Terminal App
```bash
xcode-select --install
```
brew install qt5
brew tap homebrew/versions
brew install gcc48
brew install cmake
```
### Build
@@ -200,22 +168,18 @@ xcode-select --install
Pick an installation path - this is where the final `.app` will be constructed when you run `make install`. Supply it as the `CMAKE_INSTALL_PREFIX` argument during CMake configuration.
```
git clone --recursive https://github.com/MultiMC/Launcher.git
cd Launcher
git clone https://github.com/MultiMC/MultiMC5.git
git submodule init
git submodule update
cd MultiMC5
mkdir build
cd build
cmake \
-DCMAKE_C_COMPILER=/usr/bin/clang \
-DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX:PATH="$(dirname $PWD)/dist/" \
-DCMAKE_PREFIX_PATH="/path/to/Qt5.6/" \
-DQt5_DIR="/path/to/Qt5.6/" \
-DLauncher_LAYOUT=mac-bundle \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.7 \
..
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
export CC=/usr/local/bin/gcc-4.8
export CXX=/usr/local/bin/g++-4.8
cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/Users/YOU/some/path/that/makes/sense/
make
make install
```
**Note:** The final app bundle may not run due to code signing issues, which
need to be fixed with `codesign -fs -`.
**These build instructions were taken and adapted from https://gist.github.com/number5/7250865 If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**

View File

@@ -1,41 +1,18 @@
cmake_minimum_required(VERSION 3.1)
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD)
message(AUTHOR_WARNING "You are building MultiMC in-source. This is NOT recommended!")
endif()
if(WIN32)
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
cmake_policy(SET CMP0020 OLD)
endif()
project(Launcher)
project(MultiMC)
enable_testing()
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
if(IS_IN_SOURCE_BUILD)
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR
CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*"
)
message(FATAL_ERROR "Building the Launcher is not supported in Linux-on-Windows distributions.")
endif()
endif()
option(Launcher_RUN_CLANG_TIDY "Run clang-tidy with the compiler." OFF)
if(Launcher_RUN_CLANG_TIDY)
find_program(CLANG_TIDY_COMMAND NAMES clang-tidy)
if(NOT CLANG_TIDY_COMMAND)
message(WARNING "CMake_RUN_CLANG_TIDY is ON but clang-tidy is not found!")
set(DO_CLANG_TIDY "" CACHE STRING "" FORCE)
else()
set(CLANG_TIDY_CHECKS "-modernize-use-trailing-return-type,-readability-magic-numbers,-modernize-avoid-c-arrays")
set(DO_CLANG_TIDY "${CLANG_TIDY_COMMAND};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/src/*'")
endif()
# TODO: make this per-target, differentiate between libraries and main codebase
set(CMAKE_CXX_CLANG_TIDY ${DO_CLANG_TIDY})
endif()
##################################### Set CMake options #####################################
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -55,74 +32,51 @@ set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 11)
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 ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE " -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
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}")
if(UNIX AND APPLE)
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type -O0")
# Fix build with Qt 5.13
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
##################################### Set Application options #####################################
######## Set URLs ########
set(Launcher_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch Launcher's news RSS feed from.")
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(Launcher_VERSION_MAJOR 0)
set(Launcher_VERSION_MINOR 6)
set(Launcher_VERSION_HOTFIX 16)
set(MultiMC_VERSION_MAJOR 0)
set(MultiMC_VERSION_MINOR 6)
set(MultiMC_VERSION_HOTFIX 6)
# Build number
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
# Build platform.
set(Launcher_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.")
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(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
# Notification URL
set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
# The metadata server
set(Launcher_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
# paste.ee API key
set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
# Imgur API Client ID
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
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(Launcher_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
# Bug tracker URL
set(Launcher_BUG_TRACKER_URL "" CACHE STRING "URL for the bug tracker.")
# Discord URL
set(Launcher_DISCORD_URL "" CACHE STRING "URL for the Discord guild.")
# Subreddit URL
set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
# Use the secrets library or a public stub?
option(Launcher_EMBED_SECRETS "Determines whether to embed secrets. Secrets are separate and non-public." OFF)
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(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
get_git_head_revision(MultiMC_GIT_REFSPEC MultiMC_GIT_COMMIT)
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
message(STATUS "Git refspec: ${MultiMC_GIT_REFSPEC}")
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
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: ${Launcher_RELEASE_VERSION_NAME}")
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
add_custom_target(version echo "Version: ${MultiMC_RELEASE_VERSION_NAME}")
################################ 3rd Party Libs ################################
@@ -147,55 +101,47 @@ if (Qt5_POSITION_INDEPENDENT_CODE)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
####################################### Secrets #######################################
if(Launcher_EMBED_SECRETS)
add_subdirectory(secrets)
else()
add_subdirectory(notsecrets)
endif()
####################################### Install layout #######################################
# How to install the build results
set(Launcher_LAYOUT "auto" CACHE STRING "The layout for the launcher installation (auto, win-bundle, lin-nodeps, mac-bundle)")
set_property(CACHE Launcher_LAYOUT PROPERTY STRINGS auto win-bundle lin-nodeps mac-bundle)
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(Launcher_LAYOUT STREQUAL "auto")
if(MultiMC_LAYOUT STREQUAL "auto")
if(UNIX AND APPLE)
set(Launcher_LAYOUT_REAL "mac-bundle")
set(MultiMC_LAYOUT_REAL "mac-bundle")
elseif(UNIX)
set(Launcher_LAYOUT_REAL "lin-nodeps")
set(MultiMC_LAYOUT_REAL "lin-nodeps")
elseif(WIN32)
set(Launcher_LAYOUT_REAL "win-bundle")
set(MultiMC_LAYOUT_REAL "win-bundle")
else()
message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.")
endif()
else()
set(Launcher_LAYOUT_REAL ${Launcher_LAYOUT})
set(MultiMC_LAYOUT_REAL ${MultiMC_LAYOUT})
endif()
if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
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}/${Launcher_Name}.app")
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
# Mac bundle settings
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: Minecraft launcher and management utility.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.${Launcher_Name}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2021 ${Launcher_Copyright}")
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})
@@ -204,9 +150,32 @@ if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
set(INSTALL_BUNDLE "full")
# Add the icon
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
install(FILES application/resources/MultiMC.icns DESTINATION ${RESOURCES_DEST_DIR})
elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
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")
@@ -218,13 +187,28 @@ elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
set(INSTALL_BUNDLE "nodeps")
# Set RPATH
SET(Launcher_BINARY_RPATH "$ORIGIN/")
SET(MultiMC_BINARY_RPATH "$ORIGIN/")
# Install basic runner script
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION ${BUNDLE_DEST_DIR} RENAME ${Launcher_Name})
install(PROGRAMS application/package/linux/MultiMC DESTINATION ${BUNDLE_DEST_DIR})
elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
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 ".")
@@ -233,7 +217,7 @@ elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
set(JARS_DEST_DIR "jars")
# Apps to bundle
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.exe")
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})
@@ -252,7 +236,7 @@ set_directory_properties(PROPERTIES EP_BASE External)
option(NBT_BUILD_SHARED "Build NBT shared library" ON)
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
set(NBT_NAME Launcher_nbt++)
set(NBT_NAME MultiMC_nbt++)
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
add_subdirectory(libraries/libnbtplusplus)
@@ -263,17 +247,16 @@ add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker
add_subdirectory(libraries/xz-embedded) # xz compression
add_subdirectory(libraries/quazip) # zip manipulation library
add_subdirectory(libraries/pack200) # java pack200 compression
add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
add_subdirectory(libraries/classparser) # google analytics library
add_subdirectory(libraries/optional-bare)
add_subdirectory(libraries/tomlc99) # toml parser
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
############################### Built Artifacts ###############################
add_subdirectory(buildconfig)
add_subdirectory(api/logic)
add_subdirectory(api/gui)
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
add_subdirectory(launcher)
add_subdirectory(application)

View File

@@ -1,8 +1,6 @@
# MultiMC
Portions are licensed under Apache 2.0 License:
Copyright 2012-2021 MultiMC Contributors
Copyright 2012-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
@@ -15,67 +13,6 @@ Portions are licensed under Apache 2.0 License:
See the License for the specific language governing permissions and
limitations under the License.
Portions are licensed under MS-PL:
This license governs use of the accompanying software. If you use the
software, you accept this license. If you do not accept the license,
do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and
"distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or
changes to the software.
A "contributor" is any person that distributes its contribution
under this license.
"Licensed patents" are a contributor's patent claims that read
directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license,
including the license conditions and limitations in section 3,
each contributor grants you a non-exclusive, worldwide, royalty-free
copyright license to reproduce its contribution, prepare derivative
works of its contribution, and distribute its contribution or any
derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including
the license conditions and limitations in section 3, each contributor
grants you a non-exclusive, worldwide, royalty-free license under its
licensed patents to make, have made, use, sell, offer for sale, import,
and/or otherwise dispose of its contribution in the software or derivative
works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to
use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents
that you claim are infringed by the software, your patent license
from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all
copyright, patent, trademark, and attribution notices that are present
in the software.
(D) If you distribute any portion of the software in source code form,
you may do so only under this license by including a complete copy of
this license with your distribution. If you distribute any portion of
the software in compiled or object code form, you may only do so under
a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it.
The contributors give no express warranties, guarantees or conditions.
You may have additional consumer rights under your local laws which
this license cannot change. To the extent permitted under your local
laws, the contributors exclude the implied warranties of merchantability,
fitness for a particular purpose and non-infringement.
# MinGW runtime (Windows)
Copyright (c) 2012 MinGW.org project
@@ -191,6 +128,35 @@ Portions are licensed under MS-PL:
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
# Pack200
The GNU General Public License (GPL)
Version 2, June 1991
+ "CLASSPATH" EXCEPTION TO THE GPL
Certain source files distributed by Oracle America and/or its affiliates are
subject to the following clarification and special exception to the GPL, but
only where Oracle has expressly included in the particular source file's header
the words "Oracle designates this particular file as subject to the "Classpath"
exception as provided by Oracle in the LICENSE file that accompanied this code."
Linking this library statically or dynamically with other modules is making
a combined work based on this library. Thus, the terms and conditions of
the GNU General Public License cover the whole combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,
and to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent module,
the terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library. If
you modify this library, you may extend this exception to your version of
the library, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.
# Quazip
Copyright (C) 2005-2011 Sergey A. Tachenov
@@ -286,82 +252,3 @@ Portions are licensed under MS-PL:
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.
# optional-bare
Code from https://github.com/martinmoene/optional-bare/
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
# tomlc99
MIT License
Copyright (c) 2017 CK Tan
https://github.com/cktan/tomlc99
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.
# O2 (Katabasis fork)
Copyright (c) 2012, Akos Polster
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted 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.
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 HOLDER 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.

View File

@@ -2,52 +2,44 @@
<img src="https://avatars2.githubusercontent.com/u/5411890" alt="MultiMC logo"/>
</p>
MultiMC
=======
MultiMC 5
=========
MultiMC is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. It also allows you to easily install and remove mods by simply dragging and dropping. Here are the current [features](https://github.com/MultiMC/MultiMC5/wiki#features) of MultiMC.
MultiMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
## Development
If you want to contribute, talk to us on [Discord](https://discord.gg/multimc) first.
The project uses C++ and Qt5 as the language and base framework. This might seem odd in the Minecraft community, but allows using 25MB of RAM, where other tools use an excessive amount of resources for no reason.
While blindly submitting PRs is definitely possible, they're not necessarily going to get accepted.
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 aren't looking for flashy features, but expanding upon the existing feature set without distruption or endangering future viability of the project is OK.
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.
### Building
If you want to build the launcher yourself, check [BUILD.md](BUILD.md) for build instructions.
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
The ci server is running at [ci.multimc.org](http://ci.multimc.org), where you can watch the builds happen in (or very close to) real time. It can also serve as a nice reference on how to set up the build environment on your end.
According to travis.ci, the builds are currently [![Build Status](https://travis-ci.org/MultiMC/MultiMC5.svg?branch=develop)](https://travis-ci.org/MultiMC/MultiMC5)
### Code formatting
Just follow the existing formatting.
We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted.
In general, in order of importance:
* Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
* Prefer readability over dogma.
* Keep to the existing formatting.
* Indent with 4 space unless it's in a submodule.
* Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
## Translations
Translations can be done [on crowdin](https://translate.multimc.org). Please avoid making direct pull requests to the translations repository.
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).
## 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.
Part of the reason for using the Apache license is we don't want people using the "MultiMC" name when redistributing the project. This means people must take the time to go through the source code and remove all references to "MultiMC", including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).
Apache covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.
## License
Copyright &copy; 2013-2022 MultiMC Contributors
Copyright &copy; 2013-2019 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).
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.
## Forking/Redistributing/Custom builds policy
We keep Launcher 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.
The license gives you access to the source MultiMC is build from, but:
- Not the name, logo and other branding.
- Not the API tokens required to talk to services the launcher depends on.
Because of the nature of the agreements required to interact with the Microsoft identity platform, it's impossible for us to continue allowing everyone to build the code as 'MultiMC'. The source code has been debranded and now builds as `DevLauncher` by default.
You must provide your own branding if you want to distribute your own builds.
You will also have to register your own app on Azure to be able to handle Microsoft account logins.
If you decide to fork the project, a mention of its origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.

34
api/gui/CMakeLists.txt Normal file
View File

@@ -0,0 +1,34 @@
project(MultiMC_gui LANGUAGES CXX)
set(GUI_SOURCES
DesktopServices.h
DesktopServices.cpp
# Icons
icons/MMCIcon.h
icons/MMCIcon.cpp
icons/IconList.h
icons/IconList.cpp
SkinUtils.cpp
SkinUtils.h
)
################################ COMPILE ################################
add_library(MultiMC_gui SHARED ${GUI_SOURCES})
set_target_properties(MultiMC_gui PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
generate_export_header(MultiMC_gui)
# Link
target_link_libraries(MultiMC_gui MultiMC_iconfix MultiMC_logic Qt5::Gui)
# Mark and export headers
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}
)

View File

@@ -7,7 +7,7 @@
/**
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
*/
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#if defined(Q_OS_LINUX)
#include <unistd.h>
#include <errno.h>
@@ -83,7 +83,7 @@ bool openDirectory(const QString &path, bool ensureExists)
{
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#if defined(Q_OS_LINUX)
return IndirectOpen(f);
#else
return f();
@@ -97,7 +97,7 @@ bool openFile(const QString &path)
{
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#if defined(Q_OS_LINUX)
return IndirectOpen(f);
#else
return f();
@@ -107,7 +107,7 @@ bool openFile(const QString &path)
bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid)
{
qDebug() << "Opening file" << path << "using" << application;
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#if defined(Q_OS_LINUX)
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
return IndirectOpen([&]()
{
@@ -121,7 +121,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid)
{
qDebug() << "Running" << application << "with args" << args.join(' ');
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#if defined(Q_OS_LINUX)
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
return IndirectOpen([&]()
{
@@ -139,7 +139,7 @@ bool openUrl(const QUrl &url)
{
return QDesktopServices::openUrl(url);
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#if defined(Q_OS_LINUX)
return IndirectOpen(f);
#else
return f();

37
api/gui/DesktopServices.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include <QUrl>
#include <QString>
#include "multimc_gui_export.h"
/**
* This wraps around QDesktopServices and adds workarounds where needed
* Use this instead of QDesktopServices!
*/
namespace DesktopServices
{
/**
* Open a file in whatever application is applicable
*/
MULTIMC_GUI_EXPORT bool openFile(const QString &path);
/**
* Open a file in the specified application
*/
MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
/**
* Run an application
*/
MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
/**
* Open a directory
*/
MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false);
/**
* Open the URL, most likely in a browser. Maybe.
*/
MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url);
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -15,10 +15,9 @@
#include "SkinUtils.h"
#include "net/HttpMetaCache.h"
#include "Application.h"
#include "Env.h"
#include <QFile>
#include <QPainter>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
@@ -30,18 +29,16 @@ namespace SkinUtils
*/
QPixmap getFaceFromCache(QString username, int height, int width)
{
QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath());
QFile fskin(ENV.metacache()
->resolveEntry("skins", username + ".png")
->getFullPath());
if (fskin.exists())
{
QPixmap skinTexture(fskin.fileName());
if(!skinTexture.isNull())
QPixmap skin(fskin.fileName());
if(!skin.isNull())
{
QPixmap skin = QPixmap(8, 8);
QPainter painter(&skin);
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
return skin.scaled(height, width, Qt::KeepAspectRatio);
return skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio);
}
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -17,7 +17,9 @@
#include <QPixmap>
#include "multimc_gui_export.h"
namespace SkinUtils
{
QPixmap getFaceFromCache(QString id, int height = 64, int width = 64);
QPixmap MULTIMC_GUI_EXPORT getFaceFromCache(QString id, int height = 64, int width = 64);
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -395,7 +395,7 @@ QIcon IconList::getIcon(const QString &key) const
return icons[icon_index].icon();
// Fallback for icons that don't exist.
icon_index = getIconIndex("grass");
icon_index = getIconIndex("infinity");
if (icon_index != -1)
return icons[icon_index].icon();
@@ -404,7 +404,7 @@ QIcon IconList::getIcon(const QString &key) const
int IconList::getIconIndex(const QString &key) const
{
auto iter = name_index.find(key == "default" ? "grass" : key);
auto iter = name_index.find(key == "default" ? "infinity" : key);
if (iter != name_index.end())
return *iter;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -21,15 +21,16 @@
#include <QDir>
#include <QtGui/QIcon>
#include <memory>
#include "MMCIcon.h"
#include "settings/Setting.h"
#include "Env.h" // there is a global icon list inside Env.
#include <icons/IIconList.h>
#include "QObjectPtr.h"
#include "multimc_gui_export.h"
class QFileSystemWatcher;
class IconList : public QAbstractListModel
class MULTIMC_GUI_EXPORT IconList : public QAbstractListModel, public IIconList
{
Q_OBJECT
public:
@@ -43,19 +44,19 @@ public:
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool addThemeIcon(const QString &key);
bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) override;
void saveIcon(const QString &key, const QString &path, const char * format) const override;
bool deleteIcon(const QString &key) override;
bool iconFileExists(const QString &key) const override;
virtual QStringList mimeTypes() const override;
virtual Qt::DropActions supportedDropActions() const override;
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
bool addThemeIcon(const QString &key);
bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type);
void saveIcon(const QString &key, const QString &path, const char * format) const;
bool deleteIcon(const QString &key);
bool iconFileExists(const QString &key) const;
void installIcons(const QStringList &iconFiles);
void installIcon(const QString &file, const QString &name);
void installIcons(const QStringList &iconFiles) override;
void installIcon(const QString &file, const QString &name) override;
const MMCIcon * icon(const QString &key) const;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -17,17 +17,11 @@
#include <QString>
#include <QDateTime>
#include <QIcon>
#include <icons/IIconList.h>
enum IconType : unsigned
{
Builtin,
Transient,
FileBased,
ICONS_TOTAL,
ToBeDeleted
};
#include "multimc_gui_export.h"
struct MMCImage
struct MULTIMC_GUI_EXPORT MMCImage
{
QIcon icon;
QString key;
@@ -38,7 +32,7 @@ struct MMCImage
}
};
struct MMCIcon
struct MULTIMC_GUI_EXPORT MMCIcon
{
QString m_key;
QString m_name;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -17,6 +17,8 @@
#include <memory>
#include "multimc_logic_export.h"
class MinecraftInstance;
class QDir;
class QString;
@@ -25,7 +27,7 @@ class Task;
class BaseVersion;
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
class BaseInstaller
class MULTIMC_LOGIC_EXPORT BaseInstaller
{
public:
BaseInstaller();

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -25,7 +25,6 @@
#include "FileSystem.h"
#include "Commandline.h"
#include "BuildConfig.h"
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
: QObject()
@@ -38,7 +37,6 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("notes", "");
m_settings->registerSetting("lastLaunchTime", 0);
m_settings->registerSetting("totalTimePlayed", 0);
m_settings->registerSetting("lastTimePlayed", 0);
// Custom Commands
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
@@ -136,24 +134,15 @@ void BaseInstance::setRunning(bool running)
m_isRunning = running;
if(!m_settings->get("RecordGameTime").toBool())
{
emit runningStatusChanged(running);
return;
}
if(running)
{
m_timeStarted = QDateTime::currentDateTime();
}
else
{
QDateTime timeEnded = QDateTime::currentDateTime();
qint64 current = settings()->get("totalTimePlayed").toLongLong();
QDateTime timeEnded = QDateTime::currentDateTime();
settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded));
settings()->set("lastTimePlayed", m_timeStarted.secsTo(timeEnded));
emit propertiesChanged(this);
}
@@ -171,20 +160,9 @@ int64_t BaseInstance::totalTimePlayed() const
return current;
}
int64_t BaseInstance::lastTimePlayed() const
{
if(m_isRunning)
{
QDateTime timeNow = QDateTime::currentDateTime();
return m_timeStarted.secsTo(timeNow);
}
return settings()->get("lastTimePlayed").toLongLong();
}
void BaseInstance::resetTimePlayed()
{
settings()->reset("totalTimePlayed");
settings()->reset("lastTimePlayed");
}
QString BaseInstance::instanceType() const
@@ -261,7 +239,7 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const
{
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegExp("[ \n\r\t]+"), " ");
return "MultiMC: " + name().replace(QRegExp("[ \n\r\t]+"), " ");
}
// FIXME: why is this here? move it to MinecraftInstance!!!

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -26,13 +26,13 @@
#include "settings/INIFile.h"
#include "BaseVersionList.h"
#include "minecraft/auth/MinecraftAccount.h"
#include "minecraft/auth/MojangAccount.h"
#include "MessageLevel.h"
#include "pathmatcher/IPathMatcher.h"
#include "net/Mode.h"
#include "minecraft/launch/MinecraftServerTarget.h"
#include "multimc_logic_export.h"
class QDir;
class Task;
@@ -50,7 +50,7 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
* To create a new instance type, create a new class inheriting from this class
* and implement the pure virtual functions.
*/
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
{
Q_OBJECT
protected:
@@ -71,21 +71,20 @@ public:
virtual void saveNow() = 0;
/***
* the instance has been invalidated - it is no longer tracked by the launcher for some reason,
* the instance has been invalidated - it is no longer tracked by MultiMC for some reason,
* but it has not necessarily been deleted.
*
* Happens when the instance folder changes to some other location, or the instance is removed by external means.
*/
void invalidate();
/// The instance's ID. The ID SHALL be determined by LAUNCHER internally. The ID IS guaranteed to
/// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to
/// be unique.
virtual QString id() const;
void setRunning(bool running);
bool isRunning() const;
int64_t totalTimePlayed() const;
int64_t lastTimePlayed() const;
void resetTimePlayed();
/// get the type of this instance
@@ -100,9 +99,6 @@ public:
return instanceRoot();
}
/// Path to the instance's mods directory.
virtual QString modsRoot() const = 0;
QString name() const;
void setName(QString val);
@@ -146,11 +142,10 @@ public:
virtual SettingsObjectPtr settings() const;
/// returns a valid update task
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container)
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
/// returns the current launch task (if any)
shared_qobject_ptr<LaunchTask> getLaunchTask();
@@ -226,9 +221,9 @@ public:
bool reloadSettings();
/**
* 'print' a verbose description of the instance into a QStringList
* 'print' a verbose desription of the instance into a QStringList
*/
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0;
virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
Status currentStatus() const;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -21,6 +21,7 @@
#include "BaseVersion.h"
#include "tasks/Task.h"
#include "multimc_logic_export.h"
#include "QObjectPtr.h"
/*!
@@ -35,7 +36,7 @@
* all have a default implementation, but they can be overridden by plugins to
* change the behavior of the list.
*/
class BaseVersionList : public QAbstractListModel
class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel
{
Q_OBJECT
public:
@@ -63,7 +64,7 @@ public:
* 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.
*/
virtual Task::Ptr getLoadTask() = 0;
virtual shared_qobject_ptr<Task> getLoadTask() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be
//loaded.

501
api/logic/CMakeLists.txt Normal file
View File

@@ -0,0 +1,501 @@
project(MultiMC_logic)
include (UnitTest)
set(CORE_SOURCES
# LOGIC - Base classes and infrastructure
BaseInstaller.h
BaseInstaller.cpp
BaseVersionList.h
BaseVersionList.cpp
InstanceList.h
InstanceList.cpp
InstanceTask.h
InstanceTask.cpp
LoggedProcess.h
LoggedProcess.cpp
MessageLevel.cpp
MessageLevel.h
BaseVersion.h
BaseInstance.h
BaseInstance.cpp
NullInstance.h
MMCZip.h
MMCZip.cpp
MMCStrings.h
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
Usable.h
# Prefix tree where node names are strings between separators
SeparatorPrefixTree.h
# WARNING: globals live here
Env.h
Env.cpp
# String filters
Filter.h
Filter.cpp
# JSON parsing helpers
Json.h
Json.cpp
FileSystem.h
FileSystem.cpp
Exception.h
# RW lock protected map
RWStorage.h
# A variable that has an implicit default value and keeps track of changes
DefaultVariable.h
# a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms
QObjectPtr.h
# Compression support
GZip.h
GZip.cpp
# Command line parameter parsing
Commandline.h
Commandline.cpp
# Version number string support
Version.h
Version.cpp
# A Recursive file system watcher
RecursiveFileSystemWatcher.h
RecursiveFileSystemWatcher.cpp
)
add_unit_test(FileSystem
SOURCES FileSystem_test.cpp
LIBS MultiMC_logic
DATA testdata
)
add_unit_test(GZip
SOURCES GZip_test.cpp
LIBS MultiMC_logic
)
set(PATHMATCHER_SOURCES
# Path matchers
pathmatcher/FSTreeMatcher.h
pathmatcher/IPathMatcher.h
pathmatcher/MultiMatcher.h
pathmatcher/RegexpMatcher.h
)
set(NET_SOURCES
# network stuffs
net/ByteArraySink.h
net/ChecksumValidator.h
net/Download.cpp
net/Download.h
net/FileSink.cpp
net/FileSink.h
net/HttpMetaCache.cpp
net/HttpMetaCache.h
net/MetaCacheSink.cpp
net/MetaCacheSink.h
net/NetAction.h
net/NetJob.cpp
net/NetJob.h
net/PasteUpload.cpp
net/PasteUpload.h
net/Sink.h
net/URLConstants.cpp
net/URLConstants.h
net/Validator.h
)
# Game launch logic
set(LAUNCH_SOURCES
launch/steps/PostLaunchCommand.cpp
launch/steps/PostLaunchCommand.h
launch/steps/PreLaunchCommand.cpp
launch/steps/PreLaunchCommand.h
launch/steps/TextPrint.cpp
launch/steps/TextPrint.h
launch/steps/Update.cpp
launch/steps/Update.h
launch/LaunchStep.cpp
launch/LaunchStep.h
launch/LaunchTask.cpp
launch/LaunchTask.h
launch/LogModel.cpp
launch/LogModel.h
)
# Old update system
set(UPDATE_SOURCES
updater/GoUpdate.h
updater/GoUpdate.cpp
updater/UpdateChecker.h
updater/UpdateChecker.cpp
updater/DownloadTask.h
updater/DownloadTask.cpp
)
add_unit_test(UpdateChecker
SOURCES updater/UpdateChecker_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
add_unit_test(DownloadTask
SOURCES updater/DownloadTask_test.cpp
LIBS MultiMC_logic
DATA updater/testdata
)
# Rarely used notifications
set(NOTIFICATIONS_SOURCES
# Notifications - short warning messages
notifications/NotificationChecker.h
notifications/NotificationChecker.cpp
)
# Backend for the news bar... there's usually no news.
set(NEWS_SOURCES
# News System
news/NewsChecker.h
news/NewsChecker.cpp
news/NewsEntry.h
news/NewsEntry.cpp
)
# Icon interface
set(ICONS_SOURCES
# Icons System and related code
icons/IIconList.h
icons/IIconList.cpp
icons/IconUtils.h
icons/IconUtils.cpp
)
# Minecraft services status checker
set(STATUS_SOURCES
# Status system
status/StatusChecker.h
status/StatusChecker.cpp
)
# Support for Minecraft instances and launch
set(MINECRAFT_SOURCES
# Minecraft support
minecraft/auth/AuthSession.h
minecraft/auth/AuthSession.cpp
minecraft/auth/MojangAccountList.h
minecraft/auth/MojangAccountList.cpp
minecraft/auth/MojangAccount.h
minecraft/auth/MojangAccount.cpp
minecraft/auth/YggdrasilTask.h
minecraft/auth/YggdrasilTask.cpp
minecraft/auth/flows/AuthenticateTask.h
minecraft/auth/flows/AuthenticateTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/RefreshTask.cpp
minecraft/auth/flows/ValidateTask.h
minecraft/auth/flows/ValidateTask.cpp
minecraft/gameoptions/GameOptions.h
minecraft/gameoptions/GameOptions.cpp
minecraft/update/AssetUpdateTask.h
minecraft/update/AssetUpdateTask.cpp
minecraft/update/FMLLibrariesTask.cpp
minecraft/update/FMLLibrariesTask.h
minecraft/update/FoldersTask.cpp
minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp
minecraft/update/LibrariesTask.h
minecraft/launch/ClaimAccount.cpp
minecraft/launch/ClaimAccount.h
minecraft/launch/CreateServerResourcePacksFolder.cpp
minecraft/launch/CreateServerResourcePacksFolder.h
minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h
minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/launch/ReconstructAssets.cpp
minecraft/launch/ReconstructAssets.h
minecraft/legacy/LegacyModList.h
minecraft/legacy/LegacyModList.cpp
minecraft/legacy/LegacyInstance.h
minecraft/legacy/LegacyInstance.cpp
minecraft/legacy/LegacyUpgradeTask.h
minecraft/legacy/LegacyUpgradeTask.cpp
minecraft/GradleSpecifier.h
minecraft/MinecraftInstance.cpp
minecraft/MinecraftInstance.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.h
minecraft/Rule.cpp
minecraft/Rule.h
minecraft/OneSixVersionFormat.cpp
minecraft/OneSixVersionFormat.h
minecraft/OpSys.cpp
minecraft/OpSys.h
minecraft/ParseUtils.cpp
minecraft/ParseUtils.h
minecraft/ProfileUtils.cpp
minecraft/ProfileUtils.h
minecraft/Library.cpp
minecraft/Library.h
minecraft/MojangDownloadInfo.h
minecraft/VersionFile.cpp
minecraft/VersionFile.h
minecraft/VersionFilterData.h
minecraft/VersionFilterData.cpp
minecraft/Mod.h
minecraft/Mod.cpp
minecraft/SimpleModList.h
minecraft/SimpleModList.cpp
minecraft/World.h
minecraft/World.cpp
minecraft/WorldList.h
minecraft/WorldList.cpp
# Assets
minecraft/AssetsUtils.h
minecraft/AssetsUtils.cpp
# Forge and all things forge related
minecraft/forge/ForgeXzDownload.h
minecraft/forge/ForgeXzDownload.cpp
# Skin upload utilities
minecraft/SkinUpload.cpp
minecraft/SkinUpload.h
)
add_unit_test(GradleSpecifier
SOURCES minecraft/GradleSpecifier_test.cpp
LIBS MultiMC_logic
)
add_unit_test(MojangVersionFormat
SOURCES minecraft/MojangVersionFormat_test.cpp
LIBS MultiMC_logic
DATA minecraft/testdata
)
add_unit_test(Library
SOURCES minecraft/Library_test.cpp
LIBS MultiMC_logic
)
# FIXME: shares data with FileSystem test
add_unit_test(SimpleModList
SOURCES minecraft/SimpleModList_test.cpp
DATA testdata
LIBS MultiMC_logic
)
add_unit_test(ParseUtils
SOURCES minecraft/ParseUtils_test.cpp
LIBS MultiMC_logic
)
# the screenshots feature
set(SCREENSHOTS_SOURCES
screenshots/Screenshot.h
screenshots/ImgurUpload.h
screenshots/ImgurUpload.cpp
screenshots/ImgurAlbumCreation.h
screenshots/ImgurAlbumCreation.cpp
)
set(TASKS_SOURCES
# Tasks
tasks/Task.h
tasks/Task.cpp
tasks/SequentialTask.h
tasks/SequentialTask.cpp
)
set(SETTINGS_SOURCES
# Settings
settings/INIFile.cpp
settings/INIFile.h
settings/INISettingsObject.cpp
settings/INISettingsObject.h
settings/OverrideSetting.cpp
settings/OverrideSetting.h
settings/PassthroughSetting.cpp
settings/PassthroughSetting.h
settings/Setting.cpp
settings/Setting.h
settings/SettingsObject.cpp
settings/SettingsObject.h
)
add_unit_test(INIFile
SOURCES settings/INIFile_test.cpp
LIBS MultiMC_logic
)
set(JAVA_SOURCES
# Java related code
java/launch/CheckJava.cpp
java/launch/CheckJava.h
java/JavaChecker.h
java/JavaChecker.cpp
java/JavaCheckerJob.h
java/JavaCheckerJob.cpp
java/JavaInstall.h
java/JavaInstall.cpp
java/JavaInstallList.h
java/JavaInstallList.cpp
java/JavaUtils.h
java/JavaUtils.cpp
java/JavaVersion.h
java/JavaVersion.cpp
)
add_unit_test(JavaVersion
SOURCES java/JavaVersion_test.cpp
LIBS MultiMC_logic
)
set(TRANSLATIONS_SOURCES
translations/TranslationsModel.h
translations/TranslationsModel.cpp
translations/POTranslator.h
translations/POTranslator.cpp
)
set(TOOLS_SOURCES
# Tools
tools/BaseExternalTool.cpp
tools/BaseExternalTool.h
tools/BaseProfiler.cpp
tools/BaseProfiler.h
tools/JProfiler.cpp
tools/JProfiler.h
tools/JVisualVM.cpp
tools/JVisualVM.h
tools/MCEditTool.cpp
tools/MCEditTool.h
)
set(META_SOURCES
# Metadata sources
meta/JsonFormat.cpp
meta/JsonFormat.h
meta/BaseEntity.cpp
meta/BaseEntity.h
meta/VersionList.cpp
meta/VersionList.h
meta/Version.cpp
meta/Version.h
meta/Index.cpp
meta/Index.h
)
set(FTB_SOURCES
modplatform/ftb/FtbPackFetchTask.h
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
)
################################ COMPILE ################################
# we need zlib
find_package(ZLIB REQUIRED)
set(LOGIC_SOURCES
${CORE_SOURCES}
${PATHMATCHER_SOURCES}
${NET_SOURCES}
${LAUNCH_SOURCES}
${UPDATE_SOURCES}
${NOTIFICATIONS_SOURCES}
${NEWS_SOURCES}
${STATUS_SOURCES}
${MINECRAFT_SOURCES}
${SCREENSHOTS_SOURCES}
${TASKS_SOURCES}
${SETTINGS_SOURCES}
${JAVA_SOURCES}
${TRANSLATIONS_SOURCES}
${TOOLS_SOURCES}
${META_SOURCES}
${ICONS_SOURCES}
${FTB_SOURCES}
${FLAME_SOURCES}
)
message(STATUS "FOO! ${LOGIC_SOURCES}")
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1)
generate_export_header(MultiMC_logic)
# Link
target_link_libraries(MultiMC_logic xz-embedded MultiMC_unpack200 systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES})
target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent)
# Mark and export headers
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}
)

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -25,6 +25,8 @@
#include <QHash>
#include <QStringList>
#include "multimc_logic_export.h"
/**
* @file libutil/include/cmdutils.h
* @brief commandline parsing and processing utilities
@@ -38,7 +40,7 @@ namespace Commandline
* @param args the argument string
* @return a QStringList containing all arguments
*/
QStringList splitArgs(QString args);
MULTIMC_LOGIC_EXPORT QStringList splitArgs(QString args);
/**
* @brief The FlagStyle enum
@@ -67,8 +69,8 @@ namespace ArgumentStyle
{
enum Enum
{
Space, /**< --option value */
Equals, /**< --option=value */
Space, /**< --option=value */
Equals, /**< --option value */
SpaceAndEquals, /**< --option[= ]value */
#ifdef Q_OS_WIN32
Default = Equals
@@ -81,7 +83,7 @@ enum Enum
/**
* @brief The ParsingError class
*/
class ParsingError : public std::runtime_error
class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error
{
public:
ParsingError(const QString &what);
@@ -90,7 +92,7 @@ public:
/**
* @brief The Parser class
*/
class Parser
class MULTIMC_LOGIC_EXPORT Parser
{
public:
/**

209
api/logic/Env.cpp Normal file
View File

@@ -0,0 +1,209 @@
#include "Env.h"
#include "net/HttpMetaCache.h"
#include "BaseVersion.h"
#include "BaseVersionList.h"
#include <QDir>
#include <QCoreApplication>
#include <QNetworkProxy>
#include <QNetworkAccessManager>
#include <QDebug>
#include "tasks/Task.h"
#include "meta/Index.h"
#include "FileSystem.h"
#include <QDebug>
struct Env::Private
{
QNetworkAccessManager m_qnam;
shared_qobject_ptr<HttpMetaCache> m_metacache;
std::shared_ptr<IIconList> m_iconlist;
shared_qobject_ptr<Meta::Index> m_metadataIndex;
QString m_jarsPath;
QSet<QString> m_features;
};
static Env * instance;
/*
* The *NEW* global rat nest of an object. Handle with care.
*/
Env::Env()
{
d = new Private();
}
Env::~Env()
{
delete d;
}
Env& Env::Env::getInstance()
{
if(!instance)
{
instance = new Env();
}
return *instance;
}
void Env::dispose()
{
delete instance;
instance = nullptr;
}
shared_qobject_ptr< HttpMetaCache > Env::metacache()
{
return d->m_metacache;
}
QNetworkAccessManager& Env::qnam() const
{
return d->m_qnam;
}
std::shared_ptr<IIconList> Env::icons()
{
return d->m_iconlist;
}
void Env::registerIconList(std::shared_ptr<IIconList> iconlist)
{
d->m_iconlist = iconlist;
}
shared_qobject_ptr<Meta::Index> Env::metadataIndex()
{
if (!d->m_metadataIndex)
{
d->m_metadataIndex.reset(new Meta::Index());
}
return d->m_metadataIndex;
}
void Env::initHttpMetaCache()
{
auto &m_metacache = d->m_metacache;
m_metacache.reset(new HttpMetaCache("metacache"));
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
m_metacache->addBase("liteloader", QDir("mods/liteloader").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("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->Load();
}
void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
{
// Set the application proxy settings.
if (proxyTypeStr == "SOCKS5")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
}
else if (proxyTypeStr == "HTTP")
{
QNetworkProxy::setApplicationProxy(
QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
}
else if (proxyTypeStr == "None")
{
// If we have no proxy set, set no proxy and return.
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
}
else
{
// If we have "Default" selected, set Qt to use the system proxy settings.
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
qDebug() << "Detecting proxy settings...";
QNetworkProxy proxy = QNetworkProxy::applicationProxy();
d->m_qnam.setProxy(proxy);
QString proxyDesc;
if (proxy.type() == QNetworkProxy::NoProxy)
{
qDebug() << "Using no proxy is an option!";
return;
}
switch (proxy.type())
{
case QNetworkProxy::DefaultProxy:
proxyDesc = "Default proxy: ";
break;
case QNetworkProxy::Socks5Proxy:
proxyDesc = "Socks5 proxy: ";
break;
case QNetworkProxy::HttpProxy:
proxyDesc = "HTTP proxy: ";
break;
case QNetworkProxy::HttpCachingProxy:
proxyDesc = "HTTP caching: ";
break;
case QNetworkProxy::FtpCachingProxy:
proxyDesc = "FTP caching: ";
break;
default:
proxyDesc = "DERP proxy: ";
break;
}
proxyDesc += QString("%3@%1:%2 pass %4")
.arg(proxy.hostName())
.arg(proxy.port())
.arg(proxy.user())
.arg(proxy.password());
qDebug() << proxyDesc;
}
QString Env::getJarsPath()
{
if(d->m_jarsPath.isEmpty())
{
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
}
return d->m_jarsPath;
}
void Env::setJarsPath(const QString& path)
{
d->m_jarsPath = path;
}
void Env::enableFeature(const QString& featureName, bool state)
{
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;
}

65
api/logic/Env.h Normal file
View File

@@ -0,0 +1,65 @@
#pragma once
#include <memory>
#include "icons/IIconList.h"
#include <QString>
#include <QMap>
#include "multimc_logic_export.h"
#include "QObjectPtr.h"
class QNetworkAccessManager;
class HttpMetaCache;
class BaseVersionList;
class BaseVersion;
namespace Meta
{
class Index;
}
#if defined(ENV)
#undef ENV
#endif
#define ENV (Env::getInstance())
class MULTIMC_LOGIC_EXPORT Env
{
friend class MultiMC;
private:
struct Private;
Env();
~Env();
static void dispose();
public:
static Env& getInstance();
QNetworkAccessManager &qnam() const;
shared_qobject_ptr<HttpMetaCache> metacache();
std::shared_ptr<IIconList> icons();
/// init the cache. FIXME: possible future hook point
void initHttpMetaCache();
/// Updates the application proxy settings from the settings object.
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
void registerIconList(std::shared_ptr<IIconList> iconlist);
shared_qobject_ptr<Meta::Index> metadataIndex();
QString getJarsPath();
void setJarsPath(const QString & path);
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:
Private * d;
};

View File

@@ -6,7 +6,9 @@
#include <QDebug>
#include <exception>
class Exception : public std::exception
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT Exception : public std::exception
{
public:
Exception(const QString &message) : std::exception(), m_message(message)

View File

@@ -174,11 +174,6 @@ bool copy::operator()(const QString &offset)
bool deletePath(QString path)
{
bool OK = true;
QFileInfo finfo(path);
if(finfo.isFile()) {
return QFile::remove(path);
}
QDir dir(path);
if (!dir.exists())
@@ -299,7 +294,7 @@ QString NormalizePath(QString path)
}
}
QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
QString badFilenameChars = "\"\\/?<>:*|!+\r\n";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{
@@ -403,7 +398,7 @@ QString getDesktopDir()
bool createShortCut(QString location, QString dest, QStringList args, QString name,
QString icon)
{
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#if defined Q_OS_LINUX
location = PathCombine(location, name + ".desktop");
QFile f(location);

View File

@@ -5,13 +5,14 @@
#include "Exception.h"
#include "pathmatcher/IPathMatcher.h"
#include "multimc_logic_export.h"
#include <QDir>
#include <QFlags>
namespace FS
{
class FileSystemException : public ::Exception
class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception
{
public:
FileSystemException(const QString &message) : Exception(message) {}
@@ -20,31 +21,31 @@ public:
/**
* write data to a file safely
*/
void write(const QString &filename, const QByteArray &data);
MULTIMC_LOGIC_EXPORT void write(const QString &filename, const QByteArray &data);
/**
* read data from a file safely\
*/
QByteArray read(const QString &filename);
MULTIMC_LOGIC_EXPORT QByteArray read(const QString &filename);
/**
* Update the last changed timestamp of an existing file
*/
bool updateTimestamp(const QString & filename);
MULTIMC_LOGIC_EXPORT bool updateTimestamp(const QString & filename);
/**
* Creates all the folders in a path for the specified path
* last segment of the path is treated as a file name and is ignored!
*/
bool ensureFilePathExists(QString filenamepath);
MULTIMC_LOGIC_EXPORT bool ensureFilePathExists(QString filenamepath);
/**
* Creates all the folders in a path for the specified path
* last segment of the path is treated as a folder name and is created!
*/
bool ensureFolderPathExists(QString filenamepath);
MULTIMC_LOGIC_EXPORT bool ensureFolderPathExists(QString filenamepath);
class copy
class MULTIMC_LOGIC_EXPORT copy
{
public:
copy(const QString & src, const QString & dst)
@@ -80,13 +81,13 @@ private:
/**
* Delete a folder recursively
*/
bool deletePath(QString path);
MULTIMC_LOGIC_EXPORT bool deletePath(QString path);
QString PathCombine(const QString &path1, const QString &path2);
QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2);
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
MULTIMC_LOGIC_EXPORT QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
QString AbsolutePath(QString path);
MULTIMC_LOGIC_EXPORT QString AbsolutePath(QString path);
/**
* Resolve an executable
@@ -98,7 +99,7 @@ QString AbsolutePath(QString path);
*
* @return absolute path to executable or null string
*/
QString ResolveExecutable(QString path);
MULTIMC_LOGIC_EXPORT QString ResolveExecutable(QString path);
/**
* Normalize path
@@ -108,20 +109,20 @@ QString ResolveExecutable(QString path);
*
* Returns false if the path logic somehow filed (and normalizedPath in invalid)
*/
QString NormalizePath(QString path);
MULTIMC_LOGIC_EXPORT QString NormalizePath(QString path);
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
MULTIMC_LOGIC_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
QString DirNameFromString(QString string, QString inDir = ".");
MULTIMC_LOGIC_EXPORT QString DirNameFromString(QString string, QString inDir = ".");
/// Checks if the a given Path contains "!"
bool checkProblemticPathJava(QDir folder);
MULTIMC_LOGIC_EXPORT bool checkProblemticPathJava(QDir folder);
// Get the Directory representing the User's Desktop
QString getDesktopDir();
MULTIMC_LOGIC_EXPORT QString getDesktopDir();
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
// call it *name* and assign it the icon *icon*
// return true if operation succeeded
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
MULTIMC_LOGIC_EXPORT bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
}

View File

@@ -135,7 +135,7 @@ slots:
<< "asdf"
<< QString()
#if defined(Q_OS_LINUX)
<< GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
<< MULTIMC_GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
#elif defined(Q_OS_WIN)
<< QByteArray()
#endif

View File

@@ -3,14 +3,16 @@
#include <QString>
#include <QRegularExpression>
class Filter
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT Filter
{
public:
virtual ~Filter();
virtual bool accepts(const QString & value) = 0;
};
class ContainsFilter: public Filter
class MULTIMC_LOGIC_EXPORT ContainsFilter: public Filter
{
public:
ContainsFilter(const QString &pattern);
@@ -20,7 +22,7 @@ private:
QString pattern;
};
class ExactFilter: public Filter
class MULTIMC_LOGIC_EXPORT ExactFilter: public Filter
{
public:
ExactFilter(const QString &pattern);
@@ -30,7 +32,7 @@ private:
QString pattern;
};
class RegexpFilter: public Filter
class MULTIMC_LOGIC_EXPORT RegexpFilter: public Filter
{
public:
RegexpFilter(const QString &regexp, bool invert);

View File

@@ -1,7 +1,9 @@
#pragma once
#include <QByteArray>
class GZip
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT GZip
{
public:
static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);

View File

@@ -5,10 +5,9 @@
#include "pathmatcher/RegexpMatcher.h"
#include <QtConcurrentRun>
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
{
m_origInstance = origInstance;
m_keepPlaytime = keepPlaytime;
if(!copySaves)
{
@@ -47,9 +46,6 @@ void InstanceCopyTask::copyFinished()
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(m_instName);
inst->setIconKey(m_instIcon);
if(!m_keepPlaytime) {
inst->resetTimePlayed();
}
emitSucceeded();
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "tasks/Task.h"
#include "multimc_logic_export.h"
#include "net/NetJob.h"
#include <QUrl>
#include <QFuture>
@@ -10,11 +11,11 @@
#include "BaseInstance.h"
#include "InstanceTask.h"
class InstanceCopyTask : public InstanceTask
class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
{
Q_OBJECT
public:
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
protected:
//! Entry point for tasks.
@@ -27,5 +28,4 @@ private: /* data */
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
std::unique_ptr<IPathMatcher> m_matcher;
bool m_keepPlaytime;
};

View File

@@ -4,7 +4,7 @@
//FIXME: remove this
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "minecraft/ComponentList.h"
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
{
@@ -20,7 +20,7 @@ void InstanceCreationTask::executeTask()
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
auto components = inst.getPackProfile();
auto components = inst.getComponentList();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
inst.setName(m_instName);

View File

@@ -1,13 +1,14 @@
#pragma once
#include "tasks/Task.h"
#include "multimc_logic_export.h"
#include "net/NetJob.h"
#include <QUrl>
#include "settings/SettingsObject.h"
#include "BaseVersion.h"
#include "InstanceTask.h"
class InstanceCreationTask : public InstanceTask
class MULTIMC_LOGIC_EXPORT InstanceCreationTask : public InstanceTask
{
Q_OBJECT
public:

View File

@@ -0,0 +1,411 @@
#include "InstanceImportTask.h"
#include "BaseInstance.h"
#include "FileSystem.h"
#include "Env.h"
#include "MMCZip.h"
#include "NullInstance.h"
#include "settings/INISettingsObject.h"
#include "icons/IIconList.h"
#include "icons/IconUtils.h"
#include <QtConcurrentRun>
// FIXME: this does not belong here, it's Minecraft/Flame specific
#include "minecraft/MinecraftInstance.h"
#include "minecraft/ComponentList.h"
#include "modplatform/flame/FileResolvingTask.h"
#include "modplatform/flame/PackManifest.h"
#include "Json.h"
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
{
m_sourceUrl = sourceUrl;
}
void InstanceImportTask::executeTask()
{
InstancePtr newInstance;
if (m_sourceUrl.isLocalFile())
{
m_archivePath = m_sourceUrl.toLocalFile();
processZipPack();
}
else
{
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true;
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
auto entry = ENV.metacache()->resolveEntry("general", path);
entry->setStale(true);
m_filesNetJob.reset(new NetJob(tr("Modpack download")));
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath();
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
m_filesNetJob->start();
}
}
void InstanceImportTask::downloadSucceeded()
{
processZipPack();
m_filesNetJob.reset();
}
void InstanceImportTask::downloadFailed(QString reason)
{
emitFailed(reason);
m_filesNetJob.reset();
}
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{
setProgress(current / 2, total);
}
void InstanceImportTask::processZipPack()
{
setStatus(tr("Extracting modpack"));
QDir extractDir(m_stagingPath);
qDebug() << "Attempting to create instance from" << m_archivePath;
// open the zip and find relevant files in it
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>::canceled, this, &InstanceImportTask::extractAborted);
m_extractFutureWatcher.setFuture(m_extractFuture);
}
void InstanceImportTask::extractFinished()
{
m_packZip.reset();
if (m_extractFuture.result().isEmpty())
{
emitFailed(tr("Failed to extract modpack"));
return;
}
QDir extractDir(m_stagingPath);
qDebug() << "Fixing permissions for extracted pack files...";
QDirIterator it(extractDir, QDirIterator::Subdirectories);
while (it.hasNext())
{
auto filepath = it.next();
QFileInfo file(filepath);
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;
}
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!!!
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
instanceSettings->registerSetting("InstanceType", "Legacy");
NullInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
// reset time played on import... because packs.
instance.resetTimePlayed();
// set a new nice name
instance.setName(m_instName);
// if the icon was specified by user, use that. otherwise pull icon from the pack
if (m_instIcon != "default")
{
instance.setIconKey(m_instIcon);
}
else
{
m_instIcon = instance.iconKey();
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
if (!importIconPath.isNull() && QFile::exists(importIconPath))
{
// import icon
auto iconList = ENV.icons();
if (iconList->iconFileExists(m_instIcon))
{
iconList->deleteIcon(m_instIcon);
}
iconList->installIcons({importIconPath});
}
}
emitSucceeded();
}

View File

@@ -1,21 +1,7 @@
/* Copyright 2013-2021 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 "InstanceTask.h"
#include "multimc_logic_export.h"
#include "net/NetJob.h"
#include <QUrl>
#include <QFuture>
@@ -23,15 +9,13 @@
#include "settings/SettingsObject.h"
#include "QObjectPtr.h"
#include <nonstd/optional>
class QuaZip;
namespace Flame
{
class FileResolvingTask;
}
class InstanceImportTask : public InstanceTask
class MULTIMC_LOGIC_EXPORT InstanceImportTask : public InstanceTask
{
Q_OBJECT
public:
@@ -44,9 +28,7 @@ protected:
private:
void processZipPack();
void processMultiMC();
void processTechnic();
void processFlame();
void processModrinth();
private slots:
void downloadSucceeded();
@@ -56,18 +38,17 @@ private slots:
void extractAborted();
private: /* data */
NetJob::Ptr m_filesNetJob;
NetJobPtr m_filesNetJob;
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
QUrl m_sourceUrl;
QString m_archivePath;
bool m_downloadRequired = false;
std::unique_ptr<QuaZip> m_packZip;
QFuture<nonstd::optional<QStringList>> m_extractFuture;
QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
QFuture<QStringList> m_extractFuture;
QFutureWatcher<QStringList> m_extractFutureWatcher;
enum class ModpackType{
Unknown,
MultiMC,
Technic,
Modrinth,
Flame
} m_modpackType = ModpackType::Unknown;
};

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -26,7 +26,6 @@
#include <QUuid>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMimeData>
#include "InstanceList.h"
#include "BaseInstance.h"
@@ -64,50 +63,6 @@ InstanceList::~InstanceList()
{
}
Qt::DropActions InstanceList::supportedDragActions() const
{
return Qt::MoveAction;
}
Qt::DropActions InstanceList::supportedDropActions() const
{
return Qt::MoveAction;
}
bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
{
if(data && data->hasFormat("application/x-instanceid")) {
return true;
}
return false;
}
bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
{
if(data && data->hasFormat("application/x-instanceid")) {
return true;
}
return false;
}
QStringList InstanceList::mimeTypes() const
{
auto types = QAbstractListModel::mimeTypes();
types.push_back("application/x-instanceid");
return types;
}
QMimeData * InstanceList::mimeData(const QModelIndexList& indexes) const
{
auto mimeData = QAbstractListModel::mimeData(indexes);
if(indexes.size() == 1) {
auto instanceId = data(indexes[0], InstanceIDRole).toString();
mimeData->setData("application/x-instanceid", instanceId.toUtf8());
}
return mimeData;
}
int InstanceList::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
@@ -145,10 +100,6 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
{
return pdata->name();
}
case Qt::AccessibleTextRole:
{
return tr("%1 Instance").arg(pdata->name());
}
case Qt::ToolTipRole:
{
return pdata->instanceRoot();
@@ -157,7 +108,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
{
return pdata->iconKey();
}
// HACK: see InstanceView.h in gui!
// HACK: see GroupView.h in gui!
case GroupRole:
{
return getInstanceGroup(pdata->id());
@@ -205,8 +156,8 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
{
return GroupId();
}
auto iter = m_instanceGroupIndex.find(inst->id());
if(iter != m_instanceGroupIndex.end())
auto iter = m_groupMap.find(inst->id());
if(iter != m_groupMap.end())
{
return *iter;
}
@@ -223,8 +174,8 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
}
bool changed = false;
auto iter = m_instanceGroupIndex.find(inst->id());
if(iter != m_instanceGroupIndex.end())
auto iter = m_groupMap.find(inst->id());
if(iter != m_groupMap.end())
{
if(*iter != name)
{
@@ -235,12 +186,12 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
else
{
changed = true;
m_instanceGroupIndex[id] = name;
m_groupMap[id] = name;
}
if(changed)
{
m_groupNameCache.insert(name);
m_groups.insert(name);
auto idx = getInstIndex(inst.get());
emit dataChanged(index(idx), index(idx), {GroupRole});
saveGroupList();
@@ -249,7 +200,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
QStringList InstanceList::getGroups()
{
return m_groupNameCache.toList();
return m_groups.toList();
}
void InstanceList::deleteGroup(const QString& name)
@@ -262,7 +213,7 @@ void InstanceList::deleteGroup(const QString& name)
auto instGroupName = getInstanceGroup(instID);
if(instGroupName == name)
{
m_instanceGroupIndex.remove(instID);
m_groupMap.remove(instID);
qDebug() << "Remove" << instID << "from group" << name;
removed = true;
auto idx = getInstIndex(instance.get());
@@ -278,11 +229,6 @@ void InstanceList::deleteGroup(const QString& name)
}
}
bool InstanceList::isGroupCollapsed(const QString& group)
{
return m_collapsedGroups.contains(group);
}
void InstanceList::deleteInstance(const InstanceId& id)
{
auto inst = getInstanceById(id);
@@ -292,7 +238,7 @@ void InstanceList::deleteInstance(const InstanceId& id)
return;
}
if(m_instanceGroupIndex.remove(id))
if(m_groupMap.remove(id))
{
saveGroupList();
}
@@ -304,7 +250,7 @@ void InstanceList::deleteInstance(const InstanceId& id)
return;
}
qDebug() << "Instance" << id << "has been deleted by the launcher.";
qDebug() << "Instance" << id << "has been deleted by MultiMC.";
}
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
@@ -432,19 +378,9 @@ InstanceList::InstListError InstanceList::loadList()
add(newList);
}
m_dirty = false;
updateTotalPlayTime();
return NoError;
}
void InstanceList::updateTotalPlayTime()
{
totalPlayTime = 0;
for(auto const& itr : m_instances)
{
totalPlayTime += itr.get()->totalTimePlayed();
}
}
void InstanceList::saveNow()
{
for(auto & item: m_instances)
@@ -530,7 +466,6 @@ void InstanceList::propertiesChanged(BaseInstance *inst)
if (i != -1)
{
emit dataChanged(index(i), index(i));
updateTotalPlayTime();
}
}
@@ -576,7 +511,7 @@ void InstanceList::saveGroupList()
WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++)
for (auto iter = m_groupMap.begin(); iter != m_groupMap.end(); iter++)
{
QString id = iter.key();
QString group = iter.value();
@@ -609,7 +544,7 @@ void InstanceList::saveGroupList()
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name)));
groupObj.insert("hidden", QJsonValue(QString("false")));
for (auto item : list)
{
instanceArr.append(QJsonValue(item));
@@ -633,6 +568,7 @@ void InstanceList::saveGroupList()
void InstanceList::loadGroupList()
{
qDebug() << "Will load group list now.";
QSet<QString> groupSet;
QString groupFileName = m_instDir + "/instgroups.json";
@@ -683,8 +619,7 @@ void InstanceList::loadGroupList()
return;
}
QSet<QString> groupSet;
m_instanceGroupIndex.clear();
m_groupMap.clear();
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
@@ -695,35 +630,37 @@ void InstanceList::loadGroupList()
// 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();
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();
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);
auto hidden = groupObj.value("hidden").toBool(false);
if(hidden) {
m_collapsedGroups.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_instanceGroupIndex[(*iter2).toString()] = groupName;
m_groupMap[(*iter2).toString()] = groupName;
}
}
m_groupsLoaded = true;
m_groupNameCache.unite(groupSet);
m_groups.unite(groupSet);
qDebug() << "Group list loaded.";
}
@@ -748,17 +685,6 @@ void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value)
}
}
void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
{
qDebug() << "Group" << group << (collapsed ? "collapsed" : "expanded");
if(collapsed) {
m_collapsedGroups.insert(group);
} else {
m_collapsedGroups.remove(group);
}
saveGroupList();
}
class InstanceStaging : public Task
{
Q_OBJECT
@@ -844,7 +770,7 @@ private slots:
private:
/*
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
* Basically, it starts messing things up while the launcher is extracting/creating instances
* 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;
@@ -867,7 +793,7 @@ Task * InstanceList::wrapInstanceTask(InstanceTask * task)
QString InstanceList::getStagedInstancePath()
{
QString key = QUuid::createUuid().toString();
QString relPath = FS::PathCombine("_LAUNCHER_TEMP/" , key);
QString relPath = FS::PathCombine("_MMC_TEMP/" , key);
QDir rootPath(m_instDir);
auto path = FS::PathCombine(m_instDir, relPath);
if(!rootPath.mkpath(relPath))
@@ -889,9 +815,9 @@ bool InstanceList::commitStagedInstance(const QString& path, const QString& inst
qWarning() << "Failed to move" << path << "to" << destination;
return false;
}
m_instanceGroupIndex[instID] = groupName;
m_groupMap[instID] = groupName;
instanceSet.insert(instID);
m_groupNameCache.insert(groupName);
m_groups.insert(groupName);
emit instancesChanged();
emit instanceSelectRequest(instID);
}
@@ -904,9 +830,4 @@ bool InstanceList::destroyStagingPath(const QString& keyPath)
return FS::deletePath(keyPath);
}
int InstanceList::getTotalPlayTime() {
updateTotalPlayTime();
return totalPlayTime;
}
#include "InstanceList.moc"
#include "InstanceList.moc"

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -22,6 +22,8 @@
#include "BaseInstance.h"
#include "multimc_logic_export.h"
#include "QObjectPtr.h"
class QFileSystemWatcher;
@@ -47,7 +49,7 @@ enum class GroupsState
};
class InstanceList : public QAbstractListModel
class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel
{
Q_OBJECT
@@ -93,11 +95,10 @@ public:
InstListError loadList();
void saveNow();
InstancePtr getInstanceById(QString id) const;
QModelIndex getInstanceIndexById(const QString &id) const;
QStringList getGroups();
bool isGroupCollapsed(const QString &groupName);
GroupId getInstanceGroup(const InstanceId & id) const;
void setInstanceGroup(const InstanceId & id, const GroupId& name);
@@ -125,19 +126,6 @@ public:
*/
bool destroyStagingPath(const QString & keyPath);
int getTotalPlayTime();
Qt::DropActions supportedDragActions() const override;
Qt::DropActions supportedDropActions() const override;
bool canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const override;
bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
signals:
void dataIsInvalid();
void instancesChanged();
@@ -146,7 +134,6 @@ signals:
public slots:
void on_InstFolderChanged(const Setting &setting, QVariant value);
void on_GroupStateChanged(const QString &group, bool collapsed);
private slots:
void propertiesChanged(BaseInstance *inst);
@@ -155,7 +142,6 @@ private slots:
private:
int getInstIndex(BaseInstance *inst) const;
void updateTotalPlayTime();
void suspendWatch();
void resumeWatch();
void add(const QList<InstancePtr> &list);
@@ -166,17 +152,14 @@ private:
private:
int m_watchLevel = 0;
int totalPlayTime = 0;
bool m_dirty = false;
QList<InstancePtr> m_instances;
QSet<QString> m_groupNameCache;
QSet<QString> m_groups;
SettingsObjectPtr m_globalSettings;
QString m_instDir;
QFileSystemWatcher * m_watcher;
// FIXME: this is so inefficient that looking at it is almost painful.
QSet<QString> m_collapsedGroups;
QMap<InstanceId, GroupId> m_instanceGroupIndex;
QMap<InstanceId, GroupId> m_groupMap;
QSet<InstanceId> instanceSet;
bool m_groupsLoaded = false;
bool m_instancesProbed = false;

View File

@@ -1,9 +1,10 @@
#pragma once
#include "tasks/Task.h"
#include "multimc_logic_export.h"
#include "settings/SettingsObject.h"
class InstanceTask : public Task
class MULTIMC_LOGIC_EXPORT InstanceTask : public Task
{
Q_OBJECT
public:

View File

@@ -16,32 +16,32 @@
namespace Json
{
class JsonException : public ::Exception
class MULTIMC_LOGIC_EXPORT JsonException : public ::Exception
{
public:
JsonException(const QString &message) : Exception(message) {}
};
/// @throw FileSystemException
void write(const QJsonDocument &doc, const QString &filename);
MULTIMC_LOGIC_EXPORT void write(const QJsonDocument &doc, const QString &filename);
/// @throw FileSystemException
void write(const QJsonObject &object, const QString &filename);
MULTIMC_LOGIC_EXPORT void write(const QJsonObject &object, const QString &filename);
/// @throw FileSystemException
void write(const QJsonArray &array, const QString &filename);
MULTIMC_LOGIC_EXPORT void write(const QJsonArray &array, const QString &filename);
QByteArray toBinary(const QJsonObject &obj);
QByteArray toBinary(const QJsonArray &array);
QByteArray toText(const QJsonObject &obj);
QByteArray toText(const QJsonArray &array);
MULTIMC_LOGIC_EXPORT QByteArray toBinary(const QJsonObject &obj);
MULTIMC_LOGIC_EXPORT QByteArray toBinary(const QJsonArray &array);
MULTIMC_LOGIC_EXPORT QByteArray toText(const QJsonObject &obj);
MULTIMC_LOGIC_EXPORT QByteArray toText(const QJsonArray &array);
/// @throw JsonException
QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document");
MULTIMC_LOGIC_EXPORT QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document");
/// @throw JsonException
QJsonDocument requireDocument(const QString &filename, const QString &what = "Document");
MULTIMC_LOGIC_EXPORT QJsonDocument requireDocument(const QString &filename, const QString &what = "Document");
/// @throw JsonException
QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document");
MULTIMC_LOGIC_EXPORT QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document");
/// @throw JsonException
QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document");
MULTIMC_LOGIC_EXPORT QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document");
/////////////////// WRITING ////////////////////
@@ -84,31 +84,31 @@ template <typename T>
T requireIsType(const QJsonValue &value, const QString &what = "Value");
/// @throw JsonException
template<> double requireIsType<double>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT double requireIsType<double>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT bool requireIsType<bool>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> int requireIsType<int>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT int requireIsType<int>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QString requireIsType<QString>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QDir requireIsType<QDir>(const QJsonValue &value, const QString &what);
/// @throw JsonException
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what);
template<> MULTIMC_LOGIC_EXPORT QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what);
// the following functions are higher level functions, that make use of the above functions for
// type conversion
@@ -212,11 +212,11 @@ QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key,
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \
inline TYPE requireValue##NAME(const QJsonValue &value, const QString &what = "Value") \
inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
{ \
return requireIsType<TYPE>(value, what); \
} \
inline TYPE ensureValue##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
{ \
return ensureIsType<TYPE>(value, default_, what); \
} \

View File

@@ -2,8 +2,6 @@
#include "MessageLevel.h"
#include <QDebug>
#include <sys.h>
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
{
// QProcess has a strange interface... let's map a lot of those into a few.
@@ -67,47 +65,18 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
if (status == QProcess::NormalExit)
{
//: Message displayed on instance exit
emit log({tr("Process exited with code %1 (0x%2).").arg(exit_code).arg(exit_code, 0, 16)}, MessageLevel::Launcher);
emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Finished);
}
else
{
//: Message displayed on instance crashed
if(exit_code == -1)
emit log({tr("Process crashed.")}, MessageLevel::Launcher);
emit log({tr("Process crashed.")}, MessageLevel::MultiMC);
else
emit log({tr("Process crashed with exitcode %1 (0x%2).").arg(exit_code).arg(exit_code, 0, 16)}, MessageLevel::Launcher);
emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC);
changeState(LoggedProcess::Crashed);
}
// Filter out some exit codes, which would only result in erroneous output
// -1, 0, 1 and 255 are usually program generated and don't aid much in debugging
if((exit_code < -1 || exit_code > 1) && (exit_code != 255))
{
// Gross hack for preserving the **exact bit pattern**, we need to "cast" while ignoring the sign bit
unsigned int u_exit_code = *((unsigned int *) &exit_code);
std::string statusName;
std::string statusDescription;
bool hasNameOrDescription = Sys::lookupSystemStatusCode(u_exit_code, statusName, statusDescription);
if(hasNameOrDescription)
{
emit log({tr("Below is an analysis of the exit code. THIS MAY BE INCORRECT AND SHOULD BE TAKEN WITH A GRAIN OF SALT!")}, MessageLevel::Launcher);
if(!statusName.empty())
{
emit log({tr("System exit code name: %1").arg(QString::fromStdString(statusName))}, MessageLevel::Launcher);
}
if(!statusDescription.empty())
{
emit log({tr("System exit code description: %1").arg(QString::fromStdString(statusDescription))}, MessageLevel::Launcher);
}
}
}
emit log({tr("Please note that usually neither the exit code, nor its description are enough to diagnose issues!")}, MessageLevel::Launcher);
emit log({tr("Always upload the entire log and not just the exit code.")}, MessageLevel::Launcher);
}
else
{

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -17,12 +17,13 @@
#include <QProcess>
#include "MessageLevel.h"
#include "multimc_logic_export.h"
/*
* This is a basic process.
* It has line-based logging support and hides some of the nasty bits.
*/
class LoggedProcess : public QProcess
class MULTIMC_LOGIC_EXPORT LoggedProcess : public QProcess
{
Q_OBJECT
public:

10
api/logic/MMCStrings.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <QString>
#include "multimc_logic_export.h"
namespace Strings
{
int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -208,27 +208,16 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re
// ours
nonstd::optional<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);
QStringList extracted;
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
auto numEntries = zip->getEntriesCount();
if(numEntries < 0) {
qWarning() << "Failed to enumerate files in archive";
return nonstd::nullopt;
}
else if(numEntries == 0) {
qDebug() << "Extracting empty archives seems odd...";
return extracted;
}
else if (!zip->goToFirstFile())
if (!zip->goToFirstFile())
{
qWarning() << "Failed to seek to first file in zip";
return nonstd::nullopt;
return QStringList();
}
do
{
QString name = zip->getCurrentFileName();
@@ -246,7 +235,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
{
qWarning() << "Failed to extract file" << name << "to" << absFilePath;
JlCompress::removeFile(extracted);
return nonstd::nullopt;
return QStringList();
}
extracted.append(absFilePath);
qDebug() << "Extracted file" << name;
@@ -255,58 +244,12 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
}
// ours
bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &target)
{
return JlCompress::extractFile(zip, file, target);
}
// ours
nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
QStringList MMCZip::extractDir(QString fileCompressed, QString dir)
{
QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip))
{
// check if this is a minimum size empty zip file...
QFileInfo fileInfo(fileCompressed);
if(fileInfo.size() == 22) {
return QStringList();
}
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
return nonstd::nullopt;
return {};
}
return MMCZip::extractSubDir(&zip, "", dir);
}
// ours
nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
{
QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip))
{
// check if this is a minimum size empty zip file...
QFileInfo fileInfo(fileCompressed);
if(fileInfo.size() == 22) {
return QStringList();
}
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
return nonstd::nullopt;
}
return MMCZip::extractSubDir(&zip, subdir, dir);
}
// ours
bool MMCZip::extractFile(QString fileCompressed, QString file, QString target)
{
QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip))
{
// check if this is a minimum size empty zip file...
QFileInfo fileInfo(fileCompressed);
if(fileInfo.size() == 22) {
return true;
}
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
return false;
}
return MMCZip::extractRelFile(&zip, file, target);
}

71
api/logic/MMCZip.h Normal file
View File

@@ -0,0 +1,71 @@
/* 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
#include <QString>
#include <QFileInfo>
#include <QSet>
#include "minecraft/Mod.h"
#include <functional>
#include "multimc_logic_export.h"
#include <JlCompress.h>
namespace MMCZip
{
/**
* Merge two zip files, using a filter function
*/
bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
const JlCompress::FilterFunction filter = nullptr);
/**
* 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);
/**
* Find a single file in archive by file name (not path)
*
* \return the path prefix where the file is
*/
QString MULTIMC_LOGIC_EXPORT findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
/**
* Find a multiple files of the same name in archive by file name
* If a file is found in a path, no deeper paths are searched
*
* \return true if anything was found
*/
bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
/**
* Extract a subdirectory from an archive
*/
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);
}

View File

@@ -2,8 +2,8 @@
MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
{
if (levelName == "Launcher")
return MessageLevel::Launcher;
if (levelName == "MultiMC")
return MessageLevel::MultiMC;
else if (levelName == "Debug")
return MessageLevel::Debug;
else if (levelName == "Info")

View File

@@ -13,7 +13,7 @@ enum Enum
Unknown, /**< No idea what this is or where it came from */
StdOut, /**< Undetermined stderr messages */
StdErr, /**< Undetermined stdout messages */
Launcher, /**< Launcher Messages */
MultiMC, /**< MultiMC Messages */
Debug, /**< Debug Messages */
Info, /**< Info Messages */
Message, /**< Standard Messages */

View File

@@ -27,7 +27,7 @@ public:
{
return instanceRoot();
};
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
{
return nullptr;
}
@@ -67,13 +67,10 @@ public:
{
return false;
}
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
QStringList verboseDescription(AuthSessionPtr session) override
{
QStringList out;
out << "Null instance - placeholder.";
return out;
}
QString modsRoot() const override {
return QString();
}
};

View File

@@ -1,5 +1,7 @@
#pragma once
#include "multimc_logic_export.h"
enum class ProblemSeverity
{
None,
@@ -13,7 +15,7 @@ struct PatchProblem
QString m_description;
};
class ProblemProvider
class MULTIMC_LOGIC_EXPORT ProblemProvider
{
public:
virtual ~ProblemProvider() {};
@@ -21,7 +23,7 @@ public:
virtual ProblemSeverity getProblemSeverity() const = 0;
};
class ProblemContainer : public ProblemProvider
class MULTIMC_LOGIC_EXPORT ProblemContainer : public ProblemProvider
{
public:
const QList<PatchProblem> getProblems() const override

View File

@@ -77,12 +77,6 @@ public:
{
return m_ptr;
}
bool operator==(const shared_qobject_ptr<T>& other) {
return m_ptr == other.m_ptr;
}
bool operator!=(const shared_qobject_ptr<T>& other) {
return m_ptr != other.m_ptr;
}
private:
std::shared_ptr <T> m_ptr;

View File

@@ -1,9 +1,6 @@
#pragma once
#include <QWriteLocker>
#include <QReadLocker>
#include <QMap>
#include <QSet>
template <typename K, typename V>
class RWStorage
{

View File

@@ -4,7 +4,9 @@
#include <QDir>
#include "pathmatcher/IPathMatcher.h"
class RecursiveFileSystemWatcher : public QObject
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT RecursiveFileSystemWatcher : public QObject
{
Q_OBJECT
public:

View File

@@ -3,8 +3,6 @@
#include <cstddef>
#include <memory>
#include "QObjectPtr.h"
class Usable;
/**
@@ -16,11 +14,11 @@ class Usable
{
friend class UseLock;
public:
std::size_t useCount() const
std::size_t useCount()
{
return m_useCount;
}
bool isInUse() const
bool isInUse()
{
return m_useCount > 0;
}
@@ -45,7 +43,7 @@ private:
class UseLock
{
public:
UseLock(shared_qobject_ptr<Usable> usable)
UseLock(std::shared_ptr<Usable> usable)
: m_usable(usable)
{
// this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate.
@@ -56,5 +54,5 @@ public:
m_usable->decrementUses();
}
private:
shared_qobject_ptr<Usable> m_usable;
std::shared_ptr<Usable> m_usable;
};

View File

@@ -78,7 +78,7 @@ void Version::parse()
// FIXME: this is bad. versions can contain a lot more separators...
QStringList parts = m_string.split('.');
for (const auto& part : parts)
for (const auto part : parts)
{
m_sections.append(Section(part));
}

View File

@@ -3,9 +3,11 @@
#include <QString>
#include <QList>
#include "multimc_logic_export.h"
class QUrl;
class Version
class MULTIMC_LOGIC_EXPORT Version
{
public:
Version(const QString &str);

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -0,0 +1,7 @@
#include "IIconList.h"
// blargh
IIconList::~IIconList()
{
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <QString>
#include <QStringList>
#include "multimc_logic_export.h"
enum IconType : unsigned
{
Builtin,
Transient,
FileBased,
ICONS_TOTAL,
ToBeDeleted
};
class MULTIMC_LOGIC_EXPORT IIconList
{
public:
virtual ~IIconList();
virtual bool addIcon(const QString &key, const QString &name, const QString &path, const IconType type) = 0;
virtual bool deleteIcon(const QString &key) = 0;
virtual void saveIcon(const QString &key, const QString &path, const char * format) const = 0;
virtual bool iconFileExists(const QString &key) const = 0;
virtual void installIcons(const QStringList &iconFiles) = 0;
virtual void installIcon(const QString &file, const QString &name) = 0;
};

View File

@@ -1,13 +1,14 @@
#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
QString findBestIconIn(const QString &folder, const QString & iconKey);
MULTIMC_LOGIC_EXPORT QString findBestIconIn(const QString &folder, const QString & iconKey);
// Get icon file type filter for file browser dialogs
QString getIconFilter();
MULTIMC_LOGIC_EXPORT QString getIconFilter();
}

View File

@@ -1,14 +1,14 @@
#include "JavaChecker.h"
#include "JavaUtils.h"
#include <FileSystem.h>
#include <Commandline.h>
#include <QFile>
#include <QProcess>
#include <QMap>
#include <QCoreApplication>
#include <QDebug>
#include "JavaUtils.h"
#include "FileSystem.h"
#include "Commandline.h"
#include "Application.h"
#include "Env.h"
JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
{
@@ -16,7 +16,7 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
void JavaChecker::performCheck()
{
QString checkerJar = FS::PathCombine(APPLICATION->getJarsPath(), "JavaCheck.jar");
QString checkerJar = FS::PathCombine(ENV.getJarsPath(), "JavaCheck.jar");
QStringList args;
@@ -103,15 +103,11 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
for(QString line : lines)
{
line = line.trimmed();
// NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux
if (line.contains("/bedrock/strata")) {
continue;
}
auto parts = line.split('=', QString::SkipEmptyParts);
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
{
continue;
success = false;
}
else
{
@@ -119,7 +115,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
}
}
if(!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success)
if(!results.contains("os.arch") || !results.contains("java.version") || !success)
{
result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
emit checkFinished(result);
@@ -128,7 +124,6 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
auto os_arch = results["os.arch"];
auto java_version = results["java.version"];
auto java_vendor = results["java.vendor"];
bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
@@ -137,7 +132,6 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
result.mojangPlatform = is_64 ? "64" : "32";
result.realPlatform = os_arch;
result.javaVersion = java_version;
result.javaVendor = java_vendor;
qDebug() << "Java checker succeeded.";
emit checkFinished(result);
}
@@ -146,12 +140,8 @@ void JavaChecker::error(QProcess::ProcessError err)
{
if(err == QProcess::FailedToStart)
{
qDebug() << "Java checker has failed to start.";
qDebug() << "Process environment:";
qDebug() << process->environment();
qDebug() << "Native environment:";
qDebug() << QProcessEnvironment::systemEnvironment().toStringList();
killTimer.stop();
qDebug() << "Java checker has failed to start.";
JavaCheckResult result;
{
result.path = m_path;

View File

@@ -5,17 +5,18 @@
#include "QObjectPtr.h"
#include "multimc_logic_export.h"
#include "JavaVersion.h"
class JavaChecker;
struct JavaCheckResult
struct MULTIMC_LOGIC_EXPORT JavaCheckResult
{
QString path;
QString mojangPlatform;
QString realPlatform;
JavaVersion javaVersion;
QString javaVendor;
QString outLog;
QString errorLog;
bool is_64bit = false;
@@ -30,7 +31,7 @@ struct JavaCheckResult
typedef shared_qobject_ptr<QProcess> QProcessPtr;
typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr;
class JavaChecker : public QObject
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
{
Q_OBJECT
public:

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -29,13 +29,13 @@ JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
{
}
Task::Ptr JavaInstallList::getLoadTask()
shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
{
load();
return getCurrentTask();
}
Task::Ptr JavaInstallList::getCurrentTask()
shared_qobject_ptr<Task> JavaInstallList::getCurrentTask()
{
if(m_status == Status::InProgress)
{

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -26,9 +26,11 @@
#include "QObjectPtr.h"
#include "multimc_logic_export.h"
class JavaListLoadTask;
class JavaInstallList : public BaseVersionList
class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList
{
Q_OBJECT
enum class Status
@@ -40,7 +42,7 @@ class JavaInstallList : public BaseVersionList
public:
explicit JavaInstallList(QObject *parent = 0);
Task::Ptr getLoadTask() override;
shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
@@ -54,7 +56,7 @@ public slots:
protected:
void load();
Task::Ptr getCurrentTask();
shared_qobject_ptr<Task> getCurrentTask();
protected:
Status m_status = Status::NotDone;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -31,7 +31,7 @@ JavaUtils::JavaUtils()
{
}
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#ifdef Q_OS_LINUX
static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH)
{
QDir mmcBin(QCoreApplication::applicationDirPath());
@@ -83,7 +83,7 @@ QProcessEnvironment CleanEnviroment()
qDebug() << "Env: ignoring" << key << value;
continue;
}
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
#ifdef Q_OS_LINUX
// Do not pass LD_* variables to java. They were intended for MultiMC
if(key.startsWith("LD_"))
{
@@ -150,7 +150,7 @@ JavaInstallPtr JavaUtils::GetDefaultJava()
}
#if defined(Q_OS_WIN32)
QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix)
QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName)
{
QList<JavaInstallPtr> javas;
@@ -175,6 +175,8 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz);
}
QString recommended = value;
TCHAR subKeyName[255];
DWORD subKeyNameSize, numSubKeys, retCode;
@@ -193,7 +195,7 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
if (retCode == ERROR_SUCCESS)
{
// Now open the registry key for the version that we just got.
QString newKeyName = keyName + "\\" + subKeyName + subkeySuffix;
QString newKeyName = keyName + "\\" + subKeyName;
HKEY newKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0,
@@ -202,11 +204,11 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
// Read the JavaHome value to find where Java is installed.
value = new char[0];
valueSz = 0;
if (RegQueryValueEx(newKey, keyJavaDir.toStdString().c_str(), NULL, NULL, (BYTE *)value,
if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz) == ERROR_MORE_DATA)
{
value = new char[valueSz];
RegQueryValueEx(newKey, keyJavaDir.toStdString().c_str(), NULL, NULL, (BYTE *)value,
RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value,
&valueSz);
// Now, we construct the version object and add it to the list.
@@ -235,100 +237,25 @@ QList<QString> JavaUtils::FindJavaPaths()
{
QList<JavaInstallPtr> java_candidates;
// Oracle
QList<JavaInstallPtr> JRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome");
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome");
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
QList<JavaInstallPtr> JRE32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome");
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
QList<JavaInstallPtr> JDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome");
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
// Oracle for Java 9 and newer
QList<JavaInstallPtr> NEWJRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome");
QList<JavaInstallPtr> NEWJDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome");
QList<JavaInstallPtr> NEWJRE32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome");
QList<JavaInstallPtr> NEWJDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome");
// AdoptOpenJDK
QList<JavaInstallPtr> ADOPTOPENJRE32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI");
QList<JavaInstallPtr> ADOPTOPENJRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI");
QList<JavaInstallPtr> ADOPTOPENJDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI");
QList<JavaInstallPtr> ADOPTOPENJDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI");
// Eclipse Foundation
QList<JavaInstallPtr> FOUNDATIONJDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI");
QList<JavaInstallPtr> FOUNDATIONJDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI");
// Eclipse Adoptium
QList<JavaInstallPtr> ADOPTIUMJRE32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI");
QList<JavaInstallPtr> ADOPTIUMJRE64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI");
QList<JavaInstallPtr> ADOPTIUMJDK32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI");
QList<JavaInstallPtr> ADOPTIUMJDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI");
// Microsoft
QList<JavaInstallPtr> MICROSOFTJDK64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI");
// Azul Zulu
QList<JavaInstallPtr> ZULU64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
QList<JavaInstallPtr> ZULU32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath");
// BellSoft Liberica
QList<JavaInstallPtr> LIBERICA64s = this->FindJavaFromRegistryKey(
KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath");
QList<JavaInstallPtr> LIBERICA32s = this->FindJavaFromRegistryKey(
KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath");
// List x64 before x86
java_candidates.append(JRE64s);
java_candidates.append(NEWJRE64s);
java_candidates.append(ADOPTOPENJRE64s);
java_candidates.append(ADOPTIUMJRE64s);
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK64s);
java_candidates.append(NEWJDK64s);
java_candidates.append(ADOPTOPENJDK64s);
java_candidates.append(FOUNDATIONJDK64s);
java_candidates.append(ADOPTIUMJDK64s);
java_candidates.append(MICROSOFTJDK64s);
java_candidates.append(ZULU64s);
java_candidates.append(LIBERICA64s);
java_candidates.append(JRE32s);
java_candidates.append(NEWJRE32s);
java_candidates.append(ADOPTOPENJRE32s);
java_candidates.append(ADOPTIUMJRE32s);
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK32s);
java_candidates.append(NEWJDK32s);
java_candidates.append(ADOPTOPENJDK32s);
java_candidates.append(FOUNDATIONJDK32s);
java_candidates.append(ADOPTIUMJDK32s);
java_candidates.append(ZULU32s);
java_candidates.append(LIBERICA32s);
java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
QList<QString> candidates;
@@ -400,13 +327,9 @@ QList<QString> JavaUtils::FindJavaPaths()
scanJavaDir("/usr/java");
// general locations used by distro packaging
scanJavaDir("/usr/lib/jvm");
scanJavaDir("/usr/lib64/jvm");
scanJavaDir("/usr/lib32/jvm");
// javas stored in MultiMC's folder
scanJavaDir("java");
// manually installed JDKs in /opt
scanJavaDir("/opt/jdk");
scanJavaDir("/opt/jdks");
return javas;
}
#else

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -24,9 +24,11 @@
#include <windows.h>
#endif
#include "multimc_logic_export.h"
QProcessEnvironment CleanEnviroment();
class JavaUtils : public QObject
class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject
{
Q_OBJECT
public:
@@ -37,6 +39,6 @@ public:
JavaInstallPtr GetDefaultJava();
#ifdef Q_OS_WIN
QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix = "");
QList<JavaInstallPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
#endif
};

View File

@@ -1,5 +1,6 @@
#pragma once
#include "multimc_logic_export.h"
#include <QString>
// NOTE: apparently the GNU C library pollutes the global namespace with these... undef them.
@@ -10,7 +11,7 @@
#undef minor
#endif
class JavaVersion
class MULTIMC_LOGIC_EXPORT JavaVersion
{
friend class JavaVersionTest;
public:

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -33,22 +33,22 @@ void CheckJava::executeTask()
if (perInstance)
{
emit logLine(
QString("The java binary \"%1\" couldn't be found. Please fix the java path "
tr("The java binary \"%1\" couldn't be found. Please fix the java path "
"override in the instance's settings or disable it.").arg(m_javaPath),
MessageLevel::Warning);
}
else
{
emit logLine(QString("The java binary \"%1\" couldn't be found. Please set up java in "
emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in "
"the settings.").arg(m_javaPath),
MessageLevel::Warning);
}
emitFailed(QString("Java path is not valid."));
emitFailed(tr("Java path is not valid."));
return;
}
else
{
emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::Launcher);
emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC);
}
QFileInfo javaInfo(realJavaPath);
@@ -56,13 +56,12 @@ void CheckJava::executeTask()
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString();
auto storedVendor = settings->get("JavaVendor").toString();
m_javaUnixTime = javaUnixTime;
// if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0 || storedVendor.size() == 0)
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
{
m_JavaChecker = new JavaChecker();
emit logLine(QString("Checking Java version..."), MessageLevel::Launcher);
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath;
m_JavaChecker->performCheck();
@@ -72,8 +71,7 @@ void CheckJava::executeTask()
{
auto verString = instance->settings()->get("JavaVersion").toString();
auto archString = instance->settings()->get("JavaArchitecture").toString();
auto vendorString = instance->settings()->get("JavaVendor").toString();
printJavaInfo(verString, archString, vendorString);
printJavaInfo(verString, archString);
}
emitSucceeded();
}
@@ -85,18 +83,18 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
case JavaCheckResult::Validity::Errored:
{
// Error message displayed if java can't start
emit logLine(QString("Could not start java:"), MessageLevel::Error);
emit logLine(tr("Could not start java:"), MessageLevel::Error);
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::Launcher);
emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
printSystemInfo(false, false);
emitFailed(QString("Could not start java!"));
emitFailed(tr("Could not start java!"));
return;
}
case JavaCheckResult::Validity::ReturnedInvalidData:
{
emit logLine(QString("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC);
printSystemInfo(false, false);
emitSucceeded();
return;
@@ -104,10 +102,9 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
case JavaCheckResult::Validity::Valid:
{
auto instance = m_parent->instance();
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.javaVendor);
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
instance->settings()->set("JavaVersion", result.javaVersion.toString());
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaVendor", result.javaVendor);
instance->settings()->set("JavaTimestamp", m_javaUnixTime);
emitSucceeded();
return;
@@ -115,9 +112,9 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
}
}
void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString & vendor)
void CheckJava::printJavaInfo(const QString& version, const QString& architecture)
{
emit logLine(QString("Java is version %1, using %2-bit architecture, from %3.\n\n").arg(version, architecture, vendor), MessageLevel::Launcher);
emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
printSystemInfo(true, architecture == "64");
}
@@ -127,13 +124,13 @@ void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)
auto system64 = Sys::isSystem64bit();
if(cpu64 != system64)
{
emit logLine(QString("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
}
if(javaIsKnown)
{
if(javaIs64bit != system64)
{
emit logLine(QString("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
}
}
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.
@@ -35,7 +35,7 @@ private slots:
void checkJavaFinished(JavaCheckResult result);
private:
void printJavaInfo(const QString & version, const QString & architecture, const QString & vendor);
void printJavaInfo(const QString & version, const QString & architecture);
void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
private:

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* 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.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*

View File

@@ -1,4 +1,4 @@
/* Copyright 2013-2021 MultiMC Contributors
/* Copyright 2013-2019 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -24,7 +24,9 @@
#include "LoggedProcess.h"
#include "LaunchStep.h"
class LaunchTask: public Task
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT LaunchTask: public Task
{
Q_OBJECT
protected:
@@ -103,8 +105,8 @@ signals:
void requestLogging();
public slots:
void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::Launcher);
void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::Launcher);
void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onReadyForLaunch();
void onStepFinished();
void onProgressReportingRequested();

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