diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bcf575331..95a5cece5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build 1.1.0+ +name: Build on: push: @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - name: Install Dependencies @@ -39,7 +39,8 @@ jobs: - name: Build run: | dart pub global activate cider - dart pub global run cider version ${{ env.rwl_version_full }} + dart pub global run cider version ${{ env.rwl_version_full }} + flutter pub run build_runner build --delete-conflicting-outputs flutter build linux --dart-define="build_id=${{ github.run_number }}" --dart-define="version_type=${{ env.version_type }}" --dart-define="version=${{ secrets.VERSION }}" - name: Upload File uses: actions/upload-artifact@v2 @@ -52,7 +53,7 @@ jobs: if: ${{ !contains(github.event.head_commit.message,'[ci skip]') }} runs-on: ubuntu-latest needs: ["Build-Linux"] - env: + env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }} steps: - uses: actions/checkout@v3 @@ -82,13 +83,14 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - name: Build App run: | dart pub global activate cider dart pub global run cider version ${{ env.rwl_version_full }} + flutter pub run build_runner build --delete-conflicting-outputs flutter build windows --dart-define="build_id=${{ github.run_number }}" --dart-define="version_type=${{ env.version_type }}" --dart-define="version=${{ secrets.VERSION }}" - name: Build Installer run: | @@ -132,7 +134,7 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - name: Build @@ -140,7 +142,8 @@ jobs: flutter pub get pod repo update dart pub global activate cider - dart pub global run cider version ${{ env.rwl_version_full }} + dart pub global run cider version ${{ env.rwl_version_full }} + flutter pub run build_runner build --delete-conflicting-outputs flutter build macos --dart-define="build_id=${{ github.run_number }}" --dart-define="version_type=${{ env.version_type }}" --dart-define="version=${{ secrets.VERSION }}" --release cp assets/images/macOS_logo_icon.icns build/macos/Build/Products cd build/macos/Build/Products @@ -258,10 +261,12 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - - run: flutter pub get + - run: | + flutter pub get + flutter pub run build_runner build --delete-conflicting-outputs - name: Analyze the code uses: invertase/github-action-dart-analyzer@v1 with: @@ -281,12 +286,13 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - name: Generate coverage run: | flutter pub get + flutter pub run build_runner build --delete-conflicting-outputs flutter test --coverage shell: bash continue-on-error: true diff --git a/.github/workflows/no_response.yaml b/.github/workflows/no_response.yaml deleted file mode 100644 index 03a9261a6..000000000 --- a/.github/workflows/no_response.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: No Response - -on: -# issue_comment: -# types: [created] - schedule: - - cron: "0 0 * * *" - -permissions: - issues: write - -jobs: - noResponse: - runs-on: ubuntu-latest - if: ${{ github.repository == 'RPMTW/RPMLauncher' }} - steps: - - uses: lee-dohm/no-response@9bb0a4b5e6a45046f00353d5de7d90fb8bd773bb - with: - token: ${{ github.token }} - closeComment: > - Without additional information, we do not know how to resolve this issue, so we will close this topic. - Thank you for your contribution. - - 如果沒有額外的資訊,我們不知道如何解決此議題,因此我們將關閉此議題。 - 感謝您的貢獻。 - daysUntilClose: 14 - responseRequiredLabel: "Waiting for response | 等待回應" diff --git a/.github/workflows/pull_request_analyze.yml b/.github/workflows/pull_request_analyze.yml index 84bb7de34..2e673b7cd 100644 --- a/.github/workflows/pull_request_analyze.yml +++ b/.github/workflows/pull_request_analyze.yml @@ -4,19 +4,20 @@ on: [pull_request] jobs: Analyze: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v3 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - - run: flutter pub get + - run: | + flutter pub get + flutter pub run build_runner build --delete-conflicting-outputs - name: Analyze the code uses: invertase/github-action-dart-analyzer@v1 with: - fatal-infos: true + fatal-infos: false fatal-warnings: true annotate: true annotate-only: false @@ -31,12 +32,13 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - name: Generate coverage run: | flutter pub get + flutter pub run build_runner build --delete-conflicting-outputs flutter test --coverage shell: bash continue-on-error: true @@ -46,7 +48,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage/lcov.info - Test-Build: + Build: strategy: matrix: runs-on: [ubuntu-latest, windows-latest, macos-latest] @@ -66,7 +68,7 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@main with: - flutter-version: 3.7.0-1.1.pre + flutter-version: 3.8.0-10.1.pre channel: beta cache: true - name: Install Dependencies @@ -76,7 +78,8 @@ jobs: sudo apt-get install ninja-build libgtk-3-dev libblkid-dev - name: Build for ${{ runner.os }} run: | - flutter config --enable-${{ matrix.os-name }}-desktop + flutter pub get + flutter pub run build_runner build --delete-conflicting-outputs flutter build ${{ matrix.os-name }} --dart-define="build_id=${{ github.run_number }}" --dart-define="version_type=debug" --dart-define="version=${{ secrets.VERSION }}" - name: Upload File uses: actions/upload-artifact@v2 diff --git a/.gitignore b/.gitignore index 99a08e134..ce101de18 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ rpmlauncher.snap .flatpak-builder flatpak/repo RPMLauncher-Linux + +# Generated dart files +*.g.dart \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 0ada25714..000000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "configurations": [ - { - "name": "Win32", - "includePath": [ - "${workspaceFolder}/windows/**" - ], - "windowsSdkVersion": "10.0.19041.0", - "compilerPath": "D:\\APP\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30037\\bin\\Hostx64\\x64\\cl.exe", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "windows-msvc-x64", - "configurationProvider": "ms-vscode.cmake-tools", - "defines": [], - "macFrameworkPath": [], - "forcedInclude": [], - "compilerArgs": [], - "mergeConfigurations": false, - "browse": { - "path": [ - "${workspaceFolder}/plugin/windows/**", - "${workspaceFolder}" - ], - "limitSymbolsToIncludedHeaders": true - } - }, - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/linux/**" - ], - "windowsSdkVersion": "10.0.19041.0", - "compilerPath": "/bin/cpp", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "linux-gcc-x64", - "defines": [], - "macFrameworkPath": [], - "forcedInclude": [], - "compilerArgs": [], - "mergeConfigurations": false, - "configurationProvider": "ms-vscode.cmake-tools", - "browse": { - "path": [ - "${workspaceFolder}/linux/**", - "${workspaceFolder}" - ], - "limitSymbolsToIncludedHeaders": true - } - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index e56dfd284..3cd4e1f5a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,7 @@ "--dart-define", "version_type=debug", "--dart-define", - "version=1.1.0" + "version=2.0.0" ] }, { @@ -30,7 +30,7 @@ "--dart-define", "version_type=debug", "--dart-define", - "version=1.1.0" + "version=2.0.0" ] }, { @@ -45,7 +45,7 @@ "--dart-define", "version_type=dev", "--dart-define", - "version=1.1.0" + "version=2.0.0" ] } ] diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index a23623d88..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", - "C_Cpp.errorSquiggles": "disabled", - "cSpell.language": "en,en-US", - "cSpell.words": [ - "murmurhash" - ] -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index cbd41a32e..000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "dart", - "command": "dart", - "args": [ - "format", - "lib" - ], - "problemMatcher": [], - "label": "dart: format files" - }, - ] -} \ No newline at end of file diff --git a/README.md b/README.md index 24227283c..5c596d323 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Build RPMLauncher requires Flutter SDK and Dart SDK [Official Flutter Tutorial](https://flutter.dev/desktop) ``` flutter pub get -flutter config --enable--desktop +flutter pub run build_runner build --delete-conflicting-outputs flutter build ``` @@ -86,3 +86,10 @@ Note: The RPMLauncher auto-update function is not supported for MacOS at this ti - 3X0DUS - ChAoS#6969 - KyleUltimate - 嗡嗡#5428 (RPMLauncher Logo Design) + +## License +All code is licensed under the [GPL-3.0 License](https://www.gnu.org/licenses/gpl-3.0.html). +Part of the asset is owned by the asset owner. +The RPMLauncher logo is under the [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en) license. + +NOT AN OFFICIAL MINECRAFT PRODUCT. NOT APPROVED BY OR ASSOCIATED WITH MOJANG. \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index 3aa38155b..b0af0ae12 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,10 +2,6 @@ include: package:flutter_lints/flutter.yaml linter: rules: - file_names: false - empty_catches: false - use_key_in_widget_constructors: false - avoid_function_literals_in_foreach_calls: false analyzer: errors: diff --git a/assets/fonts/Asap/Asap-Black.ttf b/assets/fonts/Asap/Asap-Black.ttf new file mode 100644 index 000000000..2c0f746d6 Binary files /dev/null and b/assets/fonts/Asap/Asap-Black.ttf differ diff --git a/assets/fonts/Asap/Asap-BlackItalic.ttf b/assets/fonts/Asap/Asap-BlackItalic.ttf new file mode 100644 index 000000000..34d77d4a2 Binary files /dev/null and b/assets/fonts/Asap/Asap-BlackItalic.ttf differ diff --git a/assets/fonts/Asap/Asap-Bold.ttf b/assets/fonts/Asap/Asap-Bold.ttf new file mode 100644 index 000000000..226601155 Binary files /dev/null and b/assets/fonts/Asap/Asap-Bold.ttf differ diff --git a/assets/fonts/Asap/Asap-BoldItalic.ttf b/assets/fonts/Asap/Asap-BoldItalic.ttf new file mode 100644 index 000000000..a312223f8 Binary files /dev/null and b/assets/fonts/Asap/Asap-BoldItalic.ttf differ diff --git a/assets/fonts/Asap/Asap-ExtraBold.ttf b/assets/fonts/Asap/Asap-ExtraBold.ttf new file mode 100644 index 000000000..7afdc30dc Binary files /dev/null and b/assets/fonts/Asap/Asap-ExtraBold.ttf differ diff --git a/assets/fonts/Asap/Asap-ExtraBoldItalic.ttf b/assets/fonts/Asap/Asap-ExtraBoldItalic.ttf new file mode 100644 index 000000000..6c6e04995 Binary files /dev/null and b/assets/fonts/Asap/Asap-ExtraBoldItalic.ttf differ diff --git a/assets/fonts/Asap/Asap-ExtraLight.ttf b/assets/fonts/Asap/Asap-ExtraLight.ttf new file mode 100644 index 000000000..6e64a5f9e Binary files /dev/null and b/assets/fonts/Asap/Asap-ExtraLight.ttf differ diff --git a/assets/fonts/Asap/Asap-ExtraLightItalic.ttf b/assets/fonts/Asap/Asap-ExtraLightItalic.ttf new file mode 100644 index 000000000..467235174 Binary files /dev/null and b/assets/fonts/Asap/Asap-ExtraLightItalic.ttf differ diff --git a/assets/fonts/Asap/Asap-Italic.ttf b/assets/fonts/Asap/Asap-Italic.ttf new file mode 100644 index 000000000..0d3c238c5 Binary files /dev/null and b/assets/fonts/Asap/Asap-Italic.ttf differ diff --git a/assets/fonts/Asap/Asap-Light.ttf b/assets/fonts/Asap/Asap-Light.ttf new file mode 100644 index 000000000..448fae592 Binary files /dev/null and b/assets/fonts/Asap/Asap-Light.ttf differ diff --git a/assets/fonts/Asap/Asap-LightItalic.ttf b/assets/fonts/Asap/Asap-LightItalic.ttf new file mode 100644 index 000000000..ae88aeefe Binary files /dev/null and b/assets/fonts/Asap/Asap-LightItalic.ttf differ diff --git a/assets/fonts/Asap/Asap-Medium.ttf b/assets/fonts/Asap/Asap-Medium.ttf new file mode 100644 index 000000000..826c338c2 Binary files /dev/null and b/assets/fonts/Asap/Asap-Medium.ttf differ diff --git a/assets/fonts/Asap/Asap-MediumItalic.ttf b/assets/fonts/Asap/Asap-MediumItalic.ttf new file mode 100644 index 000000000..35d4498f6 Binary files /dev/null and b/assets/fonts/Asap/Asap-MediumItalic.ttf differ diff --git a/assets/fonts/Asap/Asap-Regular.ttf b/assets/fonts/Asap/Asap-Regular.ttf new file mode 100644 index 000000000..9417baaa3 Binary files /dev/null and b/assets/fonts/Asap/Asap-Regular.ttf differ diff --git a/assets/fonts/Asap/Asap-SemiBold.ttf b/assets/fonts/Asap/Asap-SemiBold.ttf new file mode 100644 index 000000000..f42547088 Binary files /dev/null and b/assets/fonts/Asap/Asap-SemiBold.ttf differ diff --git a/assets/fonts/Asap/Asap-SemiBoldItalic.ttf b/assets/fonts/Asap/Asap-SemiBoldItalic.ttf new file mode 100644 index 000000000..3db3c81bc Binary files /dev/null and b/assets/fonts/Asap/Asap-SemiBoldItalic.ttf differ diff --git a/assets/fonts/Asap/Asap-Thin.ttf b/assets/fonts/Asap/Asap-Thin.ttf new file mode 100644 index 000000000..3d9d1c109 Binary files /dev/null and b/assets/fonts/Asap/Asap-Thin.ttf differ diff --git a/assets/fonts/Asap/Asap-ThinItalic.ttf b/assets/fonts/Asap/Asap-ThinItalic.ttf new file mode 100644 index 000000000..24694d313 Binary files /dev/null and b/assets/fonts/Asap/Asap-ThinItalic.ttf differ diff --git a/assets/fonts/Asap/SIL_Open_Font_License_1.1.txt b/assets/fonts/Asap/SIL_Open_Font_License_1.1.txt new file mode 100644 index 000000000..e4b0c4ff5 --- /dev/null +++ b/assets/fonts/Asap/SIL_Open_Font_License_1.1.txt @@ -0,0 +1,91 @@ +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/assets/fonts/NotoSansCJKtc-Regular.otf b/assets/fonts/GenJyuuGothic/GenJyuuGothic-Bold.ttf similarity index 51% rename from assets/fonts/NotoSansCJKtc-Regular.otf rename to assets/fonts/GenJyuuGothic/GenJyuuGothic-Bold.ttf index 62ed47d67..b776d4abb 100644 Binary files a/assets/fonts/NotoSansCJKtc-Regular.otf and b/assets/fonts/GenJyuuGothic/GenJyuuGothic-Bold.ttf differ diff --git a/assets/fonts/GenJyuuGothic/GenJyuuGothic-Medium.ttf b/assets/fonts/GenJyuuGothic/GenJyuuGothic-Medium.ttf new file mode 100644 index 000000000..45598f699 Binary files /dev/null and b/assets/fonts/GenJyuuGothic/GenJyuuGothic-Medium.ttf differ diff --git a/assets/fonts/GenJyuuGothic/SIL_Open_Font_License_1.1.txt b/assets/fonts/GenJyuuGothic/SIL_Open_Font_License_1.1.txt new file mode 100644 index 000000000..e4b0c4ff5 --- /dev/null +++ b/assets/fonts/GenJyuuGothic/SIL_Open_Font_License_1.1.txt @@ -0,0 +1,91 @@ +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/assets/images/Forge.jpg b/assets/images/Forge.jpg deleted file mode 100644 index 2ad0df985..000000000 Binary files a/assets/images/Forge.jpg and /dev/null differ diff --git a/assets/images/Logo.png b/assets/images/Logo.png deleted file mode 100644 index b99ffe915..000000000 Binary files a/assets/images/Logo.png and /dev/null differ diff --git a/assets/images/Minecraft.png b/assets/images/Minecraft.png deleted file mode 100644 index 84ac204eb..000000000 Binary files a/assets/images/Minecraft.png and /dev/null differ diff --git a/assets/images/background.png b/assets/images/background.png index 07dba6275..9295583f6 100644 Binary files a/assets/images/background.png and b/assets/images/background.png differ diff --git a/assets/images/Fabric.png b/assets/images/loader/fabric.png similarity index 100% rename from assets/images/Fabric.png rename to assets/images/loader/fabric.png diff --git a/assets/images/loader/fabric_background.png b/assets/images/loader/fabric_background.png new file mode 100644 index 000000000..04742680b Binary files /dev/null and b/assets/images/loader/fabric_background.png differ diff --git a/assets/images/loader/forge.png b/assets/images/loader/forge.png new file mode 100644 index 000000000..c204f26fc Binary files /dev/null and b/assets/images/loader/forge.png differ diff --git a/assets/images/loader/forge_background.png b/assets/images/loader/forge_background.png new file mode 100644 index 000000000..fd657678c Binary files /dev/null and b/assets/images/loader/forge_background.png differ diff --git a/assets/images/loader/quilt.png b/assets/images/loader/quilt.png new file mode 100644 index 000000000..9206ed678 Binary files /dev/null and b/assets/images/loader/quilt.png differ diff --git a/assets/images/loader/quilt_background.png b/assets/images/loader/quilt_background.png new file mode 100644 index 000000000..8f335a300 Binary files /dev/null and b/assets/images/loader/quilt_background.png differ diff --git a/assets/images/loader/vanilla.png b/assets/images/loader/vanilla.png new file mode 100644 index 000000000..d8f0c89e3 Binary files /dev/null and b/assets/images/loader/vanilla.png differ diff --git a/assets/images/loader/vanilla_background.png b/assets/images/loader/vanilla_background.png new file mode 100644 index 000000000..5537aa092 Binary files /dev/null and b/assets/images/loader/vanilla_background.png differ diff --git a/assets/images/loading_animation.json b/assets/images/loading_animation.json new file mode 100644 index 000000000..7de00c15b --- /dev/null +++ b/assets/images/loading_animation.json @@ -0,0 +1 @@ +{"nm":"Tricube Loader #1_Normal Positioning (Lottie Render)","mn":"","layers":[{"ty":4,"nm":"Cube 4 Matte [Iso 5]","mn":"","sr":1,"st":-30,"op":24,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[128,152,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[1117.5,1050,0],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[960,1140,0],"t":24}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,18.001],[-10.58,6.001],[-10.58,-17.999],[10.205,-5.999]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,6.001],[-10.58,18.001],[-10.58,-5.999],[10.205,-17.999]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-0.187,12.001],[-20.972,0.001],[-0.188,-11.999],[20.597,0.001]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[128,140],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":0},{"ty":4,"nm":"Isometric Cube 6","mn":"","sr":1,"st":-30,"op":24,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[107.215,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[802.5,1050,0],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[804.1125000000001,870,0],"t":24}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.58,18],[-10.205,6],[-10.205,-18],[10.58,-6]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[96.823,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.58,6],[-10.205,18],[-10.205,-6],[10.58,-18]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0.188,12],[-20.597,0],[0.187,-12],[20.972,0]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[107.215,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Cube 6 Matte [Iso 4]","mn":"","sr":1,"st":-30,"op":24,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[148.785,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[960,780,0],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[1115.8875,870,0],"t":24}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Side","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,18],[-10.392,6],[-10.392,-18],[10.392,-6]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Front","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,6],[-10.392,18],[-10.392,-6],[10.392,-18]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[159.177,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Top","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,12],[-20.785,0],[0,-12],[20.785,0]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[148.785,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2},{"ty":4,"nm":"Isometric Cube 5","mn":"","sr":1,"st":-30,"op":24,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[128,152,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[1117.5,1050,0],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[960,1140,0],"t":24}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,18.001],[-10.58,6.001],[-10.58,-17.999],[10.205,-5.999]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,6.001],[-10.58,18.001],[-10.58,-5.999],[10.205,-17.999]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-0.187,12.001],[-20.972,0.001],[-0.031,-11.999],[20.597,0.001]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[128,140],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3},{"ty":4,"nm":"Cube 5 Matte [Iso 6]","mn":"","sr":1,"st":-30,"op":24,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[107.215,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[802.5,1050,0],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[804.1125000000001,870,0],"t":24}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.611,18],[-10.174,6],[-10.174,-18],[10.611,-6]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[96.823,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.611,6],[-10.174,18],[-10.174,-6],[10.611,-18]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0.219,12],[-20.566,0],[0.219,-12],[21.003,0]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[107.215,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4},{"ty":4,"nm":"Isometric Cube 4","mn":"","sr":1,"st":-30,"op":24,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[148.785,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[960,780,0],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[1115.8875,870,0],"t":24}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Side","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,18],[-10.392,6],[-10.392,-18],[10.392,-6]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Front","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,6],[-10.392,18],[-10.392,-6],[10.392,-18]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[159.177,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Top","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,12],[-20.785,0],[0,-12],[20.785,0]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[148.785,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":5},{"ty":4,"nm":"Cube 2 Matte [Iso 3]","mn":"","sr":1,"st":23,"op":52,"ip":24,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[148.785,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[1117.5,870,0],"t":24},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[1115.8875,1050,0],"t":48}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Side","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,17.989],[-10.58,5.989],[-10.58,-18.01],[10.205,-6.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Front","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,5.989],[-10.58,17.989],[-10.58,-6.01],[10.205,-18.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[159.177,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Top","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-0.187,11.989],[-20.972,-0.011],[-0.188,-12.01],[20.597,-0.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[148.785,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":6},{"ty":4,"nm":"Isometric Cube 1","mn":"","sr":1,"st":23,"op":52,"ip":24,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[107.215,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[802.5,870,0],"t":24},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[960,780,0],"t":48}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.58,17.989],[-10.205,5.989],[-10.205,-18.01],[10.58,-6.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[96.823,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.58,5.989],[-10.205,17.989],[-10.205,-6.01],[10.58,-18.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0.188,11.989],[-20.597,-0.011],[0.187,-12.01],[20.972,-0.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[107.215,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":7},{"ty":4,"nm":"Cube 3 Matte [Iso 1]","mn":"","sr":1,"st":23,"op":52,"ip":24,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[107.215,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[802.5,870,0],"t":24},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[960,780,0],"t":48}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.611,17.989],[-10.174,5.989],[-10.174,-18.01],[10.611,-6.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[96.823,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.611,5.989],[-10.174,17.989],[-10.174,-6.01],[10.611,-18.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0.219,11.989],[-20.566,-0.011],[0.219,-12.01],[21.003,-0.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[107.215,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":8},{"ty":4,"nm":"Isometric Cube 2","mn":"","sr":1,"st":23,"op":52,"ip":24,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[128,152,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[960,1140,0],"t":24},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[804.1125000000001,1050,0],"t":48}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,18],[-10.392,6],[-10.392,-18],[10.392,-6]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,6],[-10.392,18],[-10.392,-6],[10.392,-18]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,12],[-20.785,0],[0,-12],[20.785,0]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[128,140],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":9},{"ty":4,"nm":"Cube 1 Matte [Iso 2]","mn":"","sr":1,"st":23,"op":52,"ip":24,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[128,152,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[960,1140,0],"t":24},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[804.1125000000001,1050,0],"t":48}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,18],[-10.392,6],[-10.392,-18],[10.392,-6]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[117.608,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.392,6],[-10.392,18],[-10.392,-6],[10.392,-18]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,158],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 3","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0,12],[-20.785,0],[0,-12],[20.785,0]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.6745,0.6745,0.6745],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[128,140],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":10},{"ty":4,"nm":"Isometric Cube 3","mn":"","sr":1,"st":23,"op":52,"ip":24,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[148.785,116,0],"ix":1},"s":{"a":0,"k":[750,750,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.8,"y":0},"i":{"x":0.2,"y":1},"s":[1117.5,870,0],"t":24},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[1115.8875,1050,0],"t":48}],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Side","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,17.989],[-10.58,5.989],[-10.58,-18.01],[10.205,-6.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[138.392,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Front","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.205,5.989],[-10.58,17.989],[-10.58,-6.01],[10.205,-18.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[159.177,122],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Top","ix":3,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-0.187,11.989],[-20.972,-0.011],[-0.188,-12.01],[20.597,-0.01]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":2,"ml":1,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[148.785,104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":11},{"ty":4,"nm":"LIttle Black Dot behind the mattes","mn":"","sr":1,"st":0,"op":53,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[-0.125,0.125,0],"ix":1},"s":{"a":0,"k":[564,564,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[959.0625,960.9375,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[10.541,10.541],"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"d":[],"c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":3}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1176,0.1176,0.1176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-0.125,0.125],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":12}],"ddd":0,"h":1920,"w":1920,"meta":{"a":"","k":"","d":"","g":"LottieFiles AE 3.0.2","tc":"#ffffff"},"v":"4.8.0","fr":60,"op":48,"ip":0,"assets":[]} \ No newline at end of file diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 000000000..f57fe0e0f Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/macOS_logo_icon.icns b/assets/images/macOS_logo_icon.icns index 21f8900a0..eca28b1bf 100644 Binary files a/assets/images/macOS_logo_icon.icns and b/assets/images/macOS_logo_icon.icns differ diff --git a/assets/images/rpmtw-logo-black.svg b/assets/images/rpmtw-logo-black.svg new file mode 100644 index 000000000..2eb1bb666 --- /dev/null +++ b/assets/images/rpmtw-logo-black.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/assets/images/rpmtw-logo-white.svg b/assets/images/rpmtw-logo-white.svg new file mode 100644 index 000000000..da05e3181 --- /dev/null +++ b/assets/images/rpmtw-logo-white.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/assets/images/versions/1.10.png b/assets/images/versions/1.10.png new file mode 100644 index 000000000..346c74132 Binary files /dev/null and b/assets/images/versions/1.10.png differ diff --git a/assets/images/versions/1.11.png b/assets/images/versions/1.11.png new file mode 100644 index 000000000..b5a6b0815 Binary files /dev/null and b/assets/images/versions/1.11.png differ diff --git a/assets/images/versions/1.12.png b/assets/images/versions/1.12.png new file mode 100644 index 000000000..898f55a24 Binary files /dev/null and b/assets/images/versions/1.12.png differ diff --git a/assets/images/versions/1.13.png b/assets/images/versions/1.13.png new file mode 100644 index 000000000..bc33e5edc Binary files /dev/null and b/assets/images/versions/1.13.png differ diff --git a/assets/images/versions/1.14.png b/assets/images/versions/1.14.png new file mode 100644 index 000000000..8896c756c Binary files /dev/null and b/assets/images/versions/1.14.png differ diff --git a/assets/images/versions/1.15.png b/assets/images/versions/1.15.png new file mode 100644 index 000000000..d25b0a568 Binary files /dev/null and b/assets/images/versions/1.15.png differ diff --git a/assets/images/versions/1.16.png b/assets/images/versions/1.16.png new file mode 100644 index 000000000..bd1b25b9e Binary files /dev/null and b/assets/images/versions/1.16.png differ diff --git a/assets/images/versions/1.17.png b/assets/images/versions/1.17.png new file mode 100644 index 000000000..ae5f81bea Binary files /dev/null and b/assets/images/versions/1.17.png differ diff --git a/assets/images/versions/1.18.png b/assets/images/versions/1.18.png new file mode 100644 index 000000000..14b16cb93 Binary files /dev/null and b/assets/images/versions/1.18.png differ diff --git a/assets/images/versions/1.19.png b/assets/images/versions/1.19.png new file mode 100644 index 000000000..55a53c709 Binary files /dev/null and b/assets/images/versions/1.19.png differ diff --git a/assets/images/versions/1.2.png b/assets/images/versions/1.2.png new file mode 100644 index 000000000..a15abef1b Binary files /dev/null and b/assets/images/versions/1.2.png differ diff --git a/assets/images/versions/1.3.png b/assets/images/versions/1.3.png new file mode 100644 index 000000000..0166eb59b Binary files /dev/null and b/assets/images/versions/1.3.png differ diff --git a/assets/images/versions/1.4.png b/assets/images/versions/1.4.png new file mode 100644 index 000000000..41ead935f Binary files /dev/null and b/assets/images/versions/1.4.png differ diff --git a/assets/images/versions/1.5.png b/assets/images/versions/1.5.png new file mode 100644 index 000000000..417b79b16 Binary files /dev/null and b/assets/images/versions/1.5.png differ diff --git a/assets/images/versions/1.6.png b/assets/images/versions/1.6.png new file mode 100644 index 000000000..2b89b7a03 Binary files /dev/null and b/assets/images/versions/1.6.png differ diff --git a/assets/images/versions/1.7.png b/assets/images/versions/1.7.png new file mode 100644 index 000000000..90a4e5ec3 Binary files /dev/null and b/assets/images/versions/1.7.png differ diff --git a/assets/images/versions/1.8.png b/assets/images/versions/1.8.png new file mode 100644 index 000000000..2ab3f52d7 Binary files /dev/null and b/assets/images/versions/1.8.png differ diff --git a/assets/images/versions/1.9.png b/assets/images/versions/1.9.png new file mode 100644 index 000000000..72fa0270d Binary files /dev/null and b/assets/images/versions/1.9.png differ diff --git a/assets/lang/en_us.json b/assets/lang/en_us.json index 310360fce..6bc409747 100644 --- a/assets/lang/en_us.json +++ b/assets/lang/en_us.json @@ -287,10 +287,6 @@ "account.delete.tooltip": "Delete account", "account.delete.content": "Are you sure you want to delete this account? (This action will not be restored)", "account.delete.notfound": "No new accounts yet", - "account.mojang.title": "Mojang Account", - "account.mojang.title.hint": "Email", - "account.mojang.passwd": "Mojang Password", - "account.mojang.passwd.hint": "Password", "account.passwd.show": "Show Password", "account.passwd.hide": "Hide Password", "account.error.empty": "Account or password cannot be empty", diff --git a/lib/account/microsoft_account_handler.dart b/lib/account/microsoft_account_handler.dart index 79f7bd89c..5fe68b7f6 100644 --- a/lib/account/microsoft_account_handler.dart +++ b/lib/account/microsoft_account_handler.dart @@ -4,13 +4,13 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:oauth2/oauth2.dart'; -import 'package:rpmlauncher/model/account/Account.dart'; +import 'package:rpmlauncher/model/account/account.dart'; import 'package:rpmlauncher/model/account/microsoft_entitlements.dart'; import 'package:rpmlauncher/util/data.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; +import 'package:rpmlauncher/util/rpml_http_client.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/on_close.dart'; import 'package:uuid/uuid.dart'; enum MicrosoftAccountStatus { @@ -91,11 +91,6 @@ class MSAccountHandler { M$ Register Application: https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app */ - static final RPMHttpClient _httpClient = RPMHttpClient( - baseOptions: BaseOptions( - contentType: ContentType.json.mimeType, - validateStatus: (status) => true)); - static Stream authorization( Credentials credentials) async* { try { @@ -156,7 +151,7 @@ class MSAccountHandler { final account = Account(AccountType.microsoft, mcAccessToken, profileJson['id'], profileJson['name'], credentials: credentials); - account.save(); + await account.save(); yield MicrosoftAccountStatus.successful; } else { @@ -173,7 +168,7 @@ class MSAccountHandler { Verify the microsoft account is able to play minecraft */ static Future validate(String accessToken) async { - final Response response = await _httpClient.get( + final Response response = await httpClient.get( 'https://api.minecraftservices.com/minecraft/profile', options: Options(headers: {'Authorization': 'Bearer $accessToken'})); @@ -181,7 +176,7 @@ class MSAccountHandler { } static Future _authorizationXBL(String accessToken) async { - final Response response = await _httpClient.post( + final Response response = await httpClient.post( 'https://user.auth.xboxlive.com/user/authenticate', data: json.encode({ 'Properties': { @@ -201,7 +196,7 @@ class MSAccountHandler { String xblToken, String userHash) async { //Authenticate with XSTS - Response response = await _httpClient.post( + Response response = await httpClient.post( 'https://xsts.auth.xboxlive.com/xsts/authorize', data: json.encode({ 'Properties': { @@ -223,7 +218,7 @@ class MSAccountHandler { String xstsToken, String userHash) async { //Authenticate with Minecraft - Response response = await _httpClient.post( + Response response = await httpClient.post( 'https://api.minecraftservices.com/launcher/login', data: json.encode({ 'xtoken': 'XBL3.0 x=$userHash;$xstsToken', @@ -245,7 +240,7 @@ class MSAccountHandler { static Future _checkingGameOwnership(String accessToken) async { //Checking Game Ownership - Response response = await _httpClient.get( + Response response = await httpClient.get( 'https://api.minecraftservices.com/entitlements/license?requestId=${const Uuid().v4()}', options: Options(headers: { 'Authorization': 'Bearer $accessToken', @@ -263,7 +258,7 @@ class MSAccountHandler { } static Future getProfile(mcAccessToken) async { - Response response = await _httpClient.get( + Response response = await httpClient.get( 'https://api.minecraftservices.com/minecraft/profile', options: Options( headers: {'Authorization': 'Bearer $mcAccessToken'}, diff --git a/lib/account/mojang_account_handler.dart b/lib/account/mojang_account_handler.dart deleted file mode 100644 index 87f2edef3..000000000 --- a/lib/account/mojang_account_handler.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:http/http.dart' as http; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -// ignore: depend_on_referenced_packages -import 'package:http_parser/http_parser.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; - -class MojangHandler { -/* -API Docs: https://wiki.vg/Authentication -*/ - - static Future logIn(String username, String password) async { - /* - The clientToken should be a randomly generated identifier and must be identical for each request. - The vanilla launcher generates a random (version 4) UUID on first run and saves it, reusing it for every subsequent request. - In case it is omitted the server will generate a random token based on Java's UUID.toString() which should then be stored by the client. - This will however also invalidate all previously acquired accessTokens for this user across all clients. - */ - - String url = '$mojangAuthAPI/authenticate'; - Response response = await RPMHttpClient().post(url, - data: { - 'agent': {'name': 'Minecraft', "version": 1}, - "username": username, - "password": password, - "requestUser": true - }, - options: Options( - contentType: 'application/json', - responseType: ResponseType.json, - headers: { - "Accept": "application/json", - }, - validateStatus: (state) => true)); - if (response.data.containsKey("error")) { - return response.data["error"]; - } else { - return response.data; - } - } - - static Future validate(String accessToken) async { - /* - Returns an empty payload (204 No Content) if successful, an error JSON with status 403 Forbidden otherwise. - */ - - String url = '$mojangAuthAPI/validate'; - Map map = { - "accessToken": accessToken, - }; - HttpClient httpClient = HttpClient(); - HttpClientRequest request = await httpClient.postUrl(Uri.parse(url)); - request.headers.add('Content-Type', 'application/json'); - request.headers.add('Accept', 'application/json'); - request.add(utf8.encode(json.encode(map))); - HttpClientResponse response = await request.close(); - int statusCode = response.statusCode; - httpClient.close(); - - return statusCode == 204; - } - - static Future refresh(accessToken) async { - /* - Refreshes a valid accessToken. It can be used to keep a user logged in between gaming sessions and is preferred over storing the user's password in a file (see lastlogin). - */ - - String url = '$mojangAuthAPI/validate'; - Map map = {"accessToken": accessToken, "requestUser": true}; - - Map body = - await jsonDecode((await RPMHttpClient().post(url, data: map)).data); - if (body.containsKey("error")) { - return body["error"]; - } - return body; - } - - static Future updateSkin( - String accessToken, File file, String variant) async { - variant = variant == I18n.format('account.skin.variant.classic') - ? 'classic' - : variant; - variant = - variant == I18n.format('account.skin.variant.slim') ? 'slim' : variant; - - String url = 'https://api.minecraftservices.com/minecraft/profile/skins'; - - http.MultipartRequest request = http.MultipartRequest('PUT', Uri.parse(url)) - ..fields['variant'] = variant - ..files.add(await http.MultipartFile.fromPath('file', file.absolute.path, - contentType: MediaType('image', 'png'))); - request.headers.addAll({'Authorization': "Bearer $accessToken"}); - http.StreamedResponse response = await request.send(); - - bool success = response.stream.bytesToString().toString().isNotEmpty; - if (!success) { - logger.error(ErrorType.network, response.reasonPhrase); - } - - return success; - } -} diff --git a/lib/api/mojang_meta_api.dart b/lib/api/mojang_meta_api.dart new file mode 100644 index 000000000..5e6860f89 --- /dev/null +++ b/lib/api/mojang_meta_api.dart @@ -0,0 +1,17 @@ +import 'package:rpmlauncher/model/game/version/mc_version_manifest.dart'; +import 'package:rpmlauncher/util/rpml_http_client.dart'; + +class MojangMetaAPI { + static const String _versionManifestUrl = + 'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json'; + + static Future getVersionManifest() async { + final response = await httpClient.get(_versionManifestUrl); + + if (response.statusCode == 200) { + return MCVersionManifest.fromJson(response.data); + } else { + throw Exception('Failed to load version manifest'); + } + } +} diff --git a/lib/config/config_helper.dart b/lib/config/config_helper.dart index 3be55e36d..a5dda8a3f 100644 --- a/lib/config/config_helper.dart +++ b/lib/config/config_helper.dart @@ -2,21 +2,19 @@ import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart'; -import 'package:rpmlauncher/util/data.dart'; +import 'package:rpmlauncher/config/json_storage.dart'; import 'package:rpmlauncher/util/launcher_path.dart'; -import 'package:rpmlauncher/util/logger.dart'; -class ConfigHelper { - /// Local copy of config. - static Map? _cachedConfig; +final ConfigHelper configHelper = ConfigHelper(); - /// The config file. - static final File _file = - File(join(LauncherPath.defaultDataHome.path, 'config.json')); +class ConfigHelper extends JsonStorage { + ConfigHelper() + : super(File(join(LauncherPath.defaultDataHome.path, 'config.json'))); - static Future init() async { - if (_file.existsSync()) { - final String stringMap = _file.readAsStringSync(); + @override + Future init() async { + if (file.existsSync()) { + final String stringMap = file.readAsStringSync(); if (stringMap.isNotEmpty) { final Object? data = json.decode(stringMap); if (data is Map && !data.containsKey('schema_version')) { @@ -24,53 +22,14 @@ class ConfigHelper { } } } else { - await set('schema_version', 1); + await setItem('schema_version', 1); } - await _readConfig(); - } - - /// Gets the config from the stored file. Once read, the config are - /// maintained in memory. - static Future> _readConfig() async { - if (_cachedConfig != null) { - return _cachedConfig!; - } - - Map config = {}; - if (_file.existsSync()) { - final String stringMap = _file.readAsStringSync(); - if (stringMap.isNotEmpty) { - final Object? data = json.decode(stringMap); - if (data is Map) { - config = data.cast(); - } - } - } - _cachedConfig = config; - - return config; - } - - /// Writes the cached config to disk. Returns [true] if the operation - /// succeeded. - static Future _writeConfig(Map config) async { - try { - if (!_file.existsSync()) { - _file.createSync(recursive: true); - } - final String stringMap = json.encode(config); - _file.writeAsStringSync(stringMap); - } catch (e, s) { - logger.error(ErrorType.config, 'Error saving config to disk: $e', - stackTrace: s); - return false; - } - return true; + await super.init(); } /// Migrate the old config to new config. - static Future _migrateOldConfig(Map config) async { + Future _migrateOldConfig(Map config) async { final Map newConfig = {}; handle(String oldKey, String newKey) { @@ -105,37 +64,6 @@ class ConfigHelper { handle('java_path_16', 'java_path_16'); handle('java_path_17', 'java_path_17'); - await _writeConfig(newConfig); - } - - /// Get the value of the key by the config file. - static T? get(String key) { - Object? value = _cachedConfig?[key]; - if (value is T) { - return value; - } else { - return null; - } - } - - /// Set the value of the key to the config file. - /// If the value is null, the key will be removed. - static Future set(String key, T? value) async { - final config = await _readConfig(); - - if (value == null) { - config.remove(key); - } else { - config[key] = value; - } - - _cachedConfig = config; - - await _writeConfig(config); - } - - /// Get all keys and values in the config file. - static Future> getAll() async { - return await _readConfig(); + await writeData(newConfig); } } diff --git a/lib/config/json_storage.dart b/lib/config/json_storage.dart new file mode 100644 index 000000000..74ea7ebff --- /dev/null +++ b/lib/config/json_storage.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:rpmlauncher/util/data.dart'; +import 'package:rpmlauncher/util/logger.dart'; +import 'package:synchronized/synchronized.dart'; + +class JsonStorage { + /// Local copy of data. + Map? _cachedData; + + // Use this lock to prevent concurrent access to data + final _lock = Lock(); + + /// The data file. + final File file; + + JsonStorage(this.file); + + Future init() async { + await readData(); + } + + /// Gets the data from the stored file. Once read, the data are + /// maintained in memory. + Future> readData() async { + Map data = {}; + + await _lock.synchronized(() async { + if (await file.exists()) { + final String stringMap = await file.readAsString(); + if (stringMap.isNotEmpty) { + final Object? data0 = json.decode(stringMap); + if (data0 is Map) { + data = data0.cast(); + } + } + } + _cachedData = data; + }); + + return data; + } + + /// Writes the cached data to disk. Returns [true] if the operation + /// succeeded. + Future writeData(Map data) async { + try { + await _lock.synchronized(() async { + if (!file.existsSync()) { + file.createSync(recursive: true); + } + final String stringMap = json.encode(data); + await file.writeAsString(stringMap); + }); + } catch (e, s) { + logger.error(ErrorType.io, 'Error saving the data to disk: $e ($file)', + stackTrace: s); + return false; + } + return true; + } + + /// Get the value of the key by the data file. + T? getItem(String key) { + Object? value = _cachedData?[key]; + if (value is T) { + return value; + } else { + return null; + } + } + + /// Set the value of the key to the data file. + /// If the value is null, the key will be removed. + Future setItem(String key, T? value) async { + final data = await readData(); + + if (value == null) { + data.remove(key); + } else { + data[key] = value; + } + + _cachedData = data; + + await writeData(data); + } + + operator []=(String key, dynamic value) => setItem(key, value); + operator [](String key) => getItem(key); + + /// Get all keys and values in the data file. + Future> getAll() async { + final data = await readData(); + _cachedData = data; + + return data; + } +} diff --git a/lib/config/launcher_config.dart b/lib/config/launcher_config.dart index 18d0422e4..53c7d75fb 100644 --- a/lib/config/launcher_config.dart +++ b/lib/config/launcher_config.dart @@ -5,50 +5,52 @@ import 'package:rpmlauncher/config/config.dart'; import 'package:rpmlauncher/config/interface_launcher_config.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/i18n/launcher_language.dart'; +import 'package:rpmlauncher/ui/theme/rpml_theme_type.dart'; import 'package:rpmlauncher/util/launcher_path.dart'; -import 'package:rpmlauncher/util/theme.dart'; import 'package:rpmlauncher/util/updater.dart'; class LauncherConfig implements ILauncherConfig { const LauncherConfig(); @override - int get schemaVersion => ConfigHelper.get('schema_version') ?? 1; + int get schemaVersion => configHelper.getItem('schema_version') ?? 1; @override set schemaVersion(int value) => - ConfigHelper.set('schema_version', value); + configHelper.setItem('schema_version', value); @override - bool get isInit => ConfigHelper.get('init') ?? false; + bool get isInit => configHelper.getItem('init') ?? false; @override - set isInit(bool value) => ConfigHelper.set('init', value); + set isInit(bool value) => configHelper.setItem('init', value); @override bool get autoInstallJava => - ConfigHelper.get('auto_install_java') ?? true; + configHelper.getItem('auto_install_java') ?? true; @override set autoInstallJava(bool value) => - ConfigHelper.set('auto_install_java', value); + configHelper.setItem('auto_install_java', value); @override - double get jvmMaxRam => ConfigHelper.get('jvm_max_ram') ?? 4096.0; + double get jvmMaxRam => configHelper.getItem('jvm_max_ram') ?? 4096.0; @override - set jvmMaxRam(double value) => ConfigHelper.set('jvm_max_ram', value); + set jvmMaxRam(double value) => + configHelper.setItem('jvm_max_ram', value); @override - List get jvmArgs => ConfigHelper.get>('jvm_args') ?? []; + List get jvmArgs => + configHelper.getItem>('jvm_args') ?? []; @override set jvmArgs(List value) => - ConfigHelper.set>('jvm_args', value); + configHelper.setItem>('jvm_args', value); @override LauncherLanguage get language { - final code = ConfigHelper.get('language'); + final code = configHelper.getItem('language'); return LauncherLanguage.values.firstWhere( (language) => language.code == code, @@ -58,63 +60,65 @@ class LauncherConfig implements ILauncherConfig { @override set language(LauncherLanguage value) => - ConfigHelper.set('language', value.code); + configHelper.setItem('language', value.code); @override bool get checkAssetsIntegrity => - ConfigHelper.get('check_assets_integrity') ?? true; + configHelper.getItem('check_assets_integrity') ?? true; @override set checkAssetsIntegrity(bool value) => - ConfigHelper.set('check_assets_integrity', value); + configHelper.setItem('check_assets_integrity', value); @override - int get gameWindowWidth => ConfigHelper.get('game_window_width') ?? 854; + int get gameWindowWidth => + configHelper.getItem('game_window_width') ?? 854; @override set gameWindowWidth(int value) => - ConfigHelper.set('game_window_width', value); + configHelper.setItem('game_window_width', value); @override int get gameWindowHeight => - ConfigHelper.get('game_window_height') ?? 480; + configHelper.getItem('game_window_height') ?? 480; @override set gameWindowHeight(int value) => - ConfigHelper.set('game_window_height', value); + configHelper.setItem('game_window_height', value); @override int get gameLogMaxLineCount => - ConfigHelper.get('game_log_max_line_count') ?? 300; + configHelper.getItem('game_log_max_line_count') ?? 300; @override set gameLogMaxLineCount(int value) => - ConfigHelper.set('game_log_max_line_count', value); + configHelper.setItem('game_log_max_line_count', value); @override - bool get showGameLogs => ConfigHelper.get('show_game_logs') ?? true; + bool get showGameLogs => configHelper.getItem('show_game_logs') ?? true; @override set showGameLogs(bool value) => - ConfigHelper.set('show_game_logs', value); + configHelper.setItem('show_game_logs', value); @override bool get autoCloseGameLogsScreen => - ConfigHelper.get('auto_close_game_logs_screen') ?? true; + configHelper.getItem('auto_close_game_logs_screen') ?? true; @override set autoCloseGameLogsScreen(bool value) => - ConfigHelper.set('auto_close_game_logs_screen', value); + configHelper.setItem('auto_close_game_logs_screen', value); @override bool get autoDownloadModDependencies => - ConfigHelper.get('auto_download_mod_dependencies') ?? true; + configHelper.getItem('auto_download_mod_dependencies') ?? true; @override set autoDownloadModDependencies(bool value) => - ConfigHelper.set('auto_download_mod_dependencies', value); + configHelper.setItem('auto_download_mod_dependencies', value); @override - int get themeId => ConfigHelper.get('theme_id') ?? ThemeUtil.getSystem(); + int get themeId => RPMLThemeType.dark.index; + // configHelper.getItem('theme_id') ?? LauncherTheme.getSystem(); @override - set themeId(int value) => ConfigHelper.set('theme_id', value); + set themeId(int value) => configHelper.setItem('theme_id', value); @override VersionTypes get updateChannel { - final channel = ConfigHelper.get('update_channel'); + final channel = configHelper.getItem('update_channel'); return VersionTypes.values.firstWhere( (type) => type.name == channel, @@ -124,67 +128,67 @@ class LauncherConfig implements ILauncherConfig { @override set updateChannel(VersionTypes value) => - ConfigHelper.set('update_channel', value.name); + configHelper.setItem('update_channel', value.name); @override Directory get launcherDataDir { - final path = ConfigHelper.get('launcher_data_dir'); + final path = configHelper.getItem('launcher_data_dir'); return path == null ? LauncherPath.defaultDataHome : Directory(path); } @override set launcherDataDir(Directory value) => - ConfigHelper.set('launcher_data_dir', value.absolute.path); + configHelper.setItem('launcher_data_dir', value.absolute.path); @override String get googleAnalyticsClientId => - ConfigHelper.get('google_analytics_client_id') ?? + configHelper.getItem('google_analytics_client_id') ?? '${Random().nextInt(0x7FFFFFFF)}.${DateTime.now().millisecondsSinceEpoch / 1000}'; @override set googleAnalyticsClientId(String value) => - ConfigHelper.set('google_analytics_client_id', value); + configHelper.setItem('google_analytics_client_id', value); @override bool get autoFullScreen => - ConfigHelper.get('auto_full_screen') ?? false; + configHelper.getItem('auto_full_screen') ?? false; @override set autoFullScreen(bool value) => - ConfigHelper.set('auto_full_screen', value); + configHelper.setItem('auto_full_screen', value); @override bool get checkAccountValidity => - ConfigHelper.get('check_account_validity') ?? true; + configHelper.getItem('check_account_validity') ?? true; @override set checkAccountValidity(bool value) => - ConfigHelper.set('check_account_validity', value); + configHelper.setItem('check_account_validity', value); @override - String? get wrapperCommand => ConfigHelper.get('wrapper_command'); + String? get wrapperCommand => configHelper.getItem('wrapper_command'); @override set wrapperCommand(String? value) => - ConfigHelper.set('wrapper_command', value); + configHelper.setItem('wrapper_command', value); @override bool get discordRichPresence => - ConfigHelper.get('enable_discord_rpc') ?? true; + configHelper.getItem('enable_discord_rpc') ?? true; @override set discordRichPresence(bool value) => - ConfigHelper.set('enable_discord_rpc', value); + configHelper.setItem('enable_discord_rpc', value); @override File? get backgroundImageFile { - final path = ConfigHelper.get('background_image_file'); + final path = configHelper.getItem('background_image_file'); return (path == null || path.isEmpty) ? null : File(path); } @override - set backgroundImageFile(File? value) => - ConfigHelper.set('background_image_file', value?.absolute.path); + set backgroundImageFile(File? value) => configHelper.setItem( + 'background_image_file', value?.absolute.path); } diff --git a/lib/database/database.dart b/lib/database/database.dart index ad2dea6a0..57d0a56f3 100644 --- a/lib/database/database.dart +++ b/lib/database/database.dart @@ -1,24 +1,15 @@ import 'package:hive/hive.dart'; -import 'package:rpmlauncher/database/data_box.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/mod_info.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; class Database { static Database? _instance; - static Database get instance => _instance!; - final DataBox modInfoBox; + static Database get instance => _instance!; - const Database._({required this.modInfoBox}); + const Database._(); static Future init() async { - Hive.init(GameRepository.getDatabaseDir().path); - Hive.registerAdapter(ModInfoAdapter()); - Hive.registerAdapter(ConflictModAdapter()); - Hive.registerAdapter(ModLoaderAdapter()); - - _instance = Database._( - modInfoBox: await DataBox.open('mod_info_index')); + Hive.init(GameRepository.getDatabaseDirectory().path); + _instance = const Database._(); } } diff --git a/lib/function/analytics.dart b/lib/function/analytics.dart index c9b781f74..4acefa99d 100644 --- a/lib/function/analytics.dart +++ b/lib/function/analytics.dart @@ -4,17 +4,17 @@ import 'package:dio/dio.dart'; import 'package:flutter/widgets.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; +import 'package:rpmlauncher/util/rpml_http_client.dart'; class Analytics { String trackingId = "G-T5LGYPGM5V"; - late RPMHttpClient dio; + late RPMLHttpClient dio; late String clientID; Analytics() { clientID = launcherConfig.googleAnalyticsClientId; - dio = RPMHttpClient(); + dio = RPMLHttpClient(); } Future ping({Duration? timeout}) async { @@ -71,7 +71,7 @@ class Analytics { options: Options( contentType: Headers.textPlainContentType, headers: {"User-Agent": getUserAgent()})); - } catch (e) {} + } catch (_) {} } String formatData(String event, Map? params) { diff --git a/lib/function/counter.dart b/lib/function/counter.dart deleted file mode 100644 index dc6579bbd..000000000 --- a/lib/function/counter.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; -import 'package:provider/provider.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/launcher_path.dart'; - -class Counter { - final Directory dataHome; - final Directory defaultDataHome; - final Logger logger; - final bool testMode; - - const Counter( - this.dataHome, this.defaultDataHome, this.logger, this.testMode); - - static Counter of(BuildContext context) { - return Provider.of(context); - } - - factory Counter.create() { - return Counter(LauncherPath.currentDataHome, LauncherPath.defaultDataHome, - Logger.current, kTestMode); - } -} diff --git a/lib/handler/game_version_handler.dart b/lib/handler/game_version_handler.dart new file mode 100644 index 000000000..d86f1e0f2 --- /dev/null +++ b/lib/handler/game_version_handler.dart @@ -0,0 +1,142 @@ +import 'package:pub_semver/pub_semver.dart'; + +class GameVersionHandler { + static Version parse(String id) { + try { + try { + return _parse(id); + } catch (e) { + /// Handling snapshot version (e.g. 21w44a) + final snapshotPattern = RegExp(r'(?:(?\d\d)w(?\d\d)[a-z])'); + if (snapshotPattern.hasMatch(id)) { + return _parseSnapshot(id, snapshotPattern); + } + + final preVersion = _parsePreVersion(id); + if (preVersion != null) { + return _parse(preVersion); + } + + rethrow; + } + } catch (e) { + throw Exception('Invalid game version: $id ($e)'); + } + } + + static Version _parse(String id) { + // Example: 1.19, 1.18 or 1.17 etc. + final patchIsEmpty = RegExp(r'^\d+\.\d+$').hasMatch(id); + if (patchIsEmpty) { + return Version.parse('$id.0'); + } + + return Version.parse(id); + } + + static Version _parseSnapshot(String id, RegExp snapshotPattern) { + /// Handling snapshot version (e.g. 21w44a) + + final match = snapshotPattern.allMatches(id).toList().first; + + String toReleaseVer(int year, int week) { + if (year == 22 && week >= 46 || year >= 23) { + return '1.19.3'; + } else if (year == 22 && week == 24) { + return '1.19.1'; + } else if (year == 22 && week >= 11 && week <= 19) { + return '1.19'; + } else if (year == 22 && week >= 3 && week <= 7) { + return '1.18.2'; + } else if (year == 21 && week >= 37) { + return '1.18'; + } else if (year == 21 && (week >= 3 && week <= 20)) { + return '1.17'; + } else if (year == 20 && week >= 6) { + return '1.16'; + } else if (year == 19 && week >= 34) { + return '1.15.2'; + } else if (year == 18 && week >= 43 || year == 19 && week <= 14) { + return '1.14'; + } else if (year == 18 && week >= 30 && week <= 33) { + return '1.13.1'; + } else if (year == 17 && week >= 43 || year == 18 && week <= 22) { + return '1.13'; + } else if (year == 17 && week == 31) { + return '1.12.1'; + } else if (year == 17 && week >= 6 && week <= 18) { + return '1.12'; + } else if (year == 16 && week == 50) { + return '1.11.1'; + } else if (year == 16 && week >= 32 && week <= 44) { + return '1.11'; + } else if (year == 16 && week >= 20 && week <= 21) { + return '1.10'; + } else if (year == 16 && week >= 14 && week <= 15) { + return '1.9.3'; + } else if (year == 15 && week >= 31 || year == 16 && week <= 7) { + return '1.9'; + } else if (year == 14 && week >= 2 && week <= 34) { + return '1.8'; + } else if (year == 13 && week >= 47 && week <= 49) { + return '1.7.4'; + } else if (year == 13 && week >= 36 && week <= 43) { + return '1.7.2'; + } else if (year == 13 && week >= 16 && week <= 26) { + return '1.6'; + } else if (year == 13 && week >= 11 && week <= 12) { + return '1.5.1'; + } else if (year == 13 && week >= 1 && week <= 10) { + return '1.5'; + } else if (year == 12 && week >= 49 && week <= 50) { + return '1.4.6'; + } else if (year == 12 && week >= 32 && week <= 42) { + return '1.4.2'; + } else if (year == 12 && week >= 15 && week <= 30) { + return '1.3.1'; + } else if (year == 12 && week >= 3 && week <= 8) { + return '1.2.1'; + } else if (year == 11 && week >= 47 || year == 12 && week <= 1) { + return '1.1'; + } else { + return '1.19'; + } + } + + int year = int.parse(match.group(1).toString()); //ex: 23 (year 2023) + int week = int.parse(match.group(2).toString()); //ex: 44 (week 44) + + return _parse(toReleaseVer(year, week)); + } + + static String? _parsePreVersion(String id) { + String result = id; + + int pos = result.indexOf('-pre'); + if (pos >= 0) result = result.substring(0, pos); + + pos = result.indexOf(' Pre-release '); + if (pos >= 0) result = result.substring(0, pos); + + pos = result.indexOf(' Pre-Release '); + if (pos >= 0) result = result.substring(0, pos); + + pos = result.indexOf(' Release Candidate '); + if (pos >= 0) result = result.substring(0, pos); + + pos = result.indexOf(RegExp(r'-rc\d+$')); + if (pos >= 0) result = result.substring(0, pos); + + if (result.startsWith(RegExp(r'[a-z]'))) result = result.substring(1); + if (result.endsWith('a') || result.endsWith('b')) { + result = result.substring(0, result.length - 1); + } + + // Remove 1.5_01, 1.5_02, etc. + pos = result.indexOf('_'); + if (pos >= 0) result = result.substring(0, pos); + + if (result != id) return result; + return null; + } +} diff --git a/lib/handler/window_handler.dart b/lib/handler/window_handler.dart index 8ce037798..e13cbaa10 100644 --- a/lib/handler/window_handler.dart +++ b/lib/handler/window_handler.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:rpmlauncher/database/data_box.dart'; +import 'package:rpmlauncher/ui/pages/home_page.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:window_size/window_size.dart'; import 'package:window_manager/window_manager.dart'; @@ -37,7 +38,7 @@ class WindowHandler { String? route = arguments['route']; String? title = arguments['title']; - LauncherInfo.route = route ?? "/"; + LauncherInfo.route = route ?? HomePage.route; id = windowID; if (title != null) { await windowManager.setTitle(title); diff --git a/lib/i18n/language_selector.dart b/lib/i18n/language_selector.dart index 3c44296c9..44bee8afe 100644 --- a/lib/i18n/language_selector.dart +++ b/lib/i18n/language_selector.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:rpmlauncher/config/config.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/i18n/launcher_language.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; +import 'package:rpmlauncher/ui/view/row_scroll_view.dart'; class LanguageSelectorWidget extends StatefulWidget { final Function()? onChanged; diff --git a/lib/launcher/Arguments.dart b/lib/launcher/Arguments.dart deleted file mode 100644 index c7a648ef5..000000000 --- a/lib/launcher/Arguments.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/util/util.dart'; - -class Arguments { - static List getVanilla( - Map args, Map variable, Version comparableVersion) { - List args_ = []; - - if (args["jvm"] != null) { - for (var jvmI in args["jvm"]) { - if (jvmI is Map) { - for (var rulesI in jvmI["rules"]) { - List value = []; - if (jvmI["value"] is List) { - value = jvmI["value"].cast(); - } else if (jvmI["value"] is String) { - value = [jvmI["value"]]; - } - - if (rulesI["os"]["name"] == Util.getMinecraftFormatOS()) { - args_.addAll(value); - } else if (rulesI["os"].containsKey("version")) { - if (rulesI["os"]["version"] == Util.getMinecraftFormatOS()) { - args_.addAll(value); - } - } - } - } else { - if (variable.containsKey(jvmI)) { - args_.add(variable[jvmI]!); - } else if (jvmI is String) { - List key = variable.keys - .where( - (element) => jvmI.contains(element), - ) - .toList(); - - if (key.isNotEmpty) { - String arg = jvmI; - key.forEach((key) => arg = arg.replaceAll(key, variable[key]!)); - args_.add(arg); - } else { - args_.add(jvmI); - } - } - } - } - } - args_.add(args["mainClass"]); - if (args["game"] != null) { - for (var gameI in args["game"]) { - if (variable.containsKey(gameI)) { - args_.add(variable[gameI] ?? ""); - } else if (gameI is String && - (gameI.startsWith("--") || !gameI.contains("{"))) { - args_.add(gameI); - } - } - } - return args_; - } - - static List getForge( - Map args, Map variable, Version comparableVersion) { - List args_ = []; - args_.addAll(getVanilla(args, variable, comparableVersion)); - args_.add(args["mainClass"]); - return args_; - } - - Map getArgsString(String versionID, MinecraftMeta meta) { - Map args_ = {}; - Version version = Util.parseMCComparableVersion(versionID); - if (version >= Version(1, 13, 0)) { - args_.addAll(meta['arguments']); - } else { - args_["game"] = meta['minecraftArguments'].toString().split(" "); - } - args_["mainClass"] = meta["mainClass"]; - - if (meta.containsKey('logging')) { - args_["logging"] = meta['logging']; - } - return args_; - } -} diff --git a/lib/launcher/CheckData.dart b/lib/launcher/CheckData.dart deleted file mode 100644 index e5a580cb6..000000000 --- a/lib/launcher/CheckData.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'dart:io'; - -import 'package:crypto/crypto.dart'; - -class CheckData { - static bool checkSha1Sync(FileSystemEntity file, String sha1Hash) { - if (sha1.convert(File(file.path).readAsBytesSync()).toString() == - sha1Hash.toString()) { - return true; - } else { - return false; - } - } - - static Future checkSha1(FileSystemEntity file, String sha1Hash) async { - if (sha1.convert(await File(file.path).readAsBytes()).toString() == - sha1Hash.toString()) { - return true; - } else { - return false; - } - } -} diff --git a/lib/launcher/Fabric/FabricAPI.dart b/lib/launcher/Fabric/FabricAPI.dart deleted file mode 100644 index c3c846ec2..000000000 --- a/lib/launcher/Fabric/FabricAPI.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/model/Game/FabricInstallerVersion.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; - -class FabricAPI { - static Future getLoaderVersions(versionID) async { - Response response = - await RPMHttpClient().get("$fabricApi/versions/loader/$versionID"); - return RPMHttpClient.json(response.data); - } - - static Future getProfileJson(versionID, loaderVersion) async { - Response response = await RPMHttpClient().get( - "$fabricApi/versions/loader/$versionID/$loaderVersion/profile/json"); - return RPMHttpClient.json(response.data); - } - - static Future getInstallerVersion() async { - Response response = - await RPMHttpClient().get("$fabricApi/versions/installer"); - return FabricInstallerVersions.fromList( - RPMHttpClient.json(response.data).cast>()); - } -} diff --git a/lib/launcher/Fabric/FabricClient.dart b/lib/launcher/Fabric/FabricClient.dart deleted file mode 100644 index 56662bd2b..000000000 --- a/lib/launcher/Fabric/FabricClient.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/launcher/Fabric/FabricAPI.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:path/path.dart'; - -import '../MinecraftClient.dart'; - -class FabricClient extends MinecraftClient { - Map fabricMeta; - String loaderVersion; - - @override - MinecraftClientHandler handler; - - FabricClient._init({ - required this.fabricMeta, - required this.handler, - required this.loaderVersion, - }); - - static Future createClient( - {required MinecraftMeta meta, - required String versionID, - required StateSetter setState, - required String loaderVersion, - required Instance instance}) async { - setState(() { - installingState.nowEvent = - I18n.format('version.list.downloading.fabric.info'); - }); - return await FabricClient._init( - handler: MinecraftClientHandler( - versionID: versionID, - meta: meta, - setState: setState, - instance: instance), - fabricMeta: - await FabricAPI.getProfileJson(versionID, loaderVersion), - loaderVersion: loaderVersion) - ._ready(); - } - - Future getFabricLibrary() async { - /* PackageName example: (abc.ab.com) - name: PackageName:JarName:JarVersion - url: https://maven.fabricmc.net - */ - - await Future.forEach(fabricMeta["libraries"].cast(), - (Map libMap) async { - Map result = Util.parseLibMaven(libMap); - Libraries lib = instance.config.libraries; - - lib.add(Library( - name: libMap["name"], - downloads: LibraryDownloads( - artifact: Artifact( - url: result["Url"], - sha1: (await RPMHttpClient().get(result["Url"] + ".sha1")) - .data - .toString(), - path: result["Path"], - )))); - - instance.config.libraries = lib; - - List paths = [GameRepository.getLibraryGlobalDir().path]; - paths.addAll(split(result["Path"])); - - installingState.downloadInfos.add(DownloadInfo(result["Url"], - savePath: join( - joinAll(paths), - ), - description: I18n.format('version.list.downloading.fabric.library'))); - }); - return this; - } - - Future getFabricArgs() async { - File vanillaArgsFile = GameRepository.getArgsFile( - versionID, ModLoader.vanilla, MinecraftSide.client); - File fabricArgsFile = GameRepository.getArgsFile( - versionID, ModLoader.fabric, MinecraftSide.client, - loaderVersion: loaderVersion); - Map argsObject = await json.decode(vanillaArgsFile.readAsStringSync()); - argsObject["mainClass"] = fabricMeta["mainClass"]; - fabricArgsFile - ..createSync(recursive: true) - ..writeAsStringSync(json.encode(argsObject)); - } - - Future _ready() async { - await handler.install(); - setState(() { - installingState.nowEvent = - I18n.format('version.list.downloading.fabric.args'); - }); - await getFabricArgs(); - await getFabricLibrary(); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - setState(() {}); - }); - return this; - } -} diff --git a/lib/launcher/Fabric/FabricServer.dart b/lib/launcher/Fabric/FabricServer.dart deleted file mode 100644 index 7d57bc7f9..000000000 --- a/lib/launcher/Fabric/FabricServer.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/launcher/Fabric/FabricAPI.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/MinecraftServer.dart'; -import 'package:rpmlauncher/model/Game/FabricInstallerVersion.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/launcher/Arguments.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; - -class FabricServer extends MinecraftServer { - @override - MinecraftServerHandler handler; - - final String loaderVersion; - - FabricServer._init({required this.handler, required this.loaderVersion}); - - static Future createServer( - {required MinecraftMeta meta, - required String versionID, - required Instance instance, - required String loaderVersion, - required StateSetter setState}) async { - return await FabricServer._init( - handler: MinecraftServerHandler( - meta: meta, - versionID: versionID, - instance: instance, - setState: setState), - loaderVersion: loaderVersion, - )._ready(); - } - - late String _serverJarPath; - - Future serverJar() async { - FabricInstallerVersions versions = await FabricAPI.getInstallerVersion(); - - String installerVersion = versions - .firstWhere((e) => e.stable, orElse: () => versions.first) - .version; - String downloadUrl = - "$fabricApi/versions/loader/$versionID/$loaderVersion/$installerVersion/server/jar"; - String jar = "$versionID-$loaderVersion-$installerVersion.jar"; - _serverJarPath = join(GameRepository.getLibraryGlobalDir().path, "net", - "fabricmc", "installer", "server", jar); - - Libraries libraries = instance.config.libraries; - libraries.add(Library( - name: - "net.fabricmc::installer:server:$versionID-$loaderVersion-$installerVersion", - downloads: LibraryDownloads( - artifact: Artifact( - url: downloadUrl, - path: "net/fabricmc/installer/server/$jar", - )))); - instance.config.libraries = libraries; - - installingState.downloadInfos.add(DownloadInfo(downloadUrl, - savePath: _serverJarPath, - description: I18n.format('version.list.downloading.main'))); - } - - Future getArgs() async { - File serverJar = File(_serverJarPath); - - File argsFile = GameRepository.getArgsFile( - versionID, ModLoader.fabric, MinecraftSide.server, - loaderVersion: loaderVersion); - await argsFile.create(recursive: true); - Map argsMap = Arguments().getArgsString(versionID, meta); - String? mainClass = Util.getJarMainClass(serverJar); - argsMap['mainClass'] = mainClass ?? "net.fabricmc.installer.ServerLauncher"; - await argsFile.writeAsString(json.encode(argsMap)); - } - - Future _ready() async { - await serverJar(); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - try { - setState(() {}); - } catch (e) {} - }); - setState(() { - installingState.nowEvent = I18n.format('version.list.downloading.args'); - }); - await getArgs(); - - return this; - } -} diff --git a/lib/launcher/Forge/ForgeAPI.dart b/lib/launcher/Forge/ForgeAPI.dart deleted file mode 100644 index 26ac6c585..000000000 --- a/lib/launcher/Forge/ForgeAPI.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeInstallProfile.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:archive/archive.dart'; -import 'package:http/http.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/util/util.dart'; - -class ForgeAPI { - static Future getAllLoaderVersion(versionID) async { - final url = Uri.parse("$forgeFilesMainAPI/maven-metadata.json"); - Response response = await get(url); - Map body = json.decode(response.body.toString()); - return body[versionID].reversed.toList(); - } - - // net/minecraftforge/forge/maven-metadata.json - - static String getGameLoaderVersion(versionID, forgeVersionID) { - return "$versionID-forge-$forgeVersionID"; - } - - static Future getProfile( - versionID, Archive archive) async { - Map? profileJson; - Map? versionJson; - - for (final file in archive) { - if (file.isFile) { - if (file.name == "install_profile.json") { - final data = file.content as List; - profileJson = json - .decode(const Utf8Decoder(allowMalformed: true).convert(data)) - .cast(); - } else if (file.name == "version.json") { - final data = file.content as List; - versionJson = json - .decode(const Utf8Decoder(allowMalformed: true).convert(data)); - } - } - } - - if (profileJson == null) return null; - - ForgeInstallProfile? profile; - if (versionJson == null && profileJson['install'] != null) { - /// Forge 14.23.5.2840 版本以前的格式 - profile = ForgeInstallProfile.fromOldJson(profileJson); - } else if (versionJson != null) { - profile = ForgeInstallProfile.fromNewJson(profileJson, - versionJson: versionJson); - } else { - return null; - } - - File profileJsonFile = GameRepository.getForgeProfileFile(versionID); - await profileJsonFile.create(recursive: true); - await profileJsonFile.writeAsString(json.encode(profile.toJson())); - return profile; - } - - static Future getForgeJar( - versionID, Archive archive, ForgeInstallProfile installProfile) async { - for (final file in archive) { - if (file.isFile) { - if (file.toString().startsWith("maven/net/minecraftforge/forge/")) { - final data = file.content as List; - File jarFile = File(join( - GameRepository.getLibraryGlobalDir().absolute.path, - file.name.split("maven/").join(""))); - jarFile.createSync(recursive: true); - jarFile.writeAsBytesSync(data); - } else if (installProfile.filePath != null && - file.toString() == installProfile.filePath) { - final data = file.content as List; - - List path = [GameRepository.getLibraryGlobalDir().path]; - path.addAll(split( - "net/minecraftforge/forge/${installProfile.version}/${installProfile.version}.jar")); - - File jarFile = File(joinAll(path)); - jarFile.createSync(recursive: true); - jarFile.writeAsBytesSync(data); - } - } - } - } - - static List parseMaven(String mavenString) { - /* - 原始內容: de.oceanlabs.mcp:mcp_config:1.16.5-20210115.111550@zip - 轉換後內容: https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_config/1.16.5-20210115.110354/mcp_config-1.16.5-20210115.110354.zip - - . -> / (套件包名稱) - : -> / - 第一個 : 後面代表套件名稱,第二個 : 後面代表版本號 - @ -> . (副檔名) - 檔案名稱組合方式: 套件名稱-套件版本號/.副檔名 (例如: mcp_config-1.16.5-20210115.110354.zip) - */ - - /// 是否為方括號,例如這種格式: [de.oceanlabs.mcp:mcp_config:1.16.5-20210115.111550@zip] - if (Util.isSurrounded(mavenString, "[", "]")) { - mavenString = - mavenString.split("[").join("").split("]").join(""); //去除方括號,方便解析 - } - - /// 以下範例的原始字串為 de.oceanlabs.mcp:mcp_config:1.16.5-20210115.111550@zip 的格式 - /// 結果: de/oceanlabs/mcp - String packageGroup = mavenString.split(":")[0].replaceAll(".", "/"); - - /// 結果: mcp_config - String packageName = mavenString.split(":")[1]; - - /// 結果: 1.16.5-20210115.111550 - String packageVersion = mavenString.split(":")[2].split("@")[0]; - - /// 結果: zip - String packageExtension = mavenString.split("@")[1]; - - return [ - "$packageGroup/$packageName/$packageVersion", - "$packageName-$packageVersion.$packageExtension" - ]; - } - - static File getLibFile(List libraries, String libraryName) { - Artifact artifact = libraries - .firstWhere( - (lib) => lib.name == libraryName && lib.downloads.artifact != null) - .downloads - .artifact!; - - return artifact.localFile; - } - - static handlingArgs(Map forgeMeta, String versionID, String forgeVersionID) { - File argsFile = GameRepository.getArgsFile( - versionID, ModLoader.vanilla, MinecraftSide.client); - File forgeArgsFile = GameRepository.getArgsFile( - versionID, ModLoader.forge, MinecraftSide.client, - loaderVersion: forgeVersionID); - Map argsObject = {}; - - if (argsObject['game'] == null) { - argsObject['game'] = []; - } - - Version version = Util.parseMCComparableVersion(versionID); - - if (version >= Version(1, 13, 0)) { - argsObject.addAll(json.decode(argsFile.readAsStringSync())); - - if (forgeMeta["arguments"] != null) { - if (forgeMeta["arguments"]["game"] != null) { - for (var i in forgeMeta["arguments"]["game"]) { - argsObject["game"].add(i); - } - } - if (forgeMeta["arguments"]["jvm"] != null) { - for (var i in forgeMeta["arguments"]["jvm"]) { - argsObject["jvm"].add(i); - } - } - } - } else { - /// Forge 1.12.2 - List minecraftArguments = - forgeMeta['minecraftArguments'].toString().split(' '); - for (var i in minecraftArguments) { - (argsObject["game"] as List).add(i); - } - } - - argsObject["mainClass"] = forgeMeta["mainClass"]; - - forgeArgsFile - ..createSync(recursive: true) - ..writeAsStringSync(json.encode(argsObject)); - } -} diff --git a/lib/launcher/Forge/ForgeClient.dart b/lib/launcher/Forge/ForgeClient.dart deleted file mode 100644 index 7e0f0f826..000000000 --- a/lib/launcher/Forge/ForgeClient.dart +++ /dev/null @@ -1,209 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:flutter/material.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeAPI.dart'; -import 'package:archive/archive.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/widget/dialog/UnSupportedForgeVersion.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; - -import '../apis.dart'; -import '../MinecraftClient.dart'; -import 'ForgeInstallProfile.dart'; -import 'Processors.dart'; - -enum ForgeClientState { successful, unknownProfile, unSupportedVersion } - -extension ForgeClientStateExtension on ForgeClientState { - Future handlerState( - BuildContext context, StateSetter setState, Instance instance, - {bool notFinal = false, - Future Function(Instance)? onSuccessful}) async { - switch (this) { - case ForgeClientState.successful: - if (!notFinal) { - installingState.nowEvent = - I18n.format('version.list.downloading.handling'); - setState(() {}); - await onSuccessful?.call(instance); - installingState.finish = true; - setState(() {}); - } - break; - case ForgeClientState.unknownProfile: - navigator.pushNamed(HomePage.route); - await Future.delayed(Duration.zero, () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: I18nText.errorInfoText(), - content: - I18nText("version.list.downloading.forge.profile.error"), - actions: const [OkClose()])); - }); - break; - case ForgeClientState.unSupportedVersion: - navigator.pushNamed(HomePage.route); - await Future.delayed(Duration.zero, () { - showDialog( - context: context, - builder: (context) => UnSupportedForgeVersion( - gameVersion: instance.config.version)); - }); - break; - } - } -} - -class ForgeClient extends MinecraftClient { - @override - MinecraftClientHandler handler; - - String forgeVersionID; - - ForgeClient._init({required this.handler, required this.forgeVersionID}); - - static Future createClient( - {required MinecraftMeta meta, - required String gameVersionID, - required StateSetter setState, - required String forgeVersionID, - required Instance instance}) async { - return await ForgeClient._init( - handler: MinecraftClientHandler( - versionID: gameVersionID, - setState: setState, - instance: instance, - meta: meta, - ), - forgeVersionID: forgeVersionID, - )._install(); - } - - Future installerJarHandler( - String forgeVersionID) async { - String loaderVersion = - ForgeAPI.getGameLoaderVersion(versionID, forgeVersionID); - File installerFile = File(join(dataHome.absolute.path, "temp", - "forge-installer", loaderVersion, "$loaderVersion-installer.jar")); - ForgeInstallProfile? installProfile; - try { - final archive = ZipDecoder().decodeBytes(installerFile.readAsBytesSync()); - - installProfile = await ForgeAPI.getProfile(versionID, archive); - - if (installProfile == null) return null; - - /// Minecraft Forge 1.17.1+ no need to extract FML from jar - if (instance.config.comparableVersion < Version(1, 17, 1)) { - await ForgeAPI.getForgeJar(versionID, archive, installProfile); - } - } on FormatException { - } on FileSystemException {} - - return installProfile; - } - - Future getForgeLibrary(forgeMeta) async { - Libraries forgeLibraries = Libraries.fromList(forgeMeta["libraries"]); - Libraries libraries = instance.config.libraries; - - libraries.addAll(forgeLibraries); - - instance.config.libraries = libraries; - forgeLibraries.forEach((lib) async { - Artifact? artifact = lib.downloads.artifact; - if (artifact != null) { - if (artifact.url == "") return; - - installingState.downloadInfos.add(DownloadInfo(artifact.url, - savePath: artifact.localFile.path, - hashCheck: true, - sh1Hash: artifact.sha1, - description: - I18n.format('version.list.downloading.forge.library'))); - } - }); - return this; - } - - Future getForgeInstaller(String forgeVersionID) async { - String loaderVersion = - ForgeAPI.getGameLoaderVersion(versionID, forgeVersionID); - - final String url = - "$forgeMavenMainUrl/${loaderVersion.split("forge-").join("")}/forge-${loaderVersion.split("forge-").join("")}-installer.jar"; - installingState.downloadInfos.add(DownloadInfo(url, - savePath: join(dataHome.absolute.path, "temp", "forge-installer", - loaderVersion, "$loaderVersion-installer.jar"), - description: I18n.format('version.list.downloading.forge.installer'))); - return this; - } - - Future runForgeProcessors( - ForgeInstallProfile profile, InstanceConfig instanceConfig) async { - await Future.forEach(profile.processors.processors, - (Processor processor) async { - await processor.execution( - instanceConfig, - profile.libraries, - ForgeAPI.getGameLoaderVersion(versionID, forgeVersionID), - versionID, - profile.data); - }); - return this; - } - - Future _install() async { - if (instance.config.comparableVersion < Version(1, 7, 0)) { - return ForgeClientState.unSupportedVersion; - } - installingState.downloadInfos = DownloadInfos.empty(); - await getForgeInstaller(forgeVersionID); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - setState(() {}); - }); - setState(() { - installingState.nowEvent = - I18n.format('version.list.downloading.forge.profile'); - }); - ForgeInstallProfile? installProfile = - await installerJarHandler(forgeVersionID); - - if (installProfile == null) { - return ForgeClientState.unknownProfile; - } - - Map forgeMeta = installProfile.versionJson; - await handler.install(); - setState(() { - installingState.nowEvent = - I18n.format('version.list.downloading.forge.args'); - }); - await ForgeAPI.handlingArgs(forgeMeta, versionID, forgeVersionID); - await getForgeLibrary(forgeMeta); - await installProfile.getInstallerLib(handler); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - setState(() {}); - }); - setState(() { - installingState.nowEvent = - I18n.format('version.list.downloading.forge.processors.run'); - }); - await runForgeProcessors(installProfile, instance.config); - - return ForgeClientState.successful; - } -} diff --git a/lib/launcher/Forge/ForgeData.dart b/lib/launcher/Forge/ForgeData.dart deleted file mode 100644 index 21d0c5e6a..000000000 --- a/lib/launcher/Forge/ForgeData.dart +++ /dev/null @@ -1,36 +0,0 @@ -class ForgeDataList { - final List forgeDataList; - final List forgeDataKeys; - - const ForgeDataList( - {required this.forgeDataList, required this.forgeDataKeys}); - - factory ForgeDataList.fromJson(Map json) { - List list = json.values.toList(); - List forgeDataList_ = []; - list.forEach((data) { - forgeDataList_.add(ForgeData.fromJson(data)); - }); - return ForgeDataList( - forgeDataList: forgeDataList_, forgeDataKeys: json.keys.toList()); - } - - List toList() => forgeDataList; - - int getIndex(String dataName) => forgeDataKeys.indexOf(dataName); -} - -class ForgeData { - final String client; - final String server; - - const ForgeData({ - required this.client, - required this.server, - }); - - factory ForgeData.fromJson(Map json) => - ForgeData(client: json['client'], server: json['server']); - - Map toJson() => {'client': client, 'server': server}; -} diff --git a/lib/launcher/Forge/ForgeInstallProfile.dart b/lib/launcher/Forge/ForgeInstallProfile.dart deleted file mode 100644 index b6ed61190..000000000 --- a/lib/launcher/Forge/ForgeInstallProfile.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'dart:convert'; - -import 'package:rpmlauncher/launcher/Forge/ForgeOldProfile.dart' as old_profile; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; - -import '../MinecraftClient.dart'; -import 'ForgeData.dart'; -import 'Processors.dart'; - -class ForgeInstallProfile { - final int? spec; - final String version; - final Map versionJson; - final String? path; //1.17.1 版本的Forge Path 會是 null - final String? filePath; - final String minecraft; - final String? jsonPath; - final ForgeDataList data; - final Processors processors; - final Libraries libraries; - - const ForgeInstallProfile({ - this.spec, - required this.version, - required this.versionJson, - this.path, - this.filePath, - required this.minecraft, - this.jsonPath, - required this.data, - required this.processors, - required this.libraries, - }); - - factory ForgeInstallProfile.fromNewJson(Map profileJson, - {Map? versionJson}) => - ForgeInstallProfile( - spec: profileJson['spec'], - version: profileJson['version'], - versionJson: profileJson['VersionJson'] ?? versionJson, - path: profileJson['path'], - minecraft: profileJson['minecraft'], - jsonPath: profileJson['json'], - data: ForgeDataList.fromJson(profileJson['data']), - processors: Processors.fromList(profileJson['processors']), - libraries: Libraries.fromList(profileJson['libraries'])); - - factory ForgeInstallProfile.fromOldJson(Map profileJson) { - old_profile.ForgeOldProfile forgeOldProfile = - old_profile.ForgeOldProfile.fromMap(profileJson); - - List newLibraries = []; - - String forgeMavenPath = forgeOldProfile.install.path; - String forgeVersion = forgeOldProfile.install.version - .replaceAll("forge ", "") - .replaceAll("Forge ", ""); - - List ignoreList = [ - forgeMavenPath, - "net.minecraft:launchwrapper:1.12", - "lzma:lzma:0.0.1", - "java3d:vecmath:1.5.2" - ]; - - forgeOldProfile.versionInfo.libraries.forEach((library) { - if (!ignoreList.contains(library.name)) { - Map result = Util.parseLibMaven(library.toMap(), - baseUrl: library.url ?? "https://repo1.maven.org/maven2/"); - newLibraries.add(Library( - name: library.name, - downloads: LibraryDownloads( - artifact: Artifact( - url: result["Url"], - path: result["Path"], - )))); - } - }); - - ///手動新增一些函式庫 - newLibraries.addAll([ - const Library( - name: "net.minecraft:launchwrapper:1.12", - downloads: LibraryDownloads( - artifact: Artifact( - url: - "https://libraries.minecraft.net/net/minecraft/launchwrapper/1.12/launchwrapper-1.12.jar", - path: - "net/minecraft/launchwrapper/1.12/launchwrapper-1.12.jar", - sha1: "111e7bea9c968cdb3d06ef4632bf7ff0824d0f36", - size: 32999))), - const Library( - name: "lzma:lzma:0.0.1", - downloads: LibraryDownloads( - artifact: Artifact( - url: - "https://phoenixnap.dl.sourceforge.net/project/kcauldron/lzma/lzma/0.0.1/lzma-0.0.1.jar", - path: "lzma/lzma/0.0.1/lzma-0.0.1.jar", - ))), - const Library( - name: "java3d:vecmath:1.5.2", - downloads: LibraryDownloads( - artifact: Artifact( - url: - "https://repo1.maven.org/maven2/javax/vecmath/vecmath/1.5.2/vecmath-1.5.2.jar", - path: "java3d/vecmath/1.5.2/vecmath-1.5.2.jar", - sha1: "fd17bc3e67f909573dfc039d8e2abecf407c4e27"))), - Library( - name: forgeMavenPath, - downloads: LibraryDownloads( - artifact: Artifact( - url: "", - path: "net/minecraftforge/forge/$forgeVersion/$forgeVersion.jar", - ))) - ]); - - Map forgeMeta = forgeOldProfile.versionInfo.toMap(); - forgeMeta['libraries'] = newLibraries.map((e) => e.toJson()).toList(); - - return ForgeInstallProfile( - version: forgeVersion, - versionJson: forgeMeta, - minecraft: forgeOldProfile.install.minecraft, - path: forgeOldProfile.install.path, - filePath: forgeOldProfile.install.filePath, - data: ForgeDataList.fromJson({}), - processors: Processors.fromList([]), - libraries: Libraries.fromList([])); - } - - Map toJson() => { - 'spec': spec, - 'version': version, - 'VersionJson': json.encode(versionJson), - 'path': path, - 'minecraft': minecraft, - 'jsonPath': jsonPath, - 'data': data.toList(), - 'processors': processors.toList(), - 'libraries': libraries.toList() - }; - - Future getInstallerLib(MinecraftClientHandler handler) async { - /* - 下載Forge安裝器的相關函式庫 (執行所需的依賴項) - */ - await Future.forEach(libraries, (Library lib) async { - Artifact? artifact = lib.downloads.artifact; - if (artifact != null) { - final url = artifact.url; - - if (url == "") return; //如果網址為無效則不執行下載 - - installingState.downloadInfos.add(DownloadInfo(url, - savePath: artifact.localFile.path, - sh1Hash: artifact.sha1, - hashCheck: true, - description: I18n.format( - 'version.list.downloading.forge.processors.library'))); - } - }); - } -} diff --git a/lib/launcher/Forge/ForgeOldProfile.dart b/lib/launcher/Forge/ForgeOldProfile.dart deleted file mode 100644 index d16aba08c..000000000 --- a/lib/launcher/Forge/ForgeOldProfile.dart +++ /dev/null @@ -1,342 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; - -class ForgeOldProfile { - final Install install; - final VersionInfo versionInfo; - ForgeOldProfile({ - required this.install, - required this.versionInfo, - }); - - ForgeOldProfile copyWith({ - Install? install, - VersionInfo? versionInfo, - List? optionals, - }) { - return ForgeOldProfile( - install: install ?? this.install, - versionInfo: versionInfo ?? this.versionInfo); - } - - Map toMap() { - return {'install': install.toMap(), 'versionInfo': versionInfo.toMap()}; - } - - factory ForgeOldProfile.fromMap(Map map) { - return ForgeOldProfile( - install: Install.fromMap(map['install']), - versionInfo: VersionInfo.fromMap(map['versionInfo']), - ); - } - - String toJson() => json.encode(toMap()); - - factory ForgeOldProfile.fromJson(String source) => - ForgeOldProfile.fromMap(json.decode(source)); - - @override - String toString() => - 'ForgeOldProfile(install: $install, versionInfo: $versionInfo)'; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ForgeOldProfile && - other.install == install && - other.versionInfo == versionInfo; - } - - @override - int get hashCode => install.hashCode ^ versionInfo.hashCode; -} - -class Install { - final String profileName; - final String target; - final String path; - final String version; - final String filePath; - final String welcome; - final String minecraft; - final String mirrorList; - final String logo; - final String? modList; - Install({ - required this.profileName, - required this.target, - required this.path, - required this.version, - required this.filePath, - required this.welcome, - required this.minecraft, - required this.mirrorList, - required this.logo, - required this.modList, - }); - - Install copyWith({ - String? profileName, - String? target, - String? path, - String? version, - String? filePath, - String? welcome, - String? minecraft, - String? mirrorList, - String? logo, - String? modList, - }) { - return Install( - profileName: profileName ?? this.profileName, - target: target ?? this.target, - path: path ?? this.path, - version: version ?? this.version, - filePath: filePath ?? this.filePath, - welcome: welcome ?? this.welcome, - minecraft: minecraft ?? this.minecraft, - mirrorList: mirrorList ?? this.mirrorList, - logo: logo ?? this.logo, - modList: modList ?? this.modList, - ); - } - - Map toMap() { - return { - 'profileName': profileName, - 'target': target, - 'path': path, - 'version': version, - 'filePath': filePath, - 'welcome': welcome, - 'minecraft': minecraft, - 'mirrorList': mirrorList, - 'logo': logo, - 'modList': modList, - }; - } - - factory Install.fromMap(Map map) { - return Install( - profileName: map['profileName'], - target: map['target'], - path: map['path'], - version: map['version'], - filePath: map['filePath'], - welcome: map['welcome'], - minecraft: map['minecraft'], - mirrorList: map['mirrorList'], - logo: map['logo'], - modList: map['modList'], - ); - } - - String toJson() => json.encode(toMap()); - - factory Install.fromJson(String source) => - Install.fromMap(json.decode(source)); - - @override - String toString() { - return 'Install(profileName: $profileName, target: $target, path: $path, version: $version, filePath: $filePath, welcome: $welcome, minecraft: $minecraft, mirrorList: $mirrorList, logo: $logo, modList: $modList)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Install && - other.profileName == profileName && - other.target == target && - other.path == path && - other.version == version && - other.filePath == filePath && - other.welcome == welcome && - other.minecraft == minecraft && - other.mirrorList == mirrorList && - other.logo == logo && - other.modList == modList; - } - - @override - int get hashCode { - return profileName.hashCode ^ - target.hashCode ^ - path.hashCode ^ - version.hashCode ^ - filePath.hashCode ^ - welcome.hashCode ^ - minecraft.hashCode ^ - mirrorList.hashCode ^ - logo.hashCode ^ - modList.hashCode; - } -} - -class VersionInfo { - final String id; - final String time; - final String releaseTime; - final String type; - final String minecraftArguments; - final String mainClass; - final String inheritsFrom; - final String jar; - final List libraries; - VersionInfo({ - required this.id, - required this.time, - required this.releaseTime, - required this.type, - required this.minecraftArguments, - required this.mainClass, - required this.inheritsFrom, - required this.jar, - required this.libraries, - }); - - VersionInfo copyWith({ - String? id, - String? time, - String? releaseTime, - String? type, - String? minecraftArguments, - String? mainClass, - String? inheritsFrom, - String? jar, - List? libraries, - }) { - return VersionInfo( - id: id ?? this.id, - time: time ?? this.time, - releaseTime: releaseTime ?? this.releaseTime, - type: type ?? this.type, - minecraftArguments: minecraftArguments ?? this.minecraftArguments, - mainClass: mainClass ?? this.mainClass, - inheritsFrom: inheritsFrom ?? this.inheritsFrom, - jar: jar ?? this.jar, - libraries: libraries ?? this.libraries, - ); - } - - Map toMap() { - return { - 'id': id, - 'time': time, - 'releaseTime': releaseTime, - 'type': type, - 'minecraftArguments': minecraftArguments, - 'mainClass': mainClass, - 'inheritsFrom': inheritsFrom, - 'jar': jar, - 'libraries': libraries.map((x) => x.toMap()).toList(), - }; - } - - factory VersionInfo.fromMap(Map map) { - return VersionInfo( - id: map['id'], - time: map['time'], - releaseTime: map['releaseTime'], - type: map['type'], - minecraftArguments: map['minecraftArguments'], - mainClass: map['mainClass'], - inheritsFrom: map['inheritsFrom'], - jar: map['jar'], - libraries: List.from( - map['libraries']?.map((x) => Librarie.fromMap(x))), - ); - } - - String toJson() => json.encode(toMap()); - - factory VersionInfo.fromJson(String source) => - VersionInfo.fromMap(json.decode(source)); - - @override - String toString() { - return 'VersionInfo(id: $id, time: $time, releaseTime: $releaseTime, type: $type, minecraftArguments: $minecraftArguments, mainClass: $mainClass, inheritsFrom: $inheritsFrom, jar: $jar, libraries: $libraries)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is VersionInfo && - other.id == id && - other.time == time && - other.releaseTime == releaseTime && - other.type == type && - other.minecraftArguments == minecraftArguments && - other.mainClass == mainClass && - other.inheritsFrom == inheritsFrom && - other.jar == jar && - listEquals(other.libraries, libraries); - } - - @override - int get hashCode { - return id.hashCode ^ - time.hashCode ^ - releaseTime.hashCode ^ - type.hashCode ^ - minecraftArguments.hashCode ^ - mainClass.hashCode ^ - inheritsFrom.hashCode ^ - jar.hashCode ^ - libraries.hashCode; - } -} - -class Librarie { - final String name; - final String? url; - Librarie({ - required this.name, - required this.url, - }); - - Librarie copyWith({ - String? name, - String? url, - }) { - return Librarie( - name: name ?? this.name, - url: url ?? this.url, - ); - } - - Map toMap() { - return { - 'name': name, - 'url': url, - }; - } - - factory Librarie.fromMap(Map map) { - return Librarie( - name: map['name'], - url: map['url'], - ); - } - - String toJson() => json.encode(toMap()); - - factory Librarie.fromJson(String source) => - Librarie.fromMap(json.decode(source)); - - @override - String toString() => 'Librarie(name: $name, url: $url)'; - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Librarie && other.name == name && other.url == url; - } - - @override - int get hashCode => name.hashCode ^ url.hashCode; -} diff --git a/lib/launcher/Forge/Processors.dart b/lib/launcher/Forge/Processors.dart deleted file mode 100644 index 5af5def05..000000000 --- a/lib/launcher/Forge/Processors.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'dart:io'; - -import 'package:dart_big5/big5.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeAPI.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeData.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/Process.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:archive/archive.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/util/data.dart'; - -class Processors { - final List processors; - const Processors({ - required this.processors, - }); - - factory Processors.fromList(List processors) { - List processors_ = []; - processors - .forEach((processor) => processors_.add(Processor.fromJson(processor))); - return Processors(processors: processors_); - } - - List toList() => processors; -} - -class Processor { - final String jar; - final List classpath; - final List args; - final Map? outputs; - final List? sides; - - const Processor({ - required this.jar, - required this.classpath, - required this.args, - this.outputs, - required this.sides, - }); - - factory Processor.fromJson(Map json) => Processor( - jar: json['jar'], - classpath: json['classpath'].cast(), - args: json['args'].cast(), - outputs: json.containsKey('outputs') - ? json['outputs'].cast() - : null, - sides: json.containsKey('sides') ? json['sides'].cast() : null); - - Map toJson() => { - 'jar': jar, - 'classpath': classpath, - 'args': args, - 'outputs': outputs, - }; - - Future execution( - InstanceConfig instanceConfig, - List libraries, - String forgeVersionID, - String gameVersionID, - ForgeDataList dataList) async { - if (sides != null && - sides!.contains("server") && - !sides!.contains("client")) { - // 目前 RPMLauncher 只支援安裝Forge客戶端 - return; - } - - int javaVersion = instanceConfig.javaVersion; - File processorJarFile = ForgeAPI.getLibFile(libraries, jar); - File installerFile = File(join(dataHome.absolute.path, "temp", - "forge-installer", forgeVersionID, "$forgeVersionID-installer.jar")); - - String classPathFiles = - processorJarFile.absolute.path + Util.getLibrarySeparator(); - - await Future.forEach(classpath, (String lib) { - classPathFiles += - "${ForgeAPI.getLibFile(libraries, lib).absolute.path}${Util.getLibrarySeparator()}"; - }); - - String? mainClass = Util.getJarMainClass(processorJarFile); - - if (mainClass == null) { - logger.error(ErrorType.io, "No MainClass found in $jar"); //如果找不到程式進入點 - return; - } else { - mainClass = mainClass - .replaceAll(" ", "") - .replaceAll("\n", "") - .replaceAll("\t", "") - .replaceAll("\r", ""); - } - List arguments = []; - - arguments.add("-cp"); - arguments.add(classPathFiles); //處理器函式庫 - arguments.add(mainClass); //程式進入點 - - await Future.forEach(args, (String _) { - if (Util.isSurrounded(_, "[", "]")) { - //解析輸入參數有 [檔案名稱] - String libName = _.split("[").join("").split("]").join(""); //去除方括號 - _ = ForgeAPI.getLibFile(libraries, libName).absolute.path; - } else if (Util.isSurrounded(_, "{", "}")) { - //如果參數包含Forge資料的內容將進行替換 - String key = _.split("{").join("").split("}").join(""); //去除 {} - - if (key == "MINECRAFT_JAR") { - _ = GameRepository.getClientJar(gameVersionID).absolute.path; - } else if (key == "SIDE") { - _ = "client"; - } else if (key == "MINECRAFT_VERSION") { - _ = GameRepository.getClientJar(gameVersionID).absolute.path; - } else if (key == "ROOT") { - _ = dataHome.absolute.path; - } else if (key == "INSTALLER") { - _ = installerFile.absolute.path; - } else if (key == "LIBRARY_DIR") { - _ = GameRepository.getLibraryGlobalDir().absolute.path; - } else if (dataList.forgeDataKeys.contains(key)) { - ForgeData data = - dataList.forgeDataList[dataList.forgeDataKeys.indexOf(key)]; - String clientData = data.client; - if (Util.isSurrounded(clientData, "[", "]")) { - String dataPath = - clientData.split("[").join("").split("]").join(""); //去除方括號 - List split_ = Util.split(dataPath, ":", max: 4); - - String? extension_; - int last = split_.length - 1; - List split = split_[last].split("@"); - if (split.length == 2) { - split_[last] = split[0]; - extension_ = split[1]; - } - - String group = split_[0].toString().replaceAll("\\", "/"); - String name = split_[1]; - String version = split_[2]; - String? classifier = split_.length >= 4 ? split_[3] : null; - String extension = extension_ ?? "jar"; - - String fileName = "$name-$version"; - if (classifier != null) fileName += "-$classifier"; - fileName = "$fileName.$extension"; - var path = "${group.replaceAll(".", "/")}/$name/$version/$fileName"; - - _ = join(GameRepository.getLibraryGlobalDir().absolute.path, - path); //資料存放路徑 - } else if (clientData.startsWith("/")) { - //例如 /data/client.lzma - final Archive archive = - ZipDecoder().decodeBytes(installerFile.readAsBytesSync()); - for (final file in archive) { - if (file.isFile && - file.name.contains(clientData.replaceFirst("/", ""))) { - final data = file.content as List; - File dataFile = File(join( - dataHome.absolute.path, - "temp", - "forge-installer", - forgeVersionID, - file.name.replaceAll("/", Platform.pathSeparator))); - dataFile.createSync(recursive: true); - dataFile.writeAsBytesSync(data); - _ = dataFile.absolute.path; - break; - } - } - } else {} - } - } else if (Util.isSurrounded(_, "'", "'")) {} - arguments.add(_); //新增處理後的參數 - }); - //如果有輸出內容 - if (outputs != null) { - // TODO: 處理輸出的內容,目前看到的都是輸出雜湊值 - } - - final exec = ConfigHelper.get( - 'java_path_16', - ) ?? - ConfigHelper.get('java_path_$javaVersion')!; - - await chmod(exec); - - logger.info("$jar - Forge process arguments: $exec ${arguments.join(" ")}"); - - Process? process = await Process.start(exec, arguments, - workingDirectory: dataHome.absolute.path); - - String errorLog = ""; - String runLog = ""; - try { - process.stdout.listen((data) { - String string = big5.decode(data); - runLog += string; - }); - process.stderr.listen((data) { - String string = big5.decode(data); - errorLog += string; - logger.info("$jar - error: $string"); - }); - } catch (err) {} - await process.exitCode.then((code) { - logger.info("$jar - Forge process is exited, exit code: $code"); - if (code != 0) { - logger.info( - "$jar - An unknown error occurred while running the Forge process:\n$errorLog\n$runLog"); - } - process = null; - }); - } -} diff --git a/lib/launcher/GameRepository.dart b/lib/launcher/GameRepository.dart deleted file mode 100644 index 679a9a670..000000000 --- a/lib/launcher/GameRepository.dart +++ /dev/null @@ -1,153 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/util/launcher_path.dart'; -import 'package:uuid/uuid.dart'; - -class GameRepository { - static void init(Directory root) { - File configFile = File(join(root.path, 'config.json')); - File accountFile = File(join(root.path, 'accounts.json')); - if (!configFile.existsSync()) { - configFile.create(recursive: true); - configFile.writeAsStringSync('{}'); - } - if (!accountFile.existsSync()) { - accountFile.create(recursive: true); - accountFile.writeAsStringSync('{}'); - } - Util.createFolderOptimization(getInstanceRootDir()); - } - - static Directory getInstanceRootDir() { - return Directory(join(dataHome.absolute.path, 'instances')); - } - - static File getConfigFile() { - File file = - File(join(LauncherPath.currentConfigHome.absolute.path, 'config.json')); - - if (!file.existsSync()) { - file.create(recursive: true); - file.writeAsStringSync(json.encode({})); - } - return file; - } - - static File getAccountFile() { - File file = File( - join(LauncherPath.currentConfigHome.absolute.path, 'accounts.json')); - - if (!file.existsSync()) { - file.create(recursive: true); - file.writeAsStringSync(json.encode({})); - } - return file; - } - - static Directory getVersionsRootDir() { - return Directory(join(dataHome.absolute.path, 'versions')); - } - - static Directory getAssetsDir() { - return Directory(join(dataHome.path, 'assets')); - } - - static File getAssetsObjectFile(String hash) { - return File(join( - getAssetsDir().absolute.path, 'objects', hash.substring(0, 2), hash)); - } - - static Directory getVersionsDir(String versionID) { - return Directory(join(getVersionsRootDir().absolute.path, versionID)); - } - - static Directory getNativesDir(String versionID) { - return Directory(join(getVersionsDir(versionID).absolute.path, 'natives')); - } - - static Directory getNativesTempDir() { - return Directory( - join(dataHome.absolute.path, 'temp_natives', const Uuid().v4())); - } - - static Directory getTempDir() { - return Directory(join(dataHome.absolute.path, 'temp')); - } - - static Directory getDatabaseDir() { - return Directory(join(dataHome.absolute.path, 'database')); - } - - static File getClientJar(String versionID) { - File file = - File(join(getVersionsDir(versionID).absolute.path, '$versionID.jar')); - - if (!file.existsSync()) { - /// RPMLauncher 舊版放置位置 - file = File(join(getVersionsDir(versionID).absolute.path, 'client.jar')); - } - return file; - } - - static File getArgsFile( - String versionID, ModLoader loader, MinecraftSide side, - {String? loaderVersion}) { - if (loader != ModLoader.vanilla && loaderVersion == null) { - throw Exception( - 'Mod loaders other than the vanilla require loader version parameters'); - } - - String argsPath = join(getVersionsDir(versionID).absolute.path, 'args'); - - /// RPMLauncher 舊版格式 - File oldFile = _oldArgs(loader, argsPath, loaderVersion: loaderVersion); - if (side.isClient && oldFile.existsSync()) { - return oldFile; - } else { - switch (loader) { - case ModLoader.fabric: - return File( - join(argsPath, 'Fabric', '${side.name}-$loaderVersion.json')); - case ModLoader.forge: - return File( - join(argsPath, 'Forge', '${side.name}-$loaderVersion.json')); - case ModLoader.vanilla: - return File(join(argsPath, '${side.name}-args.json')); - default: - throw Exception('Unknown loader, failed to get Args'); - } - } - } - - static File _oldArgs(ModLoader loader, String argsPath, - {String? loaderVersion}) { - switch (loader) { - case ModLoader.fabric: - return File(join(argsPath, 'Fabric', '$loaderVersion.json')); - case ModLoader.forge: - return File(join(argsPath, 'Forge', '$loaderVersion.json')); - case ModLoader.vanilla: - return File(join(argsPath, 'args.json')); - default: - throw Exception('Unknown loader, failed to get Args'); - } - } - - static File getModIconFile(String hash) { - return File(join(getTempDir().path, 'mod_icons', '$hash.png')); - } - - static File getForgeProfileFile(String versionID) { - return File(join(getVersionsDir(versionID).path, 'forge_profile.json')); - } - - static Directory getLibraryGlobalDir() { - return Directory(join(dataHome.absolute.path, 'libraries')); - } -} diff --git a/lib/launcher/InstallingState.dart b/lib/launcher/InstallingState.dart deleted file mode 100644 index 60152f104..000000000 --- a/lib/launcher/InstallingState.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; - -InstallingState installingState = InstallingState(); - -class InstallingState { - DownloadInfos downloadInfos = DownloadInfos.empty(); - String nowEvent = I18n.format('version.list.downloading.ready'); - bool finish = false; -} diff --git a/lib/launcher/InstanceRepository.dart b/lib/launcher/InstanceRepository.dart deleted file mode 100644 index 955992caa..000000000 --- a/lib/launcher/InstanceRepository.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:path/path.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; - -import 'GameRepository.dart'; - -class InstanceRepository { - static final Directory _instanceRootDir = GameRepository.getInstanceRootDir(); - - static Directory getInstanceDir(String instanceUUID) { - return Directory(join(_instanceRootDir.path, instanceUUID)); - } - - static String getUUIDByDir(Directory dir) { - return basename(dir.path); - } - - static File instanceConfigFile(String instanceUUID) { - return File(join(getInstanceDir(instanceUUID).path, "instance.json")); - } - - static InstanceConfig? instanceConfig(String instanceUUID) { - return InstanceConfig.fromFile(instanceConfigFile(instanceUUID)); - } - - static void updateInstanceConfigFile(String instanceUUID, Map contents) { - instanceConfigFile(instanceUUID).writeAsStringSync(json.encode(contents)); - } - - static Directory getModRootDir(String instanceUUID) { - return Directory(join(getInstanceDir(instanceUUID).path, "mods")); - } - - static Directory getResourcePackRootDir(String instanceUUID) { - return Directory(join(getInstanceDir(instanceUUID).path, "resourcepacks")); - } - - static Directory getShaderpackRootDir(String instanceUUID) { - return Directory(join(getInstanceDir(instanceUUID).path, "shaderpacks")); - } - - static Directory getWorldRootDir(String instanceUUID) { - return Directory(join(getInstanceDir(instanceUUID).path, "saves")); - } - - static Directory getScreenshotRootDir(String instanceUUID) { - return Directory(join(getInstanceDir(instanceUUID).path, "screenshots")); - } -} diff --git a/lib/launcher/MinecraftClient.dart b/lib/launcher/MinecraftClient.dart deleted file mode 100644 index 6ebbda663..000000000 --- a/lib/launcher/MinecraftClient.dart +++ /dev/null @@ -1,195 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:archive/archive.dart'; -import 'package:http/http.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/data.dart'; - -import 'Arguments.dart'; - -abstract class MinecraftClient { - MinecraftMeta get meta => handler.meta; - - MinecraftClientHandler get handler; - - StateSetter get setState => handler.setState; - - Instance get instance => handler.instance; - - String get versionID => handler.versionID; -} - -class MinecraftClientHandler { - final MinecraftMeta meta; - final String versionID; - final StateSetter setState; - final Instance instance; - - MinecraftClientHandler( - {required this.meta, - required this.versionID, - required this.setState, - required this.instance}); - - void clientJar() { - installingState.downloadInfos.add(DownloadInfo( - meta.rawMeta["downloads"]["client"]["url"], - savePath: join( - dataHome.absolute.path, "versions", versionID, "$versionID.jar"), - sh1Hash: meta.rawMeta["downloads"]["client"]["sha1"], - description: I18n.format('version.list.downloading.main'))); - } - - Future getArgs() async { - File argsFile = GameRepository.getArgsFile( - versionID, ModLoader.vanilla, MinecraftSide.client); - await argsFile.create(recursive: true); - await argsFile - .writeAsString(json.encode(Arguments().getArgsString(versionID, meta))); - } - - Future getAssets() async { - final url = Uri.parse(meta.rawMeta["assetIndex"]["url"]); - Response response = await get(url); - Map body = json.decode(response.body); - File indexFile = File(join(dataHome.absolute.path, "assets", "indexes", - "${meta.rawMeta["assets"]}.json")) - ..createSync(recursive: true); - indexFile.writeAsStringSync(response.body); - for (var i in body["objects"].keys) { - String hash = body["objects"][i]["hash"].toString(); - - installingState.downloadInfos.add(DownloadInfo( - "https://resources.download.minecraft.net/${hash.substring(0, 2)}/$hash", - savePath: GameRepository.getAssetsObjectFile(hash).path, - sh1Hash: hash, - hashCheck: true, - description: I18n.format('version.list.downloading.assets'))); - } - } - - void getLib() { - Libraries libraries = Libraries.fromList(meta.rawMeta["libraries"]); - instance.config.libraries = libraries; - - for (Library lib in libraries) { - if (lib.need) { - if (lib.downloads.classifiers != null) { - Classifiers classifiers = lib.downloads.classifiers!; - downloadNatives( - classifiers.path, classifiers.url, classifiers.sha1, versionID); - } - - Artifact? artifact = lib.downloads.artifact; - if (artifact != null) { - if (lib.name.contains('natives')) { - downloadNatives( - artifact.path, artifact.url, artifact.sha1, versionID); - } else { - installingState.downloadInfos.add(DownloadInfo(artifact.url, - savePath: artifact.localFile.path, - sh1Hash: artifact.sha1, - hashCheck: true, - description: I18n.format('version.list.downloading.library'))); - } - } - } - } - } - - void downloadNatives(String path, String url, String? sha1, version) { - List split_ = path.split("/"); - installingState.downloadInfos.add(DownloadInfo(url, - savePath: join(GameRepository.getNativesDir(version).absolute.path, - split_[split_.length - 1]), - sh1Hash: sha1, - hashCheck: true, - description: I18n.format('version.list.downloading.library'), - onDownloaded: () { - handlingNativesJar(split_[split_.length - 1], - GameRepository.getNativesDir(version).absolute.path); - })); - } - - void handlingNativesJar(String fileName, dir) { - File file = File(join(dir, fileName)); - - try { - final bytes = file.readAsBytesSync(); - final archive = ZipDecoder().decodeBytes(bytes); - for (final file in archive.files) { - final filePath = file.name; - if (filePath.contains("META-INF")) continue; - if (file.isFile) { - if (filePath.endsWith(".git") || filePath.endsWith(".sha1")) { - continue; - } - final data = file.content as List; - File(join(dir, filePath)) - ..createSync(recursive: true) - ..writeAsBytesSync(data); - } else { - Directory(join(dir, filePath)).create(recursive: true); - } - } - } on ArchiveException { - logger.error(ErrorType.io, "failed to decompress natives library jar"); - } on FileSystemException { - logger.error(ErrorType.io, "failed to open natives library jar"); - } catch (e, stackTrace) { - logger.error(ErrorType.unknown, e, stackTrace: stackTrace); - } - try { - file.deleteSync(recursive: true); - } catch (e) {} - } - - Future handlingLogging() async { - if (meta.containsKey('logging') && meta['logging'].containsKey('client')) { - Map logging = meta['logging']['client']; - if (logging.containsKey('file')) { - Map file = logging['file']; - String url = file['url']; - String sha1 = file['sha1']; - installingState.downloadInfos.add(DownloadInfo(url, - savePath: GameRepository.getAssetsObjectFile(sha1).path, - sh1Hash: sha1, - hashCheck: true, - description: I18n.format('version.list.downloading.logging'))); - } - } - } - - Future install() async { - getLib(); - clientJar(); - await getAssets(); - await handlingLogging(); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - try { - setState(() {}); - } catch (e) {} - }); - try { - setState(() { - installingState.nowEvent = I18n.format('version.list.downloading.args'); - }); - } catch (e) {} - await getArgs(); - return this; - } -} diff --git a/lib/launcher/MinecraftServer.dart b/lib/launcher/MinecraftServer.dart deleted file mode 100644 index 2c4d2620f..000000000 --- a/lib/launcher/MinecraftServer.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; - -abstract class MinecraftServer { - MinecraftMeta get meta => handler.meta; - - MinecraftServerHandler get handler; - - StateSetter get setState => handler.setState; - - Instance get instance => handler.instance; - - String get versionID => handler.versionID; -} - -class MinecraftServerHandler { - final MinecraftMeta meta; - final String versionID; - final StateSetter setState; - final Instance instance; - - MinecraftServerHandler( - {required this.meta, - required this.versionID, - required this.setState, - required this.instance}); -} diff --git a/lib/launcher/Vanilla/VanillaClient.dart b/lib/launcher/Vanilla/VanillaClient.dart deleted file mode 100644 index 47a8fb491..000000000 --- a/lib/launcher/Vanilla/VanillaClient.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; - -import '../MinecraftClient.dart'; - -class VanillaClient extends MinecraftClient { - @override - MinecraftClientHandler handler; - - VanillaClient._init({ - required this.handler, - }); - - static Future createClient( - {required MinecraftMeta meta, - required String versionID, - required Instance instance, - required StateSetter setState}) async { - return await VanillaClient._init( - handler: MinecraftClientHandler( - meta: meta, - versionID: versionID, - instance: instance, - setState: setState), - )._ready(); - } - - Future _ready() async { - await handler.install(); - return this; - } -} diff --git a/lib/launcher/Vanilla/VanillaServer.dart b/lib/launcher/Vanilla/VanillaServer.dart deleted file mode 100644 index 5717c9bd6..000000000 --- a/lib/launcher/Vanilla/VanillaServer.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/MinecraftServer.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/launcher/Arguments.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; - -class VanillaServer extends MinecraftServer { - @override - MinecraftServerHandler handler; - - VanillaServer._init({ - required this.handler, - }); - - static Future createServer( - {required MinecraftMeta meta, - required String versionID, - required Instance instance, - required StateSetter setState}) async { - return await VanillaServer._init( - handler: MinecraftServerHandler( - meta: meta, - versionID: versionID, - instance: instance, - setState: setState), - )._ready(); - } - - void serverJar() { - Map server = meta["downloads"]["server"]; - Libraries libraries = instance.config.libraries; - libraries.add(Library( - name: "net.minecraft:server:$versionID", - downloads: LibraryDownloads( - artifact: Artifact( - url: server["url"], - sha1: server["sha1"], - size: server["size"], - path: "net/minecraft/server/$versionID.jar")))); - instance.config.libraries = libraries; - - installingState.downloadInfos.add(DownloadInfo(server["url"], - savePath: join(GameRepository.getLibraryGlobalDir().path, "net", - "minecraft", "server", "$versionID.jar"), - sh1Hash: server["sha1"], - hashCheck: true, - description: I18n.format('version.list.downloading.main'))); - } - - Future getArgs() async { - File serverJar = File(join(GameRepository.getLibraryGlobalDir().path, "net", - "minecraft", "server", "$versionID.jar")); - - File argsFile = GameRepository.getArgsFile( - versionID, ModLoader.vanilla, MinecraftSide.server); - await argsFile.create(recursive: true); - Map argsMap = Arguments().getArgsString(versionID, meta); - String? mainClass = Util.getJarMainClass(serverJar); - argsMap['mainClass'] = mainClass ?? "net.minecraft.bundler.Main"; - await argsFile.writeAsString(json.encode(argsMap)); - } - - Future _ready() async { - serverJar(); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - try { - setState(() {}); - } catch (e) {} - }); - setState(() { - installingState.nowEvent = I18n.format('version.list.downloading.args'); - }); - await getArgs(); - - return this; - } -} diff --git a/lib/launcher/apis.dart b/lib/launcher/apis.dart deleted file mode 100644 index 32a8a10c0..000000000 --- a/lib/launcher/apis.dart +++ /dev/null @@ -1,17 +0,0 @@ -String mojangMetaAPI = 'https://launchermeta.mojang.com/mc/game'; -String fabricApi = 'https://meta.fabricmc.net/v2'; -String forgeFilesAPI = 'https://files.minecraftforge.net'; -String forgeFilesMainAPI = - 'https://files.minecraftforge.net/net/minecraftforge/forge'; -String forgeLatestVersionAPI = '$forgeFilesMainAPI/promotions_slim.json'; -String forgeMavenUrl = 'https://maven.minecraftforge.net'; -String forgeMavenMainUrl = - 'https://maven.minecraftforge.net/net/minecraftforge/forge'; -String mojangAuthAPI = 'https://authserver.mojang.com'; -String modrinthAPI = 'https://api.modrinth.com/v2'; -String ftbModPackAPI = 'https://api.modpacks.ch/public'; -String minecraftNewsJson = - 'https://www.minecraft.net/content/minecraft-net/_jcr_content.articles.grid'; -String recommendedModpack = - 'https://github.com/RPMTW/RPMTW-website-data/raw/main/data/RPMLauncher/recommend-modpack.json'; -String paperApi = 'https://papermc.io/api/v2'; diff --git a/lib/launcher/collection/collection.dart b/lib/launcher/collection/collection.dart new file mode 100644 index 000000000..7d19bc32d --- /dev/null +++ b/lib/launcher/collection/collection.dart @@ -0,0 +1,31 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:rpmlauncher/launcher/collection/component.dart'; + +part 'collection.g.dart'; + +@JsonSerializable() +class Collection extends Equatable { + final String name; + final String displayName; + final String? notes; + final List components; + + const Collection({ + required this.name, + required this.displayName, + this.notes, + required this.components, + }); + + factory Collection.fromJson(Map json) => + _$CollectionFromJson(json); + + Map toJson() => _$CollectionToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [name, displayName, notes, components]; +} diff --git a/lib/launcher/collection/component.dart b/lib/launcher/collection/component.dart new file mode 100644 index 000000000..b2af8bd5a --- /dev/null +++ b/lib/launcher/collection/component.dart @@ -0,0 +1,41 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'component.g.dart'; + +@JsonSerializable() +class Component extends Equatable { + final String name; + final String identifier; + final String version; + final bool mainEntry; + final bool mandatory; + + const Component({ + required this.name, + required this.identifier, + required this.version, + this.mainEntry = false, + this.mandatory = false, + }); + + factory Component.fromJson(Map json) => + _$ComponentFromJson(json); + + Map toJson() => _$ComponentToJson(this); + + factory Component.minecraft(String version) { + return Component( + name: 'Minecraft', + identifier: 'net.minecraft', + version: version, + mainEntry: true, + mandatory: true); + } + + @override + bool get stringify => true; + + @override + List get props => [name, identifier, version, mainEntry, mandatory]; +} diff --git a/lib/launcher/download/assets_download_task.dart b/lib/launcher/download/assets_download_task.dart new file mode 100644 index 000000000..6ac5bcc2f --- /dev/null +++ b/lib/launcher/download/assets_download_task.dart @@ -0,0 +1,56 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:path/path.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; +import 'package:rpmlauncher/model/game/assets/asset_object.dart'; +import 'package:rpmlauncher/model/game/assets/assets_index.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_asset_index.dart'; +import 'package:rpmlauncher/task/fetch_task.dart'; +import 'package:rpmlauncher/task/isolate_task.dart'; +import 'package:rpmlauncher/task/task_size.dart'; +import 'package:rpmlauncher/util/io_util.dart'; +import 'package:rpmlauncher/util/rpml_http_client.dart'; + +class AssetsDownloadTask extends IsolateTask { + final MCVersionAssetIndex assetIndex; + + AssetsDownloadTask(this.assetIndex); + + @override + String get name => 'assets_download_task'; + + @override + TaskSize get size => TaskSize.xLarge; + + @override + Future execute() async { + setMessage('正在下載遊戲資源中...'); + final assetsDirectory = GameRepository.getAssetsDirectory(); + + final indexFilePath = + join(assetsDirectory.path, 'indexes', '${assetIndex.id}.json'); + final indexFile = File(indexFilePath); + + if (!IOUtil.isCachedFileSha1(indexFile, assetIndex.sha1)) { + await httpClient.download(assetIndex.url, indexFilePath); + } + + final index = + AssetsIndex.fromJson(json.decode(indexFile.readAsStringSync())); + + for (final object in index.objects.values) { + _download(object); + } + + return; + } + + void _download(AssetObject object) { + addPostSubTask(FetchTask( + url: object.getDownloadUrl(), + path: object.getFilePath(), + hash: object.hash, + fileSize: object.size)); + } +} diff --git a/lib/launcher/download/game_install_task.dart b/lib/launcher/download/game_install_task.dart new file mode 100644 index 000000000..e35d0c1f5 --- /dev/null +++ b/lib/launcher/download/game_install_task.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:path/path.dart'; +import 'package:rpmlauncher/launcher/collection/collection.dart'; +import 'package:rpmlauncher/launcher/collection/component.dart'; +import 'package:rpmlauncher/launcher/download/assets_download_task.dart'; +import 'package:rpmlauncher/launcher/download/library_download_task.dart'; +import 'package:rpmlauncher/launcher/download/version_meta_download_task.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; +import 'package:rpmlauncher/model/game/loader.dart'; +import 'package:rpmlauncher/model/game/version/mc_version.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_meta.dart'; +import 'package:rpmlauncher/task/basic_task.dart'; +import 'package:rpmlauncher/task/task_size.dart'; +import 'package:rpmlauncher/util/io_util.dart'; + +class GameInstallTask extends BasicTask { + final String displayName; + final GameLoader loader; + final MCVersion version; + + GameInstallTask( + {required this.displayName, required this.loader, required this.version}); + + @override + String get name => displayName; + + @override + TaskSize get size => TaskSize.medium; + + @override + Future execute() async { + setMessage('正在安裝遊戲中...'); + + final directory = GameRepository.getCollectionsDirectory(); + IOUtil.createDirectory(directory); + + final name = + _handleDuplicateName(IOUtil.replaceFolderName(displayName), directory); + final gameDirectory = Directory(join(directory.path, name)); + IOUtil.createDirectory(gameDirectory); + + final components = [Component.minecraft(version.id)]; + final collection = Collection( + name: name, displayName: displayName, components: components); + + final configFile = File(join(gameDirectory.path, 'collection.json')); + await configFile.writeAsString(json.encode(collection.toJson())); + + final MCVersionMeta meta = preSubTasks[0].result; + + addPostSubTask(LibraryDownloadTask(meta.libraries)); + addPostSubTask(AssetsDownloadTask(meta.assetIndex)); + return; + } + + @override + Future preExecute() async { + addPreSubTask(VersionMetaDownloadTask(version)); + } + + @override + Future postExecute() async { + setMessage('安裝完成'); + } + + /// Handle duplicate names of the collection directory. + String _handleDuplicateName(String name, Directory directory) { + final collectionDirectory = Directory(join(directory.path, name)); + final directoryList = directory.listSync(); + + String findName(String name, int index) { + final regexp = RegExp(r'\(\d+\)'); + + // Replace the number in the parentheses with the next number + // Example: 'name (1)' -> 'name (2)' + if (name.contains(regexp) && name.endsWith(')')) { + name = name.replaceAllMapped(regexp, (match) { + return '($index)'; + }); + } else { + name = '$name (1)'; + } + + // Check if the directory name already exists + if (directoryList.any((e) => e.path.contains(name))) { + return findName(name, index + 1); + } + + return name; + } + + if (collectionDirectory.existsSync()) { + return findName(name, 1); + } else { + return name; + } + } +} diff --git a/lib/launcher/download/library_download_task.dart b/lib/launcher/download/library_download_task.dart new file mode 100644 index 000000000..7811c3fdf --- /dev/null +++ b/lib/launcher/download/library_download_task.dart @@ -0,0 +1,44 @@ +import 'package:rpmlauncher/model/game/version/library_download_artifact.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_library.dart'; +import 'package:rpmlauncher/task/fetch_task.dart'; +import 'package:rpmlauncher/task/isolate_task.dart'; +import 'package:rpmlauncher/task/task_size.dart'; + +class LibraryDownloadTask extends IsolateTask { + final List libraries; + + LibraryDownloadTask(this.libraries); + + @override + String get name => 'library_download_task'; + + @override + TaskSize get size => TaskSize.large; + + @override + Future execute() async { + setMessage('正在下載遊戲函式庫中...'); + for (final library in libraries) { + if (library.shouldDownload()) { + final artifact = library.downloads.artifact; + final natives = library.downloads.classifiers?.getNatives(); + + if (artifact != null) { + _download(artifact); + } + if (natives != null) { + _download(natives); + } + } + } + return; + } + + void _download(LibraryDownloadArtifact artifact) { + addPostSubTask(FetchTask( + url: artifact.url, + path: artifact.getFilePath(), + hash: artifact.sha1, + fileSize: artifact.size)); + } +} diff --git a/lib/launcher/download/version_meta_download_task.dart b/lib/launcher/download/version_meta_download_task.dart new file mode 100644 index 000000000..e094aef80 --- /dev/null +++ b/lib/launcher/download/version_meta_download_task.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:path/path.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; +import 'package:rpmlauncher/model/game/version/mc_version.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_meta.dart'; +import 'package:rpmlauncher/task/basic_task.dart'; +import 'package:rpmlauncher/task/task_size.dart'; +import 'package:rpmlauncher/util/io_util.dart'; +import 'package:rpmlauncher/util/rpml_http_client.dart'; + +class VersionMetaDownloadTask extends BasicTask { + final MCVersion version; + + VersionMetaDownloadTask(this.version); + + @override + String get name => 'version_meta_download_task'; + + @override + TaskSize get size => TaskSize.tiny; + + @override + Future execute() async { + final metaDirectory = GameRepository.getMetaDirectory(); + final filePath = + join(metaDirectory.path, 'net.minecraft', '${version.id}.json'); + final file = File(filePath); + + // Check if the file exists and the hash is correct. + if (IOUtil.isCachedFileSha1(file, version.sha1)) { + return MCVersionMeta.fromJson(json.decode(file.readAsStringSync())); + } + + // Download the file of version meta. + await httpClient.download(version.url, filePath); + + return MCVersionMeta.fromJson(json.decode(file.readAsStringSync())); + } +} diff --git a/lib/launcher/game_repository.dart b/lib/launcher/game_repository.dart new file mode 100644 index 000000000..57861dc13 --- /dev/null +++ b/lib/launcher/game_repository.dart @@ -0,0 +1,35 @@ +import 'dart:io'; + +import 'package:path/path.dart'; +import 'package:rpmlauncher/util/data.dart'; +import 'package:rpmlauncher/util/launcher_path.dart'; + +class GameRepository { + static File getAccountFile() { + return File(join(LauncherPath.currentConfigHome.path, 'accounts.json')); + } + + static Directory getDatabaseDirectory() { + return Directory(join(dataHome.path, 'database')); + } + + static Directory getCollectionsDirectory() { + return Directory(join(dataHome.path, 'collections')); + } + + static Directory getMetaDirectory() { + return Directory(join(dataHome.path, 'meta')); + } + + static Directory getAssetsDirectory() { + return Directory(join(dataHome.path, 'assets')); + } + + static Directory getAssetsObjectsDirectory() { + return Directory(join(getAssetsDirectory().path, 'objects')); + } + + static Directory getLibrariesDirectory() { + return Directory(join(dataHome.path, 'libraries')); + } +} diff --git a/lib/main.dart b/lib/main.dart index d865b2fc7..a616b6218 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:rpmlauncher/config/config_helper.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/screen/loading_screen.dart'; +import 'package:rpmlauncher/ui/screens/loading_screen.dart'; import 'package:rpmlauncher/util/launcher_path.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -28,11 +28,12 @@ Future run() async { await initBeforeRunApp(); - logger.info("Starting"); + logger.info('Starting'); runApp(const LoadingScreen()); + runApp(const SentryScreenshotWidget(child: LoadingScreen())); - logger.info("Start Done"); + logger.info('Start Done'); }, (exception, stackTrace) async { if (Util.exceptionFilter(exception, stackTrace)) return; @@ -45,6 +46,6 @@ Future run() async { Future initBeforeRunApp() async { await LauncherPath.init(); - await ConfigHelper.init(); + await configHelper.init(); await I18n.init(); } diff --git a/lib/mod/FTB/Handler.dart b/lib/mod/FTB/Handler.dart deleted file mode 100644 index 8ff709f6e..000000000 --- a/lib/mod/FTB/Handler.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'dart:convert'; - -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:http/http.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:dio/dio.dart' as dio; - -class FTBHandler { - static Future getModPackList() async { - dio.Response response = await RPMHttpClient() - .get("$ftbModPackAPI/modpack/popular/installs/FTB/all"); - Map body = RPMHttpClient.json(response.data); - return body["packs"]; - } - - static Future> getTags() async { - dio.Response response = await RPMHttpClient().get( - "$ftbModPackAPI/tag/popular/100", - ); - Map body = RPMHttpClient.json(response.data); - return body["tags"].cast(); - } - - static Future> getVersions() async { - List tags = await getTags(); - RegExp versionRegExp = RegExp(r"1[0-9]."); //開頭為 1 並且含有至少一個 . 則為版本標籤 - tags = tags.where((tag) => versionRegExp.hasMatch(tag)).toList(); - tags.sort((a, b) { - int aint = int.parse(a.replaceAll(".", "")); - int bint = int.parse(b.replaceAll('.', '')); - - return bint.compareTo(aint); - }); - return tags; - } - - static Future getVersionInfo(int modPackID, int versionID) async { - final url = Uri.parse("$ftbModPackAPI/modpack/$modPackID/$versionID"); - Response response = await get(url); - Map fileInfo = json.decode(response.body); - return fileInfo; - } - - static Text parseReleaseType(String releaseType) { - late Text releaseTypeText; - if (releaseType == "release") { - releaseTypeText = Text(I18n.format("edit.instance.mods.release"), - style: const TextStyle(color: Colors.lightGreen)); - } else if (releaseType == "beta") { - releaseTypeText = Text(I18n.format("edit.instance.mods.beta"), - style: const TextStyle(color: Colors.lightBlue)); - } else if (releaseType == "alpha") { - releaseTypeText = Text(I18n.format("edit.instance.mods.alpha"), - style: const TextStyle(color: Colors.red)); - } else { - releaseTypeText = - Text(releaseType, style: const TextStyle(color: Colors.grey)); - } - return releaseTypeText; - } - - static String getWebUrlFromName(String modPackName) { - modPackName = modPackName.toLowerCase(); - List source = modPackName.split(""); - List output = []; - final RegExp english = RegExp(r"^\w+$"); - source.forEach((e) { - if (english.hasMatch(e)) { - output.add(e); - } else { - output.add("_"); - } - }); - return "https://feed-the-beast.com/modpack/${output.join("")}"; - } -} diff --git a/lib/mod/FTB/ModPackClient.dart b/lib/mod/FTB/ModPackClient.dart deleted file mode 100644 index a6a444ad1..000000000 --- a/lib/mod/FTB/ModPackClient.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/launcher/Fabric/FabricClient.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeClient.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/launcher/MinecraftClient.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/data.dart'; - -class FTBModPackClient extends MinecraftClient { - @override - MinecraftClientHandler handler; - - int totalFiles = 0; - int parsedFiles = 0; - int downloadedFiles = 0; - - FTBModPackClient._init({ - required Map versionInfo, - required Map packData, - required this.handler, - }); - - static Future createClient({ - required MinecraftMeta meta, - required Map versionInfo, - required Map packData, - required String instanceUUID, - required StateSetter setState, - }) async { - return await FTBModPackClient._init( - versionInfo: versionInfo, - packData: packData, - handler: MinecraftClientHandler( - versionID: meta.rawMeta['id'], - meta: meta, - instance: Instance.fromUUID(instanceUUID)!, - setState: setState, - ), - )._ready(versionInfo, packData); - } - - void getFiles(Map versionInfo) { - totalFiles = ((versionInfo["files"] as List).cast()) - .where((element) => !element['serveronly']) - .length; - - for (Map file in versionInfo["files"]) { - bool serverOnly = file["serveronly"]; - if (serverOnly) continue; //如果非必要檔案則不下載 (目前RWL僅支援客戶端安裝) - - List filePath = split(file['path']); - filePath[0] = InstanceRepository.getInstanceDir(instance.uuid).path; - String fileName = file["name"]; - installingState.downloadInfos.add(DownloadInfo(file["url"], - savePath: join(joinAll(filePath), fileName), - sh1Hash: file["sha1"], - hashCheck: true, onDownloaded: () { - setState(() { - downloadedFiles++; - installingState.nowEvent = I18n.format( - 'modpack.downloading.assets.progress', - args: {"downloaded": downloadedFiles, "total": totalFiles}); - }); - })); - - parsedFiles++; - - setState(() { - installingState.nowEvent = I18n.format( - 'modpack.getting.assets.progress', - args: {"parsed": parsedFiles, "total": totalFiles}); - }); - } - } - - Future _ready( - Map versionInfo, - packData, - ) async { - String versionID = versionInfo["targets"][1]["version"]; - String loaderID = versionInfo["targets"][0]["name"]; - String loaderVersionID = versionInfo["targets"][0]["version"]; - bool isFabric = loaderID.startsWith(ModLoader.fabric.name); - bool isForge = loaderID.startsWith(ModLoader.forge.name); - - if (isFabric) { - await FabricClient.createClient( - setState: setState, - meta: meta, - versionID: versionID, - loaderVersion: loaderVersionID, - instance: instance, - ); - } else if (isForge) { - await ForgeClient.createClient( - setState: setState, - meta: meta, - gameVersionID: versionID, - forgeVersionID: loaderVersionID, - instance: instance) - .then((ForgeClientState state) => state.handlerState( - navigator.context, setState, instance, - notFinal: true)); - } - - getFiles(versionInfo); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - setState(() {}); - }); - installingState.finish = true; - return this; - } -} diff --git a/lib/mod/curseforge/curseforge_handler.dart b/lib/mod/curseforge/curseforge_handler.dart deleted file mode 100644 index 51002f873..000000000 --- a/lib/mod/curseforge/curseforge_handler.dart +++ /dev/null @@ -1,161 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:archive/archive_io.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/screen/install_curseforge_modpack.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart' hide ModLoader; -import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; - -class CurseForgeHandler { - static Text parseReleaseType(CurseForgeFileReleaseType releaseType) { - switch (releaseType) { - case CurseForgeFileReleaseType.release: - return Text(I18n.format("edit.instance.mods.release"), - style: const TextStyle(color: Colors.lightGreen)); - case CurseForgeFileReleaseType.beta: - return Text(I18n.format("edit.instance.mods.beta"), - style: const TextStyle(color: Colors.lightBlue)); - case CurseForgeFileReleaseType.alpha: - return Text(I18n.format("edit.instance.mods.alpha"), - style: const TextStyle(color: Colors.red)); - } - } - - static Widget getAddonIconWidget(CurseForgeModLogo? logo) { - if (logo == null) return const Icon(Icons.image, size: 50); - - return Image.network( - logo.url, - width: 50, - height: 50, - fit: BoxFit.contain, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded.toInt() / - loadingProgress.expectedTotalBytes!.toInt() - : null, - ); - }, - ); - } - - static Future needUpdates( - int curseID, String versionID, ModLoader loader, int hash) async { - List files = - (await RPMTWApiClient.instance.curseforgeResource.getModFiles(curseID, - gameVersion: versionID, - modLoaderType: loader.toCurseForgeType())) - .where((e) => e.gameVersions.contains(versionID)) - .toList(); - - files = filterModFiles(files, versionID, loader); - - if (files.isEmpty) return null; - final file = files.first; - if (file.fileFingerprint != hash) { - return file; - } - - return null; - } - - static Widget installModpack(File modpackFile, [String? iconUrl]) { - final Widget error = AlertDialog( - contentPadding: const EdgeInsets.all(16.0), - title: Text(I18n.format("gui.error.info")), - content: I18nText("modpack.error.format"), - actions: [ - TextButton( - child: Text(I18n.format("gui.ok")), - onPressed: () { - Navigator.pop(navigator.context); - }, - ) - ]); - - try { - return FutureBuilder( - future: Util.unZip(modpackFile), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - final Archive? archive = snapshot.data; - if (archive == null) { - return error; - } - - ArchiveFile? manifestFile = archive.findFile("manifest.json"); - - if (manifestFile != null) { - Map manifest = json.decode( - const Utf8Decoder(allowMalformed: true) - .convert(manifestFile.content)); - - return WillPopScope( - onWillPop: () => Future.value(false), - child: InstallCurseForgeModpack( - manifest: manifest, archive: archive, iconUrl: iconUrl), - ); - } else { - return error; - } - } else { - return AlertDialog( - title: I18nText("modpack.parsing"), - content: Column( - mainAxisSize: MainAxisSize.min, - children: const [ - RWLLoading(), - ], - )); - } - }); - } on FormatException { - return const RWLLoading(); - } - } - - static List filterModFiles( - List files, String gameVersion, ModLoader loader, - {bool strict = true}) { - List result = []; - - for (final file in files) { - bool isValid = CurseForgeHandler.filterVersion( - file.gameVersions, gameVersion, loader, strict); - - if (isValid) result.add(file); - } - - // if strict filtering mode cannot find any files, then use non-strict filtering mode - if (result.isEmpty && strict) { - return filterModFiles(files, gameVersion, loader, strict: false); - } - - return result; - } - - static bool filterVersion(List versions, String gameVersion, - ModLoader loader, bool strict) { - // mod loader name the first char is capitalized - // eg. forge -> Forge, fabric -> Fabric etc. - String loaderName = loader.name.toCapitalized(); - - if (strict && - versions.contains(gameVersion) && - versions.contains(loaderName)) { - return true; - } else if (!strict && versions.any((v) => v.contains(gameVersion))) { - return true; - } else { - return false; - } - } -} diff --git a/lib/mod/curseforge/curseforge_modapck_client.dart b/lib/mod/curseforge/curseforge_modapck_client.dart deleted file mode 100644 index 767241e17..000000000 --- a/lib/mod/curseforge/curseforge_modapck_client.dart +++ /dev/null @@ -1,175 +0,0 @@ -import 'dart:io'; - -import 'package:quiver/iterables.dart'; -import 'package:rpmlauncher/launcher/Fabric/FabricClient.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeClient.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/launcher/MinecraftClient.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:archive/archive.dart'; -import 'package:path/path.dart' as path; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart' hide ModLoader; - -class CurseForgeModpackClient extends MinecraftClient { - int totalAddonFiles = 0; - int parsedAddonFiles = 0; - int downloadedAddonFiles = 0; - - @override - MinecraftClientHandler handler; - - CurseForgeModpackClient._init({required this.handler}); - - static Future createClient( - {required MinecraftMeta meta, - required Map manifest, - required Instance instance, - required setState, - required Archive archive}) async { - InstanceConfig config = instance.config; - - return await CurseForgeModpackClient._init( - handler: MinecraftClientHandler( - meta: meta, - versionID: config.version, - instance: instance, - setState: setState, - ), - )._ready(meta, manifest, archive); - } - - Future getFileInfo(Map file, String instanceUUID) async { - int projectID = file['projectID']; - int fileID = file['fileID']; - bool required = file['required'] ?? true; - if (!required) return; // skip optional files - - CurseForgeModFile? fileInfo; - try { - fileInfo = await RPMTWApiClient.instance.curseforgeResource - .getModFile(projectID, fileID); - } catch (e) { - fileInfo = null; - } - - if (fileInfo != null) { - String fileName = fileInfo.fileName; - Directory? filePath; - - if (path.extension(fileName) == '.jar') { - // The file maybe is mod - filePath = InstanceRepository.getModRootDir(instanceUUID); - } else if (path.extension(fileName) == '.zip') { - // The file maybe is a resourcepack - filePath = InstanceRepository.getResourcePackRootDir(instanceUUID); - } else { - logger.error(ErrorType.modpack, 'Unknown file type: $fileName'); - return; - } - - installingState.downloadInfos.add(DownloadInfo(fileInfo.downloadUrl, - savePath: path.join(filePath.path, fileInfo.fileName), - onDownloaded: () { - setState(() { - downloadedAddonFiles++; - installingState.nowEvent = - I18n.format('modpack.downloading.assets.progress', args: { - 'downloaded': downloadedAddonFiles, - 'total': totalAddonFiles - }); - }); - })); - } else { - logger.error(ErrorType.modpack, - 'Cannot find file from CurseForge (modId: $projectID, fileId: $fileID)'); - } - - parsedAddonFiles++; - - setState(() { - installingState.nowEvent = I18n.format('modpack.getting.assets.progress', - args: {'parsed': parsedAddonFiles, 'total': totalAddonFiles}); - }); - } - - Future getAddonFiles(Map manifest, String instanceUUID) async { - List addonFiles = manifest['files'].cast(); - - totalAddonFiles = addonFiles.length; - - final queue = partition(addonFiles, 15).toList(); - // Async queue to get addon files info - for (final item in queue) { - await Future.wait(item.map((file) => getFileInfo(file, instanceUUID))); - } - } - - Future overrides( - Map packMeta, String instanceUUID, Archive packArchive) async { - final String overridesDir = packMeta['overrides']; - final String instanceDir = - InstanceRepository.getInstanceDir(instanceUUID).path; - - for (ArchiveFile file in packArchive) { - final String path = - instanceDir + Util.split(file.name, overridesDir, max: 1).join(''); - - if (file.toString().startsWith(overridesDir)) { - final data = file.content as List; - if (file.isFile) { - File(path) - ..createSync(recursive: true) - ..writeAsBytes(data); - } else { - Directory(path).create(recursive: true); - } - } - } - } - - Future _ready( - MinecraftMeta meta, Map manifest, Archive archive) async { - final InstanceConfig config = handler.instance.config; - final ModLoader loader = config.loaderEnum; - - if (loader == ModLoader.fabric) { - await FabricClient.createClient( - setState: setState, - meta: meta, - versionID: config.version, - loaderVersion: config.loaderVersion!, - instance: handler.instance); - } else { - await ForgeClient.createClient( - setState: setState, - meta: meta, - gameVersionID: config.version, - forgeVersionID: config.loaderVersion!, - instance: handler.instance) - .then((ForgeClientState state) => state.handlerState( - navigator.context, setState, instance, - notFinal: true)); - } - - installingState.nowEvent = I18n.format('modpack.getting.assets'); - setState(() {}); - await getAddonFiles(manifest, config.uuid); - await installingState.downloadInfos.downloadAll( - onReceiveProgress: (progress) { - setState(() {}); - }); - installingState.nowEvent = I18n.format('modpack.downloading.assets'); - await overrides(manifest, config.uuid, archive); - - installingState.finish = true; - return this; - } -} diff --git a/lib/mod/mod_loader.dart b/lib/mod/mod_loader.dart deleted file mode 100644 index f39eb9678..000000000 --- a/lib/mod/mod_loader.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:hive/hive.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart'; - -import '../i18n/i18n.dart'; - -part 'mod_loader.g.dart'; - -@HiveType(typeId: 1) -enum ModLoader { - @HiveField(0) - vanilla, - @HiveField(1) - fabric, - @HiveField(2) - forge, - @HiveField(3) - paper, - @HiveField(4) - unknown; - - const ModLoader(); - - String get fixedString => name; - - String get i18nString { - switch (this) { - case ModLoader.vanilla: - return I18n.format("version.list.mod.loader.vanilla"); - case ModLoader.fabric: - return I18n.format("version.list.mod.loader.fabric"); - case ModLoader.forge: - return I18n.format("version.list.mod.loader.forge"); - case ModLoader.paper: - return I18n.format("version.list.mod.loader.paper"); - default: - return I18n.format("version.list.mod.loader.unknown"); - } - } - - List supportedSides() { - switch (this) { - case ModLoader.vanilla: - return [MinecraftSide.client, MinecraftSide.server]; - case ModLoader.fabric: - return [MinecraftSide.client, MinecraftSide.server]; - case ModLoader.forge: - return [MinecraftSide.client]; - case ModLoader.paper: - return [MinecraftSide.server]; - default: - return [MinecraftSide.client]; - } - } - - bool supportInstall() { - switch (this) { - case ModLoader.vanilla: - return true; - case ModLoader.fabric: - return true; - case ModLoader.forge: - return true; - case ModLoader.paper: - return true; - default: - return false; - } - } - - CurseForgeModLoaderType? toCurseForgeType() { - try { - return CurseForgeModLoaderType.values.byName(name); - } catch (e) { - return null; - } - } -} - -class ModLoaderUttily { - static ModLoader getByIndex(index) { - switch (index) { - case 0: - return ModLoader.vanilla; - case 1: - return ModLoader.fabric; - case 2: - return ModLoader.forge; - case 3: - return ModLoader.paper; - default: - return ModLoader.vanilla; - } - } - - static ModLoader getByString(String loader) { - try { - return ModLoader.values.byName(loader); - } catch (e) { - return ModLoader.vanilla; - } - } - - static ModLoader getByI18nString(String modLoaderName) { - return ModLoaderUttily.getByIndex(ModLoader.values - .map((e) => e.i18nString) - .toList() - .indexOf(modLoaderName)); - } - - static int getIndexByLoader(ModLoader loader) { - switch (loader) { - case ModLoader.vanilla: - return 0; - case ModLoader.fabric: - return 1; - case ModLoader.forge: - return 2; - case ModLoader.paper: - return 3; - default: - return 0; - } - } -} diff --git a/lib/mod/mod_loader.g.dart b/lib/mod/mod_loader.g.dart deleted file mode 100644 index b6c6bfeaf..000000000 --- a/lib/mod/mod_loader.g.dart +++ /dev/null @@ -1,61 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'mod_loader.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class ModLoaderAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - ModLoader read(BinaryReader reader) { - switch (reader.readByte()) { - case 0: - return ModLoader.vanilla; - case 1: - return ModLoader.fabric; - case 2: - return ModLoader.forge; - case 3: - return ModLoader.paper; - case 4: - return ModLoader.unknown; - default: - return ModLoader.vanilla; - } - } - - @override - void write(BinaryWriter writer, ModLoader obj) { - switch (obj) { - case ModLoader.vanilla: - writer.writeByte(0); - break; - case ModLoader.fabric: - writer.writeByte(1); - break; - case ModLoader.forge: - writer.writeByte(2); - break; - case ModLoader.paper: - writer.writeByte(3); - break; - case ModLoader.unknown: - writer.writeByte(4); - break; - } - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is ModLoaderAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/mod/modrinth_handler.dart b/lib/mod/modrinth_handler.dart deleted file mode 100644 index 98b3cb4db..000000000 --- a/lib/mod/modrinth_handler.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; - -class ModrinthHandler { - static Future> getModList(String versionID, String loader, - TextEditingController search, List beforeModList, int index, sort) async { - String searchFilter = ''; - if (search.text.isNotEmpty) { - searchFilter = '&query=${search.text}'; - } - List modList = beforeModList; - Response response = await RPMHttpClient().get( - '$modrinthAPI/search?facets=[["versions:$versionID"],["categories:$loader"],["project_type:mod"]]$searchFilter&offset=${20 * index}&limit=20&index=$sort'); - Map body = RPMHttpClient.json(response); - modList.addAll(body['hits']); - return modList; - } - - static Future> getModFilesInfo( - modrinthID, versionID, loader) async { - Response response = - await RPMHttpClient().get('$modrinthAPI/project/$modrinthID/version'); - late List filesInfo = []; - - List modVersions = RPMHttpClient.json(response).cast(); - modVersions.forEach((versions) { - if (versions['game_versions'].any((element) => element == versionID) && - versions['loaders'].any((element) => element == loader)) { - filesInfo.add(versions); - } - }); - return filesInfo; - } - - static Text parseReleaseType(String releaseType) { - late Text releaseTypeString; - if (releaseType == 'release') { - releaseTypeString = Text(I18n.format('edit.instance.mods.release'), - style: const TextStyle(color: Colors.lightGreen)); - } else if (releaseType == 'beta') { - releaseTypeString = Text(I18n.format('edit.instance.mods.beta'), - style: const TextStyle(color: Colors.lightBlue)); - } else if (releaseType == 'alpha') { - releaseTypeString = Text(I18n.format('edit.instance.mods.alpha'), - style: const TextStyle(color: Colors.red)); - } - return releaseTypeString; - } - - static Text parseSide(String sideString, String side, Map data) { - Text parse(sideName, type) { - late final Text sideText; - if (type == 'required') { - sideText = Text( - sideName + I18n.format('edit.instance.mods.side.required'), - style: const TextStyle(color: Colors.red), - ); - } else if (type == 'optional') { - sideText = Text( - sideName + I18n.format('edit.instance.mods.side.optional'), - style: const TextStyle(color: Colors.lightGreenAccent), - ); - } else if (type == 'unsupported') { - sideText = Text( - sideName + I18n.format('edit.instance.mods.side.unsupported'), - style: const TextStyle(color: Colors.grey), - ); - } - - return sideText; - } - - return parse(sideString, data[side]); - } -} diff --git a/lib/model/Game/FabricInstallerVersion.dart b/lib/model/Game/FabricInstallerVersion.dart deleted file mode 100644 index 7bb89f0a2..000000000 --- a/lib/model/Game/FabricInstallerVersion.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'dart:collection'; -import 'dart:convert'; - -class FabricInstallerVersions extends ListMixin { - List _versions; - - FabricInstallerVersions( - this._versions, - ); - - factory FabricInstallerVersions.fromJson(String str) => - FabricInstallerVersions.fromList( - json.decode(str).cast>()); - - String toJson() => json.encode(toList()); - - static FabricInstallerVersions fromList(List> list) => - FabricInstallerVersions( - list.map((e) => FabricInstallerVersion.fromMap(e)).toList(), - ); - - @override - int get length => _versions.length; - - @override - set length(int newLength) { - _versions.length = newLength; - } - - @override - FabricInstallerVersion operator [](int index) { - return _versions[index]; - } - - @override - void operator []=(int index, FabricInstallerVersion value) { - _versions[index] = value; - } -} - -class FabricInstallerVersion { - final String url; - final String maven; - final String version; - final bool stable; - FabricInstallerVersion({ - required this.url, - required this.maven, - required this.version, - required this.stable, - }); - - FabricInstallerVersion copyWith({ - String? url, - String? maven, - String? version, - bool? stable, - }) { - return FabricInstallerVersion( - url: url ?? this.url, - maven: maven ?? this.maven, - version: version ?? this.version, - stable: stable ?? this.stable, - ); - } - - Map toMap() { - return { - 'url': url, - 'maven': maven, - 'version': version, - 'stable': stable, - }; - } - - factory FabricInstallerVersion.fromMap(Map map) { - return FabricInstallerVersion( - url: map['url'] ?? '', - maven: map['maven'] ?? '', - version: map['version'] ?? '', - stable: map['stable'] ?? false, - ); - } - - String toJson() => json.encode(toMap()); - - factory FabricInstallerVersion.fromJson(String source) => - FabricInstallerVersion.fromMap(json.decode(source)); - - @override - String toString() { - return 'FabricInstallerVersion(url: $url, maven: $maven, version: $version, stable: $stable)'; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is FabricInstallerVersion && - other.url == url && - other.maven == maven && - other.version == version && - other.stable == stable; - } - - @override - int get hashCode { - return url.hashCode ^ maven.hashCode ^ version.hashCode ^ stable.hashCode; - } -} diff --git a/lib/model/Game/GameLogs.dart b/lib/model/Game/GameLogs.dart deleted file mode 100644 index 6f232eda6..000000000 --- a/lib/model/Game/GameLogs.dart +++ /dev/null @@ -1,251 +0,0 @@ -import 'dart:collection'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; - -enum GameLogType { info, warn, error, debug, fatal, unknown } - -extension GameLogTypeExtra on GameLogType { - Widget getText() { - late Widget text; - - switch (this) { - case GameLogType.info: - text = Text( - I18n.format('log.type.info'), - style: const TextStyle( - color: Colors.lightGreen, - ), - textAlign: TextAlign.center, - ); - break; - case GameLogType.warn: - text = Text( - I18n.format('log.type.warn'), - style: TextStyle(color: Colors.orange.shade500), - textAlign: TextAlign.center, - ); - break; - case GameLogType.error: - text = Text( - I18n.format('log.type.error'), - style: const TextStyle( - color: Colors.red, - ), - textAlign: TextAlign.center, - ); - break; - case GameLogType.debug: - text = Text(I18n.format('log.type.debug'), - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.deepPurple, - )); - break; - case GameLogType.fatal: - text = Text( - I18n.format('log.type.fatal'), - textAlign: TextAlign.center, - style: TextStyle(color: Colors.red.shade800), - ); - break; - case GameLogType.unknown: - text = Text( - I18n.format('log.type.unknown'), - textAlign: TextAlign.center, - style: const TextStyle(color: Colors.grey), - ); - break; - } - - return text; - } -} - -class GameLogs extends ListBase { - List _list; - - GameLogs(this._list); - - @override - GameLog operator [](int index) => _list[index]; - - @override - void operator []=(int index, GameLog value) { - _list[index] = value; - } - - @override - int get length => _list.length; - - @override - set length(int newLength) { - _list.length = newLength; - } - - @override - GameLogs toList({bool growable = true}) => - GameLogs(_list.toList(growable: growable)); - - @override - GameLogs getRange(int start, int end, {bool growable = true}) => - GameLogs(_list.getRange(start, end).toList()); - - GameLogs whereLog(bool Function(GameLog element) test) { - return GameLogs(super.where(test).toList()); - } - - void addLog(String source) { - List lines = LineSplitter.split(source).toList(); - for (final String line in lines) { - _list.add(GameLog.format(line)); - } - } - - void addLogs(List sources) { - sources.forEach((source) => _list.add(GameLog.format(source))); - } - - String toLogString() { - return _list.map((e) => e.formattedString).join(); - } - - factory GameLogs.empty() => GameLogs([]); -} - -class GameLog { - final String source; - final GameLogType type; - final DateTime time; - final String? formattedString; - final String thread; - final Widget widget; - - const GameLog(this.source, this.type, this.time, this.thread, - {this.widget = const SizedBox(), this.formattedString}); - - static GameLogType parseType(String source) { - source = getInfoString(source).split('/')[1]; - switch (source) { - case 'INFO': - return GameLogType.info; - case 'WARN': - return GameLogType.warn; - case 'ERROR': - return GameLogType.error; - case 'DEBUG': - return GameLogType.debug; - case 'FATAL': - return GameLogType.fatal; - default: - return GameLogType.unknown; - } - } - - static String getTimeString(String source) { - return source.split('[')[1].split(']')[0]; - } - - static String getInfoString(String source) { - return source.split('[')[2].split(']')[0]; - } - - static DateTime _parseTime(String source) { - String timeString = getTimeString(source); - int hour = int.parse(timeString.split(':')[0]); - int minute = int.parse(timeString.split(':')[1]); - int second = int.parse(timeString.split(':')[2]); - DateTime now = DateTime.now(); - return DateTime(now.year, now.month, now.day, hour, minute, second); - } - - static String _parseSource(String source) { - return source - .split('[${getTimeString(source)}]')[1] - .split('[${getInfoString(source)}]: ') - .join() - .trimLeft(); - } - - static String _parseThread(String source) { - return getInfoString(source).split('/')[0]; - } - - factory GameLog.format(String source) { - try { - DateTime time = _parseTime(source); - String thread = _parseThread(source); - GameLogType type = parseType(source); - String formattedString = _parseSource(source); - - return GameLog(source, type, time, thread, - formattedString: formattedString, - widget: LogView( - source: source, - thread: thread, - time: time, - type: type, - formattedString: formattedString)); - } catch (e) { - return GameLog(source, GameLogType.unknown, DateTime.now(), "unknown"); - } - } -} - -class LogView extends StatelessWidget { - const LogView({ - Key? key, - required this.source, - required this.thread, - required this.time, - required this.type, - required this.formattedString, - }) : super(key: key); - - final String source; - final String thread; - final DateTime time; - final GameLogType type; - final String? formattedString; - - @override - Widget build(BuildContext context) { - return ListTile( - minLeadingWidth: 320, - leading: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: 120, - child: Text( - thread, - style: TextStyle(color: Colors.lightBlue.shade300), - textAlign: TextAlign.center, - ), - ), - SizedBox( - width: 100, - height: 25, - child: Text( - DateFormat.jms(Platform.localeName).format(time), - textAlign: TextAlign.center, - ), - ), - SizedBox( - width: 100, - child: type.getText(), - ), - ], - ), - title: SelectableText( - formattedString ?? source, - style: const TextStyle(fontSize: 15), - // fontFamily: 'mono', - // 由於修改字體會導致框架約束錯誤,目前尚未找到問題來源 - ), - ); - } -} diff --git a/lib/model/Game/Libraries.dart b/lib/model/Game/Libraries.dart deleted file mode 100644 index 7417c2888..000000000 --- a/lib/model/Game/Libraries.dart +++ /dev/null @@ -1,390 +0,0 @@ -import 'dart:collection'; -import 'dart:convert'; -import 'dart:core'; -import 'dart:io'; - -import 'package:path/path.dart'; -import 'package:pub_semver/pub_semver.dart'; - -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/util.dart'; - -class Libraries extends ListBase { - final List _libraries; - - Libraries(this._libraries); - - factory Libraries.fromList(List libraries) { - return Libraries(libraries.map((e) => Library.fromMap(e)).toList()); - } - - @override - String toString() => json.encode(toList()); - - List> toJson() => - _libraries.map((library) => library.toJson()).toList(); - - List getLibrariesFiles() { - final List files = []; - final List needLibraries = - _libraries.where((library) => library.need).toList(); - - /// 處理重複的函式庫並保留最新版本 - final List librariesName = - needLibraries.map((e) => e.packageName).toList(); - final Set librariesNameSet = librariesName.toSet(); - - if (librariesName.length > librariesNameSet.length) { - librariesNameSet.forEach((name) { - List duplicateLibraries = - needLibraries.where((lib) => lib.packageName == name).toList(); - - /// Detected duplicate libraries - if (duplicateLibraries.length > 1) { - Library keepLibrary = (duplicateLibraries - ..sort((a, b) => - a.comparableVersion.compareTo(b.comparableVersion))) - .last; - - List needDeleteLibraries = - duplicateLibraries.where((lib) => lib != keepLibrary).toList(); - needDeleteLibraries.forEach(needLibraries.remove); - } - }); - } - - needLibraries.forEach((Library library) { - Artifact? artifact = library.downloads.artifact; - if (artifact != null) { - if (artifact.localFile.existsSync() || kTestMode) { - files.add(artifact.localFile); - } - } - }); - - files.toSet().toList(); - - return files; - } - - String getLibrariesLauncherArgs(File? clientJar) { - List files = [ - ...(clientJar != null ? [clientJar] : []) - ]; - files.addAll(getLibrariesFiles()); - return files.map((File file) => file.path).join(Util.getLibrarySeparator()); - } - - @override - int get length => _libraries.length; - - @override - set length(int newLength) { - _libraries.length = newLength; - } - - @override - Library operator [](int index) { - return _libraries[index]; - } - - @override - void operator []=(int index, Library value) { - _libraries[index] = value; - } - - @override - void add(Library element) { - _libraries.add(element); - } -} - -class Library { - final String name; - final LibraryDownloads downloads; - final LibraryRules? rules; - final LibraryNatives? natives; - bool get need => parseLibRule() || (natives != null && (natives!.isNatives)); - - String get packageName { - return name.replaceAll(':$version', ''); - } - - String get version => name.split(':').last; - - Version get comparableVersion { - try { - return Version.parse(version); - } catch (e) { - try { - return Version.parse('$version.0'); - } catch (e) { - return Version.none; - } - } - } - - const Library( - {required this.name, required this.downloads, this.rules, this.natives}); - - factory Library.fromMap(Map map) { - if (map['rules'] is LibraryRules) { - map['rules'] = (map['rules'] as LibraryRules).toMap(); - } - - return Library( - name: map['name'], - rules: map['rules'] != null ? LibraryRules.fromJson(map['rules']) : null, - downloads: LibraryDownloads.fromJson(map['downloads']), - natives: map['natives'] != null - ? LibraryNatives.fromJson(map['natives']) - : null, - ); - } - - bool parseLibRule() { - bool need = true; - if (rules != null) { - if (rules!.isNotEmpty) { - rules!.forEach((rule) { - if (rule.features != null) { - need = false; - return; - } - if (rule.os?['name'] == Util.getMinecraftFormatOS() || - rule.os == null) { - if (rule.action == 'allow') { - need = true; - } else if (rule.action == 'disallow') { - need = false; - } - } else if (rule.os?['name'] != Util.getMinecraftFormatOS()) { - if (rule.action == 'allow') { - need = false; - } - } - }); - } - } - return need; - } - - Map toJson() { - Map map = { - 'name': name, - 'downloads': downloads.toJson(), - }; - if (natives != null) map['natives'] = natives!.toMap(); - if (rules != null) map['rules'] = rules?.map((e) => e.toMap()).toList(); - return map; - } - - @override - String toString() => json.encode(toJson()); - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is Library && - other.name == name && - other.downloads == downloads && - other.natives == natives; - } - - @override - int get hashCode => name.hashCode ^ downloads.hashCode ^ natives.hashCode; -} - -class LibraryDownloads { - final Artifact? artifact; - final Classifiers? classifiers; - - const LibraryDownloads({required this.artifact, this.classifiers}); - - factory LibraryDownloads.fromJson(Map json) { - Classifiers? classifiers; - Artifact? artifact; - - dynamic classifiersMap = json['classifiers']; - - if (classifiersMap is Map && - (classifiersMap.containsKey('natives-${Platform.operatingSystem}') || - classifiersMap - .containsKey('natives-${Util.getMinecraftFormatOS()}'))) { - classifiers = Classifiers.fromJson(json['classifiers']); - } - - if (json['artifact'] != null && json['artifact'] is Map) { - artifact = Artifact.fromJson(json['artifact']); - } - - return LibraryDownloads(artifact: artifact, classifiers: classifiers); - } - - Map toJson() { - Map map = {}; - - if (artifact != null) map['artifact'] = artifact!.toJson(); - if (classifiers != null) map['classifiers'] = classifiers!.toJson(); - - return map; - } -} - -class LibraryRules extends ListBase { - final List rules; - - LibraryRules({ - required this.rules, - }); - - factory LibraryRules.fromJson(List list) { - List rules = []; - list.forEach((rule) { - rules.add(LibraryRule.fromMap(rule)); - }); - return LibraryRules(rules: rules); - } - - List toMap() => rules.map((e) => e.toMap()).toList(); - - @override - int get length => rules.length; - - @override - set length(int length) => rules.length = length; - - @override - LibraryRule operator [](int index) { - return rules[index]; - } - - @override - void operator []=(int index, LibraryRule value) { - rules[index] = value; - } -} - -class LibraryRule { - final String action; - final Map? os; - final Map? features; - - const LibraryRule({required this.action, this.os, this.features}); - - factory LibraryRule.fromMap(Map json) => LibraryRule( - action: json['action'], os: json['os'], features: json['features']); - - Map toMap() { - return { - 'action': action, - if (os != null) 'os': os, - if (features != null) 'features': features, - }; - } -} - -class LibraryNatives { - final bool isWindows; - final bool isLinux; - final bool isOSX; //osx -> macos - bool get isNatives { - if (Platform.isLinux) { - return isLinux; - } else if (Platform.isWindows) { - return isWindows; - } else if (Platform.isMacOS) { - return isOSX; - } else { - return false; - } - } - - const LibraryNatives({ - this.isWindows = false, - this.isLinux = false, - this.isOSX = false, - }); - - factory LibraryNatives.fromJson(Map json) => LibraryNatives( - isWindows: json.containsKey('windows'), - isLinux: json.containsKey('linux'), - isOSX: json.containsKey('osx')); - - Map toMap() { - Map json = {}; - if (isWindows) { - json['windows'] = 'natives-windows'; - } - if (isLinux) { - json['linux'] = 'natives-linux'; - } - if (isOSX) { - json['osx'] = 'natives-macos'; - } - return json; - } - - String toJson() => json.encode(toMap()); -} - -class Artifact { - final String path; //file save path - final String url; // file download url - final String? sha1; //file sha1 hash - final int? size; //File size in bytes - - File get localFile => - File(join(GameRepository.getLibraryGlobalDir().path, path)); - - const Artifact({ - required this.path, - required this.url, - this.sha1, - this.size, - }); - - factory Artifact.fromJson(Map json) => Artifact( - path: json['path'], - url: json['url'], - sha1: json['sha1'], - size: json['size']); - - Map toJson() => - {'path': path, 'url': url, 'sha1': sha1, 'size': size}; -} - -class Classifiers { - final String path; //file save path - final String url; // file download url - final String sha1; //file sha1 hash - final int size; //File size in bytes - - const Classifiers( - {required this.path, - required this.url, - required this.sha1, - required this.size}); - - factory Classifiers.fromJson(Map json) { - Map systemNatives = json['natives-${Platform.operatingSystem}'] ?? - json['natives-${Util.getMinecraftFormatOS()}']; - - return Classifiers( - path: systemNatives['path'], - url: systemNatives['url'], - sha1: systemNatives['sha1'], - size: systemNatives['size']); - } - - Map toJson() => { - 'natives-${Platform.operatingSystem}': { - 'path': path, - 'url': url, - 'sha1': sha1, - 'size': size - } - }; -} diff --git a/lib/model/Game/MinecraftMeta.dart b/lib/model/Game/MinecraftMeta.dart deleted file mode 100644 index b7ec5e1c1..000000000 --- a/lib/model/Game/MinecraftMeta.dart +++ /dev/null @@ -1,22 +0,0 @@ -class MinecraftMeta { - Map rawMeta; - - MinecraftMeta(this.rawMeta); - - operator [](String key) => rawMeta[key]; - operator []=(String key, dynamic value) => rawMeta[key] = value; - - int get javaVersion { - try { - if (rawMeta.containsKey("javaVersion")) { - return rawMeta["javaVersion"]["majorVersion"]; - } else { - return 8; - } - } catch (e) { - return 8; - } - } - - bool containsKey(String key) => rawMeta.containsKey(key); -} diff --git a/lib/model/Game/MinecraftNews.dart b/lib/model/Game/MinecraftNews.dart deleted file mode 100644 index 62590dcaa..000000000 --- a/lib/model/Game/MinecraftNews.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:collection'; - -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; - -class MinecraftNews extends ListBase { - final List news; - - MinecraftNews(this.news); - - factory MinecraftNews.formMap(Map json) { - List news = - json['article_grid'].map((e) => MinecraftNew.formMap(e)).toList().cast(); - - return MinecraftNews(news); - } - - static Future fromWeb() async { - final response = await RPMHttpClient().get(minecraftNewsJson); - return MinecraftNews.formMap(response.data); - } - - @override - int get length => news.length; - @override - set length(int length) => news.length = length; - @override - MinecraftNew operator [](int index) { - return news[index]; - } - - @override - void operator []=(int index, MinecraftNew value) { - news[index] = value; - } -} - -class MinecraftNew { - final String title; - final String link; - final String description; - final String imageUri; - - MinecraftNew(this.title, this.link, this.description, this.imageUri); - - factory MinecraftNew.formMap(Map map) { - final Map titlePart = map['preferred_tile'] ?? map['default_tile']; - - return MinecraftNew( - titlePart['title'], - 'https://www.minecraft.net${map['article_url']}', - titlePart['sub_header'], - 'https://www.minecraft.net${titlePart['image']['imageURL']}'); - } -} diff --git a/lib/model/Game/MinecraftSide.dart b/lib/model/Game/MinecraftSide.dart deleted file mode 100644 index 2aa9b157b..000000000 --- a/lib/model/Game/MinecraftSide.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:rpmlauncher/i18n/i18n.dart'; - -enum MinecraftSide { client, server } - -extension MinecraftSideExtension on MinecraftSide { - String get i18nName { - switch (this) { - case MinecraftSide.client: - return I18n.format('version.side.client'); - case MinecraftSide.server: - return I18n.format('version.side.server'); - } - } - - bool get isClient => this == MinecraftSide.client; - - bool get isServer => this == MinecraftSide.server; -} diff --git a/lib/model/Game/MinecraftVersion.dart b/lib/model/Game/MinecraftVersion.dart deleted file mode 100644 index e9ef1e5aa..000000000 --- a/lib/model/Game/MinecraftVersion.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/util.dart'; - -class MCVersionManifest { - String latestRelease; - - String? latestSnapshot; - - List versions; - - MCVersionManifest(this.latestRelease, this.versions, {this.latestSnapshot}); - - factory MCVersionManifest.fromJson(Map data) { - return MCVersionManifest( - data['latest']['release'], - (data['versions'] as List) - .map((d) => MCVersion.fromJson(d)) - // TODO: 支援 Minecraft 遠古版本 - .where((version) => version.comparableVersion >= Version(1, 7, 0)) - .toList(), - latestSnapshot: data['latest']['snapshot']); - } - - static Future getVanilla() async { - Response response = - await RPMHttpClient().get("$mojangMetaAPI/version_manifest_v2.json"); - return MCVersionManifest.fromJson(response.data); - } - - static Future getForge() async { - MCVersionManifest vanilla = await getVanilla(); - Response response = - await RPMHttpClient().get("$forgeFilesMainAPI/maven-metadata.json"); - - Map data = response.data; - List versions = []; - - data.keys.forEach((key) { - if (vanilla.versions.any((e) => e.id == key)) { - versions.add(vanilla.versions.firstWhere((e) => e.id == key)); - } - }); - - return MCVersionManifest( - data.keys.last, - versions.reversed.toList(), - ); - } - - static Future getFabric() async { - MCVersionManifest vanilla = await getVanilla(); - Response response = await RPMHttpClient().get("$fabricApi/versions/game"); - - List data = response.data.cast(); - List versions = []; - - data.forEach((e) { - if (vanilla.versions.any((e2) => e2.id == e['version'])) { - versions - .add(vanilla.versions.firstWhere((e2) => e2.id == e['version'])); - } - }); - - return MCVersionManifest( - data.firstWhere((e) => e['stable'] == true)['version'], versions, - latestSnapshot: - data.firstWhere((e) => e['stable'] == false)['version']); - } - - static Future formLoaderType(ModLoader loader) async { - switch (loader) { - case ModLoader.forge: - return getForge(); - case ModLoader.fabric: - return getFabric(); - case ModLoader.vanilla: - return getVanilla(); - default: - return getVanilla(); - } - } -} - -class MCVersion { - String id; - - MCVersionType type; - - String url; - - String time; - - String releaseTime; - - String sha1; - - int complianceLevel; - - DateTime get timeDateTime => DateTime.parse(time); - - DateTime get releaseDateTime => DateTime.parse(releaseTime); - - Version get comparableVersion => Util.parseMCComparableVersion(id); - - Future get meta async => - MinecraftMeta((await RPMHttpClient().get(url)).data); - - MCVersion(this.id, this.type, this.url, this.time, this.releaseTime, - this.sha1, this.complianceLevel); - - factory MCVersion.fromJson(Map json) { - return MCVersion( - json['id'], - MCVersionType.values.firstWhere((_) => _.name == json['type']), - json['url'], - json['time'], - json['releaseTime'], - json['sha1'], - json['complianceLevel']); - } -} - -enum MCVersionType { - release, - snapshot, - beta, - alpha, -} - -extension MCVersionTypeExtension on MCVersionType { - String get name { - switch (this) { - case MCVersionType.release: - return 'release'; - case MCVersionType.snapshot: - return 'snapshot'; - case MCVersionType.beta: - return 'old_beta'; - case MCVersionType.alpha: - return 'old_alpha'; - } - } -} diff --git a/lib/model/Game/RecommendedModpack.dart b/lib/model/Game/RecommendedModpack.dart deleted file mode 100644 index d3b78a360..000000000 --- a/lib/model/Game/RecommendedModpack.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'dart:collection'; - -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/util/util.dart'; - -class RecommendedModpacks with ListMixin { - List _list = []; - - RecommendedModpacks(this._list); - - @override - int get length => _list.length; - - @override - set length(int newLength) { - _list.length = newLength; - } - - @override - RecommendedModpack operator [](int index) { - return _list[index]; - } - - @override - void operator []=(int index, RecommendedModpack value) { - _list[index] = value; - } - - factory RecommendedModpacks.fromList(List> list) { - return RecommendedModpacks(list - .map((Map map) => RecommendedModpack.fromJson(map)) - .toList()); - } -} - -class RecommendedModpack { - final String name; - final String description; - final String image; - final String? link; - final Version version; - final RecommendedModpackType type; - final ModLoader loader; - final String loaderVersion; - - final List? mods; - final bool visible; - final int? curseforgeID; - - RecommendedModpack( - {required this.name, - required this.description, - required this.image, - this.link, - required this.version, - required this.type, - required this.loader, - required this.loaderVersion, - this.mods, - required this.visible, - this.curseforgeID}); - - factory RecommendedModpack.fromJson(Map json) { - return RecommendedModpack( - name: json['name'], - description: json['description'], - image: json['image'], - link: json['link'], - version: Util.parseMCComparableVersion(json['version']), - type: RecommendedModpackType.values.byName(json['type']), - loader: ModLoader.values.byName(json['loader']), - loaderVersion: json['loaderVersion'], - mods: json['mods']?.cast>(), - visible: json['visible'] ?? true, - curseforgeID: json['curseforgeID'], - ); - } -} - -enum RecommendedModpackType { - instance, - curseforgeModpack, -} diff --git a/lib/model/Game/instance.dart b/lib/model/Game/instance.dart deleted file mode 100644 index 4094d0cf6..000000000 --- a/lib/model/Game/instance.dart +++ /dev/null @@ -1,501 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:io/io.dart'; -import 'package:path/path.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/handler/window_handler.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/account/Account.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/IO/JsonStorage.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/IO/Properties.dart'; -import 'package:rpmlauncher/screen/account.dart'; -import 'package:rpmlauncher/screen/check_assets.dart'; -import 'package:rpmlauncher/screen/ms_oauth_login.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/dialog/agree_eula_dialog.dart'; -import 'package:rpmlauncher/widget/dialog/CheckDialog.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/DynamicImageFile.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; -import 'package:uuid/uuid.dart'; - -class Instance { - /// 安裝檔的名稱 - String get name => config.name; - - /// UUID of the instance - final String uuid; - - /// 安裝檔的設定物件 - final InstanceConfig config; - - /// 安裝檔的資料夾 - Directory get directory => InstanceRepository.getInstanceDir(uuid); - - /// 安裝檔的資料夾路徑 - String get path => directory.path; - - File? get imageFile { - File file = File(join(path, 'icon.png')); - if (file.existsSync()) { - return file; - } - return null; - } - - Widget imageWidget( - {double width = 64, double height = 64, bool expand = false}) { - Widget widget = Image.asset( - 'assets/images/Minecraft.png', - width: width, - height: height, - ); - - if (imageFile != null) { - try { - widget = DynamicImageFile( - imageFile: imageFile!, width: width, height: height); - } catch (e) {} - } else if (config.loaderEnum == ModLoader.forge) { - widget = Image.asset( - 'assets/images/Forge.jpg', - width: width, - height: height, - ); - } else if (config.loaderEnum == ModLoader.fabric) { - widget = Image.asset( - 'assets/images/Fabric.png', - width: width, - height: height, - ); - } else if (config.loaderEnum == ModLoader.unknown) { - widget = Stack( - alignment: Alignment.center, - children: [ - widget, - const Positioned( - right: 2, - top: 2, - child: Icon(Icons.error_sharp, size: 30, color: Colors.red), - ) - ], - ); - } - - if (!expand) { - widget = ClipRRect( - borderRadius: BorderRadius.circular(10), - child: widget, - ); - } - - return widget; - } - - Instance(this.uuid, this.config); - - static Instance? fromUUID(String uuid) { - InstanceConfig? config = InstanceConfig.fromUUID(uuid); - - if (config != null) { - return Instance(uuid, config); - } - return null; - } - - Future launch(BuildContext context) async { - if (!AccountStorage().hasAccount) { - return showDialog( - barrierDismissible: false, - context: context, - builder: (context) => AlertDialog( - title: Text(I18n.format('gui.error.info')), - content: Text(I18n.format('account.null')), - actions: [ - ElevatedButton( - onPressed: () { - AccountScreen.push(context); - }, - child: Text(I18n.format('gui.login'))) - ]), - ); - } else { - Account account = AccountStorage().getDefault()!; - - showDialog( - barrierDismissible: false, - context: context, - builder: (context) => FutureBuilder( - future: Util.validateAccount(account) - .timeout(const Duration(seconds: 15), onTimeout: () => false), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - if (!snapshot.data) { - //如果帳號已經過期並且嘗試自動更新失敗 - - return AlertDialog( - title: I18nText.errorInfoText(), - content: I18nText('account.refresh.microsoft.error'), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - showDialog( - barrierDismissible: false, - context: context, - builder: (context) => MSLoginWidget()); - }, - child: I18nText('account.again')) - ], - ); - } else { - //如果帳號未過期 - WidgetsBinding.instance.addPostFrameCallback((_) { - navigator.pop(); - Util.javaCheckDialog( - allJavaVersions: config.needJavaVersion, - hasJava: () async { - if (config.sideEnum.isServer) { - File eulaFile = File(join(path, 'eula.txt')); - if (!eulaFile.existsSync()) { - const Properties properties = Properties({ - 'eula': 'false', - }); - - eulaFile.writeAsStringSync( - Properties.encode(properties)); - } - - try { - Properties properties; - properties = Properties.decode( - eulaFile.readAsStringSync(encoding: utf8)); - bool agreeEula = - properties['eula'].toString().toBool(); - - if (!agreeEula) { - await showDialog( - context: navigator.context, - builder: (context) => AgreeEulaDialog( - properties: properties, - eulaFile: eulaFile)); - } - } on FileSystemException {} - } - - final context = navigator.context; - if (context.mounted) { - showDialog( - context: context, - builder: (context) => CheckAssetsScreen( - instanceDir: directory, - )); - } - }); - }); - - return const SizedBox.shrink(); - } - } else { - return const Center(child: RWLLoading()); - } - })); - } - } - - void openFolder() { - Util.openFileManager(directory); - } - - void edit(BuildContext context) { - WindowHandler.createSubWindow(context, '/instance/${basename(path)}/edit', - title: 'RPMLauncher - $name'); - } - - Future copy() async { - Future copyInstance() async { - String uuid = const Uuid().v4(); - - await copyPath(path, InstanceRepository.getInstanceDir(uuid).path); - - InstanceConfig newInstanceConfig = - InstanceRepository.instanceConfig(uuid)!; - - newInstanceConfig.storage['uuid'] = uuid; - newInstanceConfig.name = - '${newInstanceConfig.name} (${I18n.format('gui.copy')})'; - } - - showDialog( - context: navigator.context, - builder: (context) => FutureBuilder( - future: copyInstance(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: I18nText('gui.instance.copy.successful'), - actions: const [OkClose()], - ); - } else if (snapshot.hasError) { - return AlertDialog( - title: I18nText.errorInfoText(), - content: I18nText('gui.instance.copy.error'), - actions: const [OkClose()], - ); - } else { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText('gui.instance.copy.copying'), - const RWLLoading() - ], - ), - ); - } - })); - } - - Future delete() async { - showDialog( - context: navigator.context, - builder: (context) { - return CheckDialog( - title: I18n.format('gui.instance.delete'), - message: I18n.format('gui.instance.delete.tips'), - onPressedOK: (context) { - Navigator.of(context).pop(); - try { - directory.deleteSync(recursive: true); - } on FileSystemException { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: I18nText.errorInfoText(), - content: I18nText('gui.instance.delete.error'), - actions: const [OkClose()], - )); - } - }, - ); - }, - ); - } - - List getModFiles() { - return InstanceRepository.getModRootDir(uuid) - .listSync() - .where((file) => - extension(file.path, 2).contains('.jar') && - file.existsSync() && - file is File) - .toList(); - } -} - -/// 安裝檔設定類別 -class InstanceConfig { - late JsonStorage storage; - - /// 安裝檔案名稱 - String get name => storage['name'] ?? 'Name not found'; - - /// 安裝檔的UUID - String get uuid => storage['uuid']; - - String get side => storage['side']; - - MinecraftSide get sideEnum => MinecraftSide.values.byName(side); - - /// 安裝檔模組載入器,可以是 forge、fabric、vanilla、unknown - String get loader => storage['loader']; - - /// 模組載入器的枚舉值 [ModLoader] - ModLoader get loaderEnum => ModLoaderUttily.getByString(loader); - - /// 安裝檔的遊戲版本 - String get version => storage['version']; - - /// 安裝檔的資源檔案 ID - String get assetsID => storage['assets_id']; - - /// 可比較大小的遊戲版本 - Version get comparableVersion => Util.parseMCComparableVersion(version); - - /// 安裝檔的模組載入器版本 - String? get loaderVersion => storage['loader_version']; - - /// 安裝檔需要的Java版本,可以是 8/16/17 - int get javaVersion => storage['java_version']; - - List get needJavaVersion { - List javaVersions = []; - if (loaderEnum == ModLoader.forge) { - javaVersions.add(16); - } - if (!javaVersions.contains(javaVersion)) { - javaVersions.add(javaVersion); - } - return javaVersions; - } - - /// 安裝檔的遊玩時間,預設為 0 - int get playTime => storage['play_time'] ?? 0; - - /// 安裝檔最後遊玩的時間,預設為 null - int? get lastPlay => storage['last_play']; - - String get lastPlayLocalString => lastPlay == null - ? I18n.format('datas.found.not') - : Util.formatDate(DateTime.fromMillisecondsSinceEpoch(lastPlay!)); - - /// 安裝檔最多可以使用的記憶體,預設為 null - double? get javaMaxRam => storage['java_max_ram']; - - /// 安裝檔的JVM (Java 虛擬機器) 參數,預設為 null - List? get javaJvmArgs => storage['java_jvm_args']; - - Libraries get libraries => Libraries.fromList(storage['libraries'] ?? []); - - set name(String value) => storage.setItem('name', value); - set side(String value) => storage.setItem('side', value); - set loader(String value) => storage.setItem('loader', value); - set version(String value) => storage.setItem('version', value); - set loaderVersion(String? value) => storage.setItem('loader_version', value); - set javaVersion(int value) => storage.setItem('java_version', value); - set playTime(int? value) => storage.setItem('play_time', value ?? 0); - set lastPlay(int? value) => storage.setItem('last_play', value ?? 0); - set javaMaxRam(double? value) => storage.setItem('java_max_ram', value); - set javaJvmArgs(List? value) => - storage.setItem('java_jvm_args', value); - set libraries(Libraries value) => - storage.setItem('libraries', value.toJson()); - - InstanceConfig( - {required String name, - required MinecraftSide side, - required String loader, - required String version, - required int javaVersion, - required String uuid, - required String assetsID, - String? loaderVersion, - int? playTime, - int? lastPlay, - double? javaMaxRam, - List? javaJvmArgs, - Libraries? libraries}) { - storage = JsonStorage(InstanceRepository.instanceConfigFile(uuid)); - - storage['uuid'] = uuid; - - storage['name'] = name; - storage['side'] = side.name; - storage['loader'] = loader; - storage['version'] = version; - storage['loader_version'] = loaderVersion; - storage['java_version'] = javaVersion; - storage['play_time'] = playTime; - storage['last_play'] = lastPlay; - storage['java_max_ram'] = javaMaxRam; - storage['java_jvm_args'] = javaJvmArgs; - storage['libraries'] = (libraries ?? Libraries([])).toJson(); - storage['assets_id'] = assetsID; - } - - factory InstanceConfig.unknown([File? file]) { - String name = file == null ? 'unknown' : basename(file.parent.path); - return InstanceConfig( - name: name, - side: MinecraftSide.client, - loader: ModLoader.unknown.name, - version: '1.18.1', - javaVersion: 16, - libraries: Libraries.fromList([]), - uuid: name, - assetsID: '1.18', - ); - } - - /// 使用 安裝檔UUID來建立 [InstanceConfig] - static InstanceConfig? fromUUID(String instanceUUID) { - return InstanceConfig.fromFile( - InstanceRepository.instanceConfigFile(instanceUUID)); - } - - static InstanceConfig? fromFile(File file) { - late InstanceConfig config; - try { - String source; - try { - source = file.readAsStringSync(); - } catch (e) { - if (e is FileSystemException) { - /// 當遇到檔案錯誤時將跳過載入,回傳空的安裝檔 - return null; - } else { - /// 如果是其他錯誤將重新拋出錯誤交給上層例外處理 - rethrow; - } - } - - Map data = json.decode(source); - - config = InstanceConfig( - name: data['name'], - side: MinecraftSide.values - .byName(data['side'] ?? MinecraftSide.client.name), - loader: data['loader'], - version: data['version'], - loaderVersion: data['loader_version'], - javaVersion: data['java_version'], - playTime: data['play_time'], - lastPlay: data['last_play'], - javaMaxRam: data['java_max_ram'], - javaJvmArgs: data['java_jvm_args']?.cast(), - libraries: Libraries.fromList(data['libraries']), - uuid: basename(file.parent.path), - assetsID: data['assets_id'] ?? data['version'], - ); - } catch (e, stackTrace) { - logger.error(ErrorType.instance, e, stackTrace: stackTrace); - config = InstanceConfig.unknown(file); - - try { - config.storage.setItem('error', { - /// 新增安裝檔錯誤資訊 - 'stack_trace': stackTrace.toString(), - 'message': e.toString(), - 'source_instance_config': file.readAsStringSync() - }); - } catch (e) {} - - Future.delayed(Duration.zero, () { - showDialog( - context: navigator.context, - builder: (context) => AlertDialog( - title: - I18nText('gui.error.info', textAlign: TextAlign.center), - content: I18nText('instance.error.format', - args: {'error': e.toString()}, - textAlign: TextAlign.center), - actions: const [OkClose()], - )); - }); - } - return config; - } - - void createConfigFile() => storage.save(); -} diff --git a/lib/model/Game/mod_info.dart b/lib/model/Game/mod_info.dart deleted file mode 100644 index ea4c93802..000000000 --- a/lib/model/Game/mod_info.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -import 'package:path/path.dart'; -import 'package:pub_semver/pub_semver.dart'; - -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/widget/FileDeleteError.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart' hide ModLoader; - -part 'mod_info.g.dart'; - -@HiveType(typeId: 3) -class ModInfo extends HiveObject { - @HiveField(0) - final ModLoader loader; - @HiveField(1) - final String name; - @HiveField(2) - final String? description; - @HiveField(3) - final String? version; - @HiveField(4) - int? curseID; - @HiveField(5) - final List conflicts; - @HiveField(6) - final String namespace; - @HiveField(7) - final int murmur2Hash; - @HiveField(8) - final String md5Hash; - - @HiveField(9) - DateTime? lastUpdate; - @HiveField(10) - bool needsUpdate; - @HiveField(11) - Map? lastUpdateData; - - Future getImageWidget() async { - File imageFile = GameRepository.getModIconFile(md5Hash); - Widget image = const Icon(Icons.image, size: 50); - if (imageFile.existsSync()) { - image = Image.file(imageFile, fit: BoxFit.fill); - } else { - if (curseID != null) { - CurseForgeMod? mod; - try { - mod = - await RPMTWApiClient.instance.curseforgeResource.getMod(curseID!); - } catch (e) { - mod = null; - } - - CurseForgeModLogo? logo = mod?.logo; - if (logo != null) { - await RPMHttpClient().download(logo.url, imageFile.path); - image = Image.file(imageFile, fit: BoxFit.fill); - } - } - } - - return image; - } - - Future updating(Directory modDir, File file) async { - Response response = await RPMHttpClient().get( - lastUpdateData!['downloadUrl'], - options: Options(responseType: ResponseType.bytes)); - - File newFile = File(join(modDir.path, lastUpdateData!['fileName'])); - - await newFile.create(recursive: true); - newFile.writeAsBytesSync(response.data); - - if (newFile.path != file.path) { - await file.delete(recursive: true); - } - return true; - } - - ModInfo( - {required this.loader, - required this.name, - required this.description, - required this.version, - required this.curseID, - required this.conflicts, - required this.namespace, - required this.murmur2Hash, - required this.md5Hash, - this.lastUpdate, - this.needsUpdate = false, - this.lastUpdateData}); - - Future deleteMod(File file, {Function? onDeleting}) async { - bool deleted = false; - await showDialog( - context: navigator.context, - builder: (context) { - return _DeleteModWidget(file: file, onDeleting: onDeleting); - }, - ); - return deleted; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ModInfo && - other.loader == loader && - other.name == name && - other.description == description && - other.version == version && - other.curseID == curseID && - listEquals(other.conflicts, conflicts) && - other.namespace == namespace && - other.murmur2Hash == murmur2Hash && - other.md5Hash == md5Hash && - other.lastUpdate == lastUpdate && - other.needsUpdate == needsUpdate && - other.lastUpdateData == lastUpdateData; - } - - @override - int get hashCode { - return loader.hashCode ^ - name.hashCode ^ - description.hashCode ^ - version.hashCode ^ - curseID.hashCode ^ - conflicts.hashCode ^ - namespace.hashCode ^ - murmur2Hash.hashCode ^ - md5Hash.hashCode ^ - lastUpdate.hashCode ^ - needsUpdate.hashCode ^ - lastUpdateData.hashCode; - } -} - -class _DeleteModWidget extends StatelessWidget { - const _DeleteModWidget({ - Key? key, - required this.file, - this.onDeleting, - }) : super(key: key); - - final File file; - final Function? onDeleting; - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: I18nText("gui.tips.info"), - content: I18nText("edit.instance.mods.list.delete.check"), - actions: [ - TextButton( - child: Text(I18n.format("gui.cancel")), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: I18nText("gui.confirm"), - onPressed: () { - Navigator.of(context).pop(); - onDeleting?.call(); - try { - if (file.existsSync()) { - file.deleteSync(recursive: true); - } - } on FileSystemException { - showDialog( - context: context, - builder: (context) => const FileDeleteError()); - } - }) - ], - ); - } -} - -@HiveType(typeId: 2) -class ConflictMod { - @HiveField(0) - final String namespace; - @HiveField(1) - final String versionID; - - /// 如果版本號為 * 代表任何版本都會衝突 - /// Fabric 衝突模組版本號使用 Semver 的語意版本表達規範 - bool isConflict(ModInfo mod) { - if (versionID == "*" && mod.namespace == namespace) return true; - if (versionID == "*") return false; - try { - Version modVersion = Version.parse(mod.version!); - - VersionConstraint versionConstraint = VersionConstraint.parse(versionID); - - if (mod.namespace == namespace && - modVersion.allowsAll(versionConstraint)) { - return true; - } else { - return false; - } - } on FormatException { - return false; - } catch (e, stackTrace) { - logger.error(ErrorType.unknown, e, stackTrace: stackTrace); - return false; - } - } - - const ConflictMod({ - required this.namespace, - required this.versionID, - }); - - Map toMap() { - return { - 'namespace': namespace, - 'versionID': versionID, - }; - } - - factory ConflictMod.fromMap(Map map) { - return ConflictMod( - namespace: map['namespace'], - versionID: map['versionID'], - ); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is ConflictMod && - other.namespace == namespace && - other.versionID == versionID; - } - - @override - int get hashCode => namespace.hashCode ^ versionID.hashCode; -} diff --git a/lib/model/Game/mod_info.g.dart b/lib/model/Game/mod_info.g.dart deleted file mode 100644 index 5c7ff1c1d..000000000 --- a/lib/model/Game/mod_info.g.dart +++ /dev/null @@ -1,111 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'mod_info.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class ModInfoAdapter extends TypeAdapter { - @override - final int typeId = 3; - - @override - ModInfo read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return ModInfo( - loader: fields[0] as ModLoader, - name: fields[1] as String, - description: fields[2] as String?, - version: fields[3] as String?, - curseID: fields[4] as int?, - conflicts: (fields[5] as List).cast(), - namespace: fields[6] as String, - murmur2Hash: fields[7] as int, - md5Hash: fields[8] as String, - lastUpdate: fields[9] as DateTime?, - needsUpdate: fields[10] as bool, - lastUpdateData: (fields[11] as Map?)?.cast(), - ); - } - - @override - void write(BinaryWriter writer, ModInfo obj) { - writer - ..writeByte(12) - ..writeByte(0) - ..write(obj.loader) - ..writeByte(1) - ..write(obj.name) - ..writeByte(2) - ..write(obj.description) - ..writeByte(3) - ..write(obj.version) - ..writeByte(4) - ..write(obj.curseID) - ..writeByte(5) - ..write(obj.conflicts) - ..writeByte(6) - ..write(obj.namespace) - ..writeByte(7) - ..write(obj.murmur2Hash) - ..writeByte(8) - ..write(obj.md5Hash) - ..writeByte(9) - ..write(obj.lastUpdate) - ..writeByte(10) - ..write(obj.needsUpdate) - ..writeByte(11) - ..write(obj.lastUpdateData); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is ModInfoAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class ConflictModAdapter extends TypeAdapter { - @override - final int typeId = 2; - - @override - ConflictMod read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return ConflictMod( - namespace: fields[0] as String, - versionID: fields[1] as String, - ); - } - - @override - void write(BinaryWriter writer, ConflictMod obj) { - writer - ..writeByte(2) - ..writeByte(0) - ..write(obj.namespace) - ..writeByte(1) - ..write(obj.versionID); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is ConflictModAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/model/IO/JsonStorage.dart b/lib/model/IO/JsonStorage.dart deleted file mode 100644 index 97ec7f8c6..000000000 --- a/lib/model/IO/JsonStorage.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -class JsonStorage { - final File _file; - late Map _data; - - JsonStorage(this._file) { - if (!_file.existsSync()) { - _file.createSync(recursive: true); - _file.writeAsStringSync('{}'); - } - try { - _data = json.decode(_file.readAsStringSync()); - } catch (e) { - _data = {}; - } - } - - operator []=(String key, dynamic value) => setItem(key, value); - operator [](String key) => getItem(key); - - void removeItem(String key) { - _data.remove(key); - save(); - } - - /// 儲存變更並且更新資料檔案 - void setItem(String key, dynamic value) { - _data[key] = value; - save(); - } - - /// 儲存資料檔案 - void save() { - if (!_file.existsSync()) { - _file.createSync(recursive: true); - } - _file.writeAsStringSync(encode); - } - - // /// 重新從檔案中載入資料 - // void updateData() { - // _data = json.decode(_file.readAsStringSync()); - // } - - // 從 key 取得資料 - dynamic getItem(String key) { - // updateData(); - return _data[key]; - } - - /// 取得資料的 Map - Map toMap() => _data; - - /// 取得資料的字串,為 json 格式 - String get encode => json.encode(_data); -} diff --git a/lib/model/IO/download_info.dart b/lib/model/IO/download_info.dart deleted file mode 100644 index d83c29a57..000000000 --- a/lib/model/IO/download_info.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'dart:collection'; - -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:quiver/iterables.dart'; -import 'package:rpmlauncher/launcher/CheckData.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/data.dart'; - -class DownloadInfos extends IterableBase { - /// 一個下載資訊列表的類別 - /// [infos] 下載資訊列表 - /// [progress] 下載進度,如果尚未開始下載則為 0.0 - /// [downloading] 是否正在下載檔案中 - - List infos; - double progress = 0.0; - bool downloading = false; - - DownloadInfos(this.infos); - - factory DownloadInfos.empty() { - return DownloadInfos([]); - } - - /// 下載所有檔案 - Future downloadAll( - {Function(double progress)? onReceiveProgress, - Function(double progress)? onAllDownloading, - int max = 10}) async { - downloading = true; - - int count = infos.length; - int done = 0; - - await _downloadAsync( - onDone: () { - done++; - progress = done / count; - onReceiveProgress?.call(progress); - }, - onDownloading: (progress) => onAllDownloading?.call(progress), - max: max); - infos.clear(); - - downloading = false; - } - - /// 異步下載檔案 - /// [max] 最多同時執行幾個異步函數 - Future _downloadAsync( - {Function? onDone, - Function(double progress)? onDownloading, - required int max}) async { - List> queueInfos = partition(infos, max).toList(); - - for (List infos in queueInfos) { - await Future.wait(infos.map((e) => e - .download(onDownloading: (progress) => onDownloading?.call(progress)) - .whenComplete(() => onDone?.call()))); - } - } - - void add(DownloadInfo value) { - return infos.add(value); - } - - @override - Iterator get iterator => infos.iterator; -} - -class DownloadInfo { - /// [hashCheck] 是否檢查雜湊值檔案完整性 - /// [sh1Hash] Sh1 雜湊值,用於檢測檔案完整性 - /// [mo5Hash] Sh1 雜湊值,用於檢測檔案完整性 - - final String? title; - final String savePath; - final String downloadUrl; - final String? description; - final bool hashCheck; - final String? sh1Hash; - final Function? onDownloaded; - - Uri get downloadUri => Uri.parse(downloadUrl); - File get file => File(savePath); - double progress = double.nan; - - /// 下載檔案 - Future download({Function(double progress)? onDownloading}) async { - if (description != null) { - installingState.nowEvent = description!; - } - bool notNeedDownload = hashCheck && - file.existsSync() && - sh1Hash != null && - CheckData.checkSha1Sync(file, sh1Hash!); - - if (!notNeedDownload) { - try { - List bytes = (await RPMHttpClient().get(downloadUrl, - onReceiveProgress: (int count, int total) { - progress = count / total; - onDownloading?.call(progress); - }, options: Options(responseType: ResponseType.bytes))) - .data as List; - File file = File(savePath); - file.createSync(recursive: true); - file.writeAsBytesSync(bytes); - } catch (error, stackTrace) { - logger.error(ErrorType.download, error, stackTrace: stackTrace); - } - } - onDownloaded?.call(); - } - - DownloadInfo(this.downloadUrl, - {this.title, - this.hashCheck = false, - this.sh1Hash, - this.onDownloaded, - this.description, - required this.savePath}); -} diff --git a/lib/model/UI/ViewOptions.dart b/lib/model/UI/ViewOptions.dart deleted file mode 100644 index 263a36c1c..000000000 --- a/lib/model/UI/ViewOptions.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'dart:collection'; - -import 'package:flutter/cupertino.dart'; - -class ViewOptions extends IterableBase { - List options = []; - - ViewOptions(this.options); - - @override - Iterator get iterator => options.iterator; -} - -class ViewOptionTile { - final bool show; - final String? title; - final Widget? icon; - final String? description; - - ViewOptionTile( - {required this.title, - required this.icon, - this.description, - this.show = true}); - - factory ViewOptionTile.show() { - return ViewOptionTile(title: null, icon: null, show: true); - } -} diff --git a/lib/model/account/Account.dart b/lib/model/account/account.dart similarity index 64% rename from lib/model/account/Account.dart rename to lib/model/account/account.dart index 94910ccc7..2b623fe9c 100644 --- a/lib/model/account/Account.dart +++ b/lib/model/account/account.dart @@ -1,13 +1,12 @@ import 'dart:convert'; -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:oauth2/oauth2.dart'; +import 'package:rpmlauncher/config/json_storage.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; +import 'package:rpmlauncher/ui/widget/rpml_network_image.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/model/IO/JsonStorage.dart'; -import 'package:rpmlauncher/widget/RPMNetworkImage.dart'; - +/// Now only support Microsoft Account enum AccountType { mojang, microsoft, @@ -23,8 +22,8 @@ class Account { final Credentials? credentials; Widget get imageWidget { - return RPMNetworkImage( - src: "https://minotar.net/helm/$uuid", + return RPMLNetworkImage( + src: 'https://minotar.net/helm/$uuid', errorWidget: const Icon(Icons.person)); } @@ -56,7 +55,7 @@ class Account { : null); } - void save() => AccountStorage().save(this); + Future save() async => await AccountStorage.save(this); @override bool operator ==(Object other) { @@ -83,23 +82,24 @@ class Account { } class AccountStorage { - File get _file => GameRepository.getAccountFile(); - late JsonStorage _storage; + static final JsonStorage _storage = + JsonStorage(GameRepository.getAccountFile()); - AccountStorage() { - _storage = JsonStorage(_file); - } + static bool get hasAccount => getCount() > 0 && getDefault() != null; - bool get hasAccount => getCount() > 0 && getDefault() != null; + static Future init() { + return _storage.init(); + } - void add(AccountType type, String accessToken, String uuid, String userName, - {String? email, Credentials? credentials}) { + static Future add( + AccountType type, String accessToken, String uuid, String userName, + {String? email, Credentials? credentials}) async { final account = Account(type, accessToken, uuid, userName, email: email, credentials: credentials); - account.save(); + await account.save(); } - Account? getDefault() { + static Account? getDefault() { int? index = getIndex(); try { return index != null ? getByIndex(index) : null; @@ -108,56 +108,56 @@ class AccountStorage { } } - void removeByIndex(int index) { + static void removeByIndex(int index) { Map? accounts = _storage.getItem('account'); accounts?.remove(accounts.keys.toList()[index]); - _storage.setItem("account", accounts); + _storage.setItem('account', accounts); } - void removeByUUID(String uuid) { + static void removeByUUID(String uuid) { Map? accounts = _storage.getItem('account'); accounts?.remove(uuid); - _storage.setItem("account", accounts); + _storage.setItem('account', accounts); } - Map getAll() { - return _storage.toMap(); + static Future> getAll() { + return _storage.getAll(); } - int getCount() { + static int getCount() { return _storage.getItem('account') == null ? 0 : _storage.getItem('account').keys.length; } - void setIndex(int index) { - _storage.setItem("index", index); + static Future setIndex(int index) async { + await _storage.setItem('index', index); } - int? getIndex() { - return _storage.getItem("index"); + static int? getIndex() { + return _storage.getItem('index'); } - void save(Account account) { + static Future save(Account account) async { Map? accounts = _storage.getItem('account'); accounts ??= {}; accounts[account.uuid] = account.toJson(); - _storage.setItem("account", accounts); + await _storage.setItem('account', accounts); if (getIndex() == null) { - setIndex(0); + await setIndex(0); } } - Account getByIndex(int index) { + static Account getByIndex(int index) { Map accounts = _storage.getItem('account'); return Account.fromJson(accounts[accounts.keys.toList()[index]]); } - Account getByUUID(String uuid) { + static Account getByUUID(String uuid) { return Account.fromJson(_storage.getItem('account')[uuid]); } } diff --git a/lib/model/game/assets/asset_object.dart b/lib/model/game/assets/asset_object.dart new file mode 100644 index 000000000..14828d387 --- /dev/null +++ b/lib/model/game/assets/asset_object.dart @@ -0,0 +1,34 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:path/path.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; + +part 'asset_object.g.dart'; + +@JsonSerializable() +class AssetObject extends Equatable { + final String hash; + final int size; + + const AssetObject({required this.hash, required this.size}); + + factory AssetObject.fromJson(Map json) => + _$AssetObjectFromJson(json); + + Map toJson() => _$AssetObjectToJson(this); + + String getDownloadUrl() { + return 'https://resources.download.minecraft.net/${hash.substring(0, 2)}/$hash'; + } + + String getFilePath() { + final directory = GameRepository.getAssetsObjectsDirectory(); + return join(directory.path, hash.substring(0, 2), hash); + } + + @override + bool get stringify => true; + + @override + List get props => [hash, size]; +} diff --git a/lib/model/game/assets/assets_index.dart b/lib/model/game/assets/assets_index.dart new file mode 100644 index 000000000..57e4f66ed --- /dev/null +++ b/lib/model/game/assets/assets_index.dart @@ -0,0 +1,23 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:rpmlauncher/model/game/assets/asset_object.dart'; + +part 'assets_index.g.dart'; + +@JsonSerializable() +class AssetsIndex extends Equatable { + final Map objects; + + const AssetsIndex({required this.objects}); + + factory AssetsIndex.fromJson(Map json) => + _$AssetsIndexFromJson(json); + + Map toJson() => _$AssetsIndexToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [objects]; +} diff --git a/lib/model/Game/jvm_args.dart b/lib/model/game/jvm_args.dart similarity index 100% rename from lib/model/Game/jvm_args.dart rename to lib/model/game/jvm_args.dart diff --git a/lib/model/game/loader.dart b/lib/model/game/loader.dart new file mode 100644 index 000000000..f5827f6c6 --- /dev/null +++ b/lib/model/game/loader.dart @@ -0,0 +1,14 @@ +enum GameLoader { + vanilla, + fabric, + forge, + quilt; + + String getIconAssets() { + return 'assets/images/loader/$name.png'; + } + + String getBackgroundAssets() { + return 'assets/images/loader/${name}_background.png'; + } +} diff --git a/lib/model/game/version/library_download_artifact.dart b/lib/model/game/version/library_download_artifact.dart new file mode 100644 index 000000000..1ca7bcc8e --- /dev/null +++ b/lib/model/game/version/library_download_artifact.dart @@ -0,0 +1,38 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:path/path.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; + +part 'library_download_artifact.g.dart'; + +@JsonSerializable() +class LibraryDownloadArtifact extends Equatable { + final String path; + final String sha1; + final int size; + final String url; + + const LibraryDownloadArtifact( + {required this.path, + required this.sha1, + required this.size, + required this.url}); + + factory LibraryDownloadArtifact.fromJson(Map json) { + return _$LibraryDownloadArtifactFromJson(json); + } + + Map toJson() => _$LibraryDownloadArtifactToJson(this); + + String getFilePath() { + final directory = GameRepository.getLibrariesDirectory(); + + return join(directory.path, path); + } + + @override + bool get stringify => true; + + @override + List get props => [path, sha1, size, url]; +} diff --git a/lib/model/game/version/library_download_classifiers.dart b/lib/model/game/version/library_download_classifiers.dart new file mode 100644 index 000000000..354f9f8d2 --- /dev/null +++ b/lib/model/game/version/library_download_classifiers.dart @@ -0,0 +1,49 @@ +import 'dart:io'; + +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'library_download_artifact.dart'; + +part 'library_download_classifiers.g.dart'; + +@JsonSerializable() +class LibraryDownloadClassifiers extends Equatable { + @JsonKey(name: 'natives-linux') + final LibraryDownloadArtifact? linuxNatives; + @JsonKey(name: 'natives-osx') + final LibraryDownloadArtifact? macOSNatives; + @JsonKey(name: 'natives-windows') + final LibraryDownloadArtifact? windowsNatives; + + const LibraryDownloadClassifiers( + {this.linuxNatives, this.macOSNatives, this.windowsNatives}); + + factory LibraryDownloadClassifiers.fromJson(Map json) { + return _$LibraryDownloadClassifiersFromJson(json); + } + + Map toJson() => _$LibraryDownloadClassifiersToJson(this); + + /// Returns the natives for the current operating system. + LibraryDownloadArtifact? getNatives() { + final os = Platform.operatingSystem; + + switch (os) { + case 'linux': + return linuxNatives; + case 'macos': + return macOSNatives; + case 'windows': + return windowsNatives; + default: + return null; + } + } + + @override + bool get stringify => true; + + @override + List get props => [linuxNatives, macOSNatives, windowsNatives]; +} diff --git a/lib/model/game/version/library_downloads.dart b/lib/model/game/version/library_downloads.dart new file mode 100644 index 000000000..1018d60bb --- /dev/null +++ b/lib/model/game/version/library_downloads.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'library_download_artifact.dart'; +import 'library_download_classifiers.dart'; + +part 'library_downloads.g.dart'; + +@JsonSerializable() +class LibraryDownloads extends Equatable { + final LibraryDownloadArtifact? artifact; + final LibraryDownloadClassifiers? classifiers; + + const LibraryDownloads({this.artifact, this.classifiers}); + + factory LibraryDownloads.fromJson(Map json) => + _$LibraryDownloadsFromJson(json); + + Map toJson() => _$LibraryDownloadsToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [artifact, classifiers]; +} diff --git a/lib/model/game/version/logging_client.dart b/lib/model/game/version/logging_client.dart new file mode 100644 index 000000000..460682910 --- /dev/null +++ b/lib/model/game/version/logging_client.dart @@ -0,0 +1,27 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'logging_client_file.dart'; + +part 'logging_client.g.dart'; + +@JsonSerializable() +class LoggingClient extends Equatable { + final String argument; + final LoggingClientFile file; + final String type; + + const LoggingClient(this.argument, this.file, this.type); + + factory LoggingClient.fromJson(Map json) { + return _$LoggingClientFromJson(json); + } + + Map toJson() => _$LoggingClientToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [argument, file, type]; +} diff --git a/lib/model/game/version/logging_client_file.dart b/lib/model/game/version/logging_client_file.dart new file mode 100644 index 000000000..720300741 --- /dev/null +++ b/lib/model/game/version/logging_client_file.dart @@ -0,0 +1,29 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'logging_client_file.g.dart'; + +@JsonSerializable() +class LoggingClientFile extends Equatable { + final String id; + final String sha1; + final int size; + final String url; + + const LoggingClientFile( + {required this.id, + required this.sha1, + required this.size, + required this.url}); + + factory LoggingClientFile.fromJson(Map json) => + _$LoggingClientFileFromJson(json); + + Map toJson() => _$LoggingClientFileToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [id, sha1, size, url]; +} diff --git a/lib/model/game/version/mc_latest_version.dart b/lib/model/game/version/mc_latest_version.dart new file mode 100644 index 000000000..b2057b0d5 --- /dev/null +++ b/lib/model/game/version/mc_latest_version.dart @@ -0,0 +1,24 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'mc_latest_version.g.dart'; + +@JsonSerializable() +class MCLatestVersion extends Equatable { + final String release; + final String snapshot; + + const MCLatestVersion({required this.release, required this.snapshot}); + + factory MCLatestVersion.fromJson(Map json) { + return _$MCLatestVersionFromJson(json); + } + + Map toJson() => _$MCLatestVersionToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [release, snapshot]; +} diff --git a/lib/model/game/version/mc_version.dart b/lib/model/game/version/mc_version.dart new file mode 100644 index 000000000..4e6405301 --- /dev/null +++ b/lib/model/game/version/mc_version.dart @@ -0,0 +1,48 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_type.dart'; + +part 'mc_version.g.dart'; + +@JsonSerializable() +class MCVersion extends Equatable { + final String id; + final MCVersionType type; + final String url; + final DateTime time; + final DateTime releaseTime; + final String sha1; + final int complianceLevel; + + const MCVersion({ + required this.id, + required this.type, + required this.url, + required this.time, + required this.releaseTime, + required this.sha1, + required this.complianceLevel, + }); + + factory MCVersion.fromJson(Map json) { + return _$MCVersionFromJson(json); + } + + Map toJson() => _$MCVersionToJson(this); + + @override + bool get stringify => true; + + @override + List get props { + return [ + id, + type, + url, + time, + releaseTime, + sha1, + complianceLevel, + ]; + } +} diff --git a/lib/model/game/version/mc_version_argument.dart b/lib/model/game/version/mc_version_argument.dart new file mode 100644 index 000000000..3ec10cb30 --- /dev/null +++ b/lib/model/game/version/mc_version_argument.dart @@ -0,0 +1,44 @@ +import 'package:equatable/equatable.dart'; +import 'mc_version_rule.dart'; + +class MCVersionArgument extends Equatable { + final List value; + final List? rules; + + const MCVersionArgument({ + required this.value, + this.rules, + }); + + factory MCVersionArgument.fromJson(dynamic source) { + if (source is String) { + return MCVersionArgument(value: [source]); + } + + List value; + if (source['value'] is String) { + value = [source['value']]; + } else { + value = List.from(source['value'] as List); + } + + return MCVersionArgument( + value: value, + rules: (source['rules'] as List) + .map((e) => MCVersionRule.fromJson(e)) + .toList()); + } + + Map toJson() { + return { + 'value': value, + 'rules': rules, + }; + } + + @override + bool get stringify => true; + + @override + List get props => [value, rules]; +} diff --git a/lib/model/game/version/mc_version_arguments.dart b/lib/model/game/version/mc_version_arguments.dart new file mode 100644 index 000000000..f54b8e780 --- /dev/null +++ b/lib/model/game/version/mc_version_arguments.dart @@ -0,0 +1,25 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'mc_version_argument.dart'; + +part 'mc_version_arguments.g.dart'; + +@JsonSerializable() +class MCVersionArguments extends Equatable { + final List game; + final List jvm; + + const MCVersionArguments({required this.game, required this.jvm}); + + factory MCVersionArguments.fromJson(Map json) { + return _$MCVersionArgumentsFromJson(json); + } + + Map toJson() => _$MCVersionArgumentsToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [game, jvm]; +} diff --git a/lib/model/game/version/mc_version_asset_index.dart b/lib/model/game/version/mc_version_asset_index.dart new file mode 100644 index 000000000..64cf24c09 --- /dev/null +++ b/lib/model/game/version/mc_version_asset_index.dart @@ -0,0 +1,33 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'mc_version_asset_index.g.dart'; + +@JsonSerializable() +class MCVersionAssetIndex extends Equatable { + final String id; + final String sha1; + final int size; + final int totalSize; + final String url; + + const MCVersionAssetIndex({ + required this.id, + required this.sha1, + required this.size, + required this.totalSize, + required this.url, + }); + + factory MCVersionAssetIndex.fromJson(Map json) { + return _$MCVersionAssetIndexFromJson(json); + } + + Map toJson() => _$MCVersionAssetIndexToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [id, sha1, size, totalSize, url]; +} diff --git a/lib/model/game/version/mc_version_download.dart b/lib/model/game/version/mc_version_download.dart new file mode 100644 index 000000000..a4cd44868 --- /dev/null +++ b/lib/model/game/version/mc_version_download.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'mc_version_download.g.dart'; + +@JsonSerializable() +class MCVersionDownload extends Equatable { + final String sha1; + final int size; + final String url; + + const MCVersionDownload( + {required this.sha1, required this.size, required this.url}); + + factory MCVersionDownload.fromJson(Map json) { + return _$MCVersionDownloadFromJson(json); + } + + Map toJson() => _$MCVersionDownloadToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [sha1, size, url]; +} diff --git a/lib/model/game/version/mc_version_downloads.dart b/lib/model/game/version/mc_version_downloads.dart new file mode 100644 index 000000000..180622149 --- /dev/null +++ b/lib/model/game/version/mc_version_downloads.dart @@ -0,0 +1,41 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'mc_version_download.dart'; +part 'mc_version_downloads.g.dart'; + +@JsonSerializable() +class MCVersionDownloads extends Equatable { + final MCVersionDownload client; + @JsonKey(name: 'client_mappings') + final MCVersionDownload? clientMappings; + final MCVersionDownload? server; + @JsonKey(name: 'server_mappings') + final MCVersionDownload? serverMappings; + + const MCVersionDownloads({ + required this.client, + this.clientMappings, + this.server, + this.serverMappings, + }); + + factory MCVersionDownloads.fromJson(Map json) { + return _$MCVersionDownloadsFromJson(json); + } + + Map toJson() => _$MCVersionDownloadsToJson(this); + + @override + bool get stringify => true; + + @override + List get props { + return [ + client, + clientMappings, + server, + serverMappings, + ]; + } +} diff --git a/lib/model/game/version/mc_version_java_version.dart b/lib/model/game/version/mc_version_java_version.dart new file mode 100644 index 000000000..42654277c --- /dev/null +++ b/lib/model/game/version/mc_version_java_version.dart @@ -0,0 +1,39 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'mc_version_java_version.g.dart'; + +@JsonSerializable() +class MCVersionJavaVersion extends Equatable { + final JavaVersionComponent component; + final int majorVersion; + + const MCVersionJavaVersion( + {required this.component, required this.majorVersion}); + + factory MCVersionJavaVersion.fromJson(Map json) { + return _$MCVersionJavaVersionFromJson(json); + } + + Map toJson() => _$MCVersionJavaVersionToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [component, majorVersion]; +} + +@JsonEnum() +enum JavaVersionComponent { + @JsonValue('java-runtime-alpha') + javaRuntimeAlpha, + @JsonValue('java-runtime-beta') + javaRuntimeBeta, + @JsonValue('java-runtime-gamma') + javaRuntimeGamma, + @JsonValue('jre-legacy') + jreLegacy, + @JsonValue('minecraft-java-exe') + minecraftJavaExe +} diff --git a/lib/model/game/version/mc_version_library.dart b/lib/model/game/version/mc_version_library.dart new file mode 100644 index 000000000..006acc6b5 --- /dev/null +++ b/lib/model/game/version/mc_version_library.dart @@ -0,0 +1,33 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'library_downloads.dart'; +import 'mc_version_rule.dart'; + +part 'mc_version_library.g.dart'; + +@JsonSerializable() +class MCVersionLibrary extends Equatable { + final LibraryDownloads downloads; + final String name; + final List? rules; + + const MCVersionLibrary( + {required this.downloads, required this.name, this.rules}); + + factory MCVersionLibrary.fromJson(Map json) { + return _$MCVersionLibraryFromJson(json); + } + + Map toJson() => _$MCVersionLibraryToJson(this); + + /// Follows the rules of the library to determine whether it should be downloaded. + bool shouldDownload() { + return rules?.every((rule) => rule.isAllowed()) ?? true; + } + + @override + bool get stringify => true; + + @override + List get props => [downloads, name, rules]; +} diff --git a/lib/model/game/version/mc_version_logging.dart b/lib/model/game/version/mc_version_logging.dart new file mode 100644 index 000000000..b1d6e58c5 --- /dev/null +++ b/lib/model/game/version/mc_version_logging.dart @@ -0,0 +1,24 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'logging_client.dart'; + +part 'mc_version_logging.g.dart'; + +@JsonSerializable() +class MCVersionLogging extends Equatable { + final LoggingClient client; + + const MCVersionLogging({required this.client}); + + factory MCVersionLogging.fromJson(Map json) { + return _$MCVersionLoggingFromJson(json); + } + + Map toJson() => _$MCVersionLoggingToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [client]; +} diff --git a/lib/model/game/version/mc_version_manifest.dart b/lib/model/game/version/mc_version_manifest.dart new file mode 100644 index 000000000..40faf3e15 --- /dev/null +++ b/lib/model/game/version/mc_version_manifest.dart @@ -0,0 +1,25 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import 'mc_latest_version.dart'; +import 'mc_version.dart'; + +part 'mc_version_manifest.g.dart'; + +@JsonSerializable(createToJson: false) +class MCVersionManifest extends Equatable { + final MCLatestVersion latest; + final List versions; + + const MCVersionManifest({required this.latest, required this.versions}); + + factory MCVersionManifest.fromJson(Map json) { + return _$MCVersionManifestFromJson(json); + } + + @override + bool get stringify => true; + + @override + List get props => [latest, versions]; +} diff --git a/lib/model/game/version/mc_version_meta.dart b/lib/model/game/version/mc_version_meta.dart new file mode 100644 index 000000000..cdb746ea5 --- /dev/null +++ b/lib/model/game/version/mc_version_meta.dart @@ -0,0 +1,78 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_type.dart'; + +import 'mc_version_arguments.dart'; +import 'mc_version_asset_index.dart'; +import 'mc_version_downloads.dart'; +import 'mc_version_java_version.dart'; +import 'mc_version_library.dart'; +import 'mc_version_logging.dart'; + +part 'mc_version_meta.g.dart'; + +@JsonSerializable() +class MCVersionMeta extends Equatable { + final MCVersionArguments? arguments; + final MCVersionAssetIndex assetIndex; + final String assets; + final int complianceLevel; + final MCVersionDownloads downloads; + final String id; + final MCVersionJavaVersion javaVersion; + final List libraries; + final MCVersionLogging? logging; + final String mainClass; + final String? minecraftArguments; + final int minimumLauncherVersion; + final DateTime releaseTime; + final DateTime time; + final MCVersionType type; + + const MCVersionMeta({ + this.arguments, + required this.assetIndex, + required this.assets, + required this.complianceLevel, + required this.downloads, + required this.id, + required this.javaVersion, + required this.libraries, + this.logging, + required this.mainClass, + required this.minimumLauncherVersion, + this.minecraftArguments, + required this.releaseTime, + required this.time, + required this.type, + }); + + factory MCVersionMeta.fromJson(Map json) { + return _$MCVersionMetaFromJson(json); + } + + Map toJson() => _$MCVersionMetaToJson(this); + + @override + bool get stringify => true; + + @override + List get props { + return [ + arguments, + assetIndex, + assets, + complianceLevel, + downloads, + id, + javaVersion, + libraries, + logging, + mainClass, + minimumLauncherVersion, + releaseTime, + time, + type, + ]; + } +} diff --git a/lib/model/game/version/mc_version_rule.dart b/lib/model/game/version/mc_version_rule.dart new file mode 100644 index 000000000..4952d5428 --- /dev/null +++ b/lib/model/game/version/mc_version_rule.dart @@ -0,0 +1,45 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:rpmlauncher/model/game/version/rule_os.dart'; +import 'package:rpmlauncher/util/util.dart'; + +import 'rule_features.dart'; + +part 'mc_version_rule.g.dart'; + +@JsonSerializable() +class MCVersionRule extends Equatable { + final RuleAction action; + final RuleFeatures? features; + final RuleOS? os; + + const MCVersionRule({required this.action, this.features, this.os}); + + factory MCVersionRule.fromJson(Map json) => + _$MCVersionRuleFromJson(json); + + Map toJson() => _$MCVersionRuleToJson(this); + + bool isAllowed() { + // Check if the rule is for the current operating system. + final isCurrentOS = Util.getMinecraftFormatOS() == os?.name?.name; + + if (isCurrentOS) { + return action == RuleAction.allow; + } else { + return true; + } + } + + @override + bool get stringify => true; + + @override + List get props => [action, features, os]; +} + +@JsonEnum() +enum RuleAction { + allow, + disallow, +} diff --git a/lib/model/game/version/mc_version_type.dart b/lib/model/game/version/mc_version_type.dart new file mode 100644 index 000000000..86064adc8 --- /dev/null +++ b/lib/model/game/version/mc_version_type.dart @@ -0,0 +1,11 @@ +import 'package:json_annotation/json_annotation.dart'; + +@JsonEnum() +enum MCVersionType { + release, + snapshot, + @JsonValue('old_beta') + oldBeta, + @JsonValue('old_alpha') + oldAlpha, +} diff --git a/lib/model/game/version/rule_features.dart b/lib/model/game/version/rule_features.dart new file mode 100644 index 000000000..c5cfd4382 --- /dev/null +++ b/lib/model/game/version/rule_features.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'rule_features.g.dart'; + +@JsonSerializable() +class RuleFeatures extends Equatable { + @JsonKey(name: 'is_demo_user') + final bool? isDemoUser; + @JsonKey(name: 'has_custom_resolution') + final bool? hasCustomResolution; + + const RuleFeatures({this.isDemoUser, this.hasCustomResolution}); + + factory RuleFeatures.fromJson(Map json) { + return _$RuleFeaturesFromJson(json); + } + + Map toJson() => _$RuleFeaturesToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [isDemoUser, hasCustomResolution]; +} diff --git a/lib/model/game/version/rule_os.dart b/lib/model/game/version/rule_os.dart new file mode 100644 index 000000000..93dc831b0 --- /dev/null +++ b/lib/model/game/version/rule_os.dart @@ -0,0 +1,34 @@ +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'rule_os.g.dart'; + +@JsonSerializable() +class RuleOS extends Equatable { + final RuleOSName? name; + final String? version; + final RuleOSArch? arch; + + const RuleOS({this.name, this.version, this.arch}); + + factory RuleOS.fromJson(Map json) => _$RuleOSFromJson(json); + + Map toJson() => _$RuleOSToJson(this); + + @override + bool get stringify => true; + + @override + List get props => [name]; +} + +@JsonEnum() +enum RuleOSName { + linux, + @JsonValue('osx') + macOS, + windows, +} + +@JsonEnum() +enum RuleOSArch { x86 } diff --git a/lib/model/IO/isolate_option.dart b/lib/model/io/isolate_option.dart similarity index 54% rename from lib/model/IO/isolate_option.dart rename to lib/model/io/isolate_option.dart index c5caa4155..9f52ee3b2 100644 --- a/lib/model/IO/isolate_option.dart +++ b/lib/model/io/isolate_option.dart @@ -1,29 +1,33 @@ +import 'dart:io'; import 'dart:isolate'; -import 'package:rpmlauncher/function/counter.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/data.dart'; import 'package:rpmlauncher/util/launcher_path.dart'; import 'package:rpmtw_api_client/rpmtw_api_client.dart'; class IsolateOption { bool _initialized = false; - final Counter _counter; final T _argument; final List? _ports; + final Directory _currentDataHome; + final Directory _defaultDataHome; + final Logger _logger; + final bool _isTestMode; - IsolateOption._(this._counter, this._argument, this._ports); + IsolateOption._(this._argument, this._ports, this._currentDataHome, + this._defaultDataHome, this._logger, this._isTestMode); - factory IsolateOption.create(T argument, {List? ports}) { - return IsolateOption._(Counter.of(navigator.context), argument, - ports?.map((e) => e.sendPort).toList()); - } - - Counter get counter { - _checkInit(); - return _counter; + factory IsolateOption.create(T argument, {List? ports}) { + return IsolateOption._( + argument, + ports, + LauncherPath.currentDataHome, + LauncherPath.defaultDataHome, + Logger.current, + kTestMode, + ); } T get argument { @@ -37,6 +41,11 @@ class IsolateOption { port?.send(data); } + SendPort? getPort(int index) { + _checkInit(); + return _ports?[index]; + } + void _checkInit() { if (!_initialized) { throw Exception( @@ -49,9 +58,9 @@ class IsolateOption { return; } - LauncherPath.setCustomDataHome(_counter.dataHome, _counter.defaultDataHome); - Logger.setCustomLogger(_counter.logger); - kTestMode = _counter.testMode; + LauncherPath.setCustomDataHome(_currentDataHome, _defaultDataHome); + Logger.setCustomLogger(_logger); + kTestMode = _isTestMode; RPMTWApiClient.init(); _initialized = true; diff --git a/lib/model/IO/Properties.dart b/lib/model/io/properties.dart similarity index 95% rename from lib/model/IO/Properties.dart rename to lib/model/io/properties.dart index 31a5105e4..84a076779 100644 --- a/lib/model/IO/Properties.dart +++ b/lib/model/io/properties.dart @@ -44,9 +44,9 @@ class Properties with MapMixin { lines.add('$k$splitChar$v'); }); - properties.comments.forEach((e) { - lines.insert(0, '#$e'); - }); + for (final comment in properties.comments) { + lines.insert(0, '#$comment'); + } return lines.join('\n'); } diff --git a/lib/pages/curseforge_addon_page.dart b/lib/pages/curseforge_addon_page.dart deleted file mode 100644 index 08d239edb..000000000 --- a/lib/pages/curseforge_addon_page.dart +++ /dev/null @@ -1,246 +0,0 @@ -import 'package:rpmlauncher/mod/curseforge/curseforge_handler.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart'; - -class _CurseForgeAddonPageState extends State { - late TextEditingController searchController; - late ScrollController scrollController; - - List oldAddonList = []; - int index = 0; - - final List sortItems = [ - CurseForgeSortField.featured, - CurseForgeSortField.popularity, - CurseForgeSortField.lastUpdated, - CurseForgeSortField.name, - CurseForgeSortField.author, - CurseForgeSortField.totalDownloads - ]; - CurseForgeSortField sortItem = CurseForgeSortField.popularity; - - @override - void initState() { - searchController = TextEditingController(); - scrollController = ScrollController(); - - super.initState(); - - scrollController.addListener(() { - if ((scrollController.position.maxScrollExtent - - scrollController.position.pixels) < - 50) { - // if scroll to bottom - index += 20; - setState(() {}); - } - }); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Column( - children: [ - Text(widget.title, textAlign: TextAlign.center), - const SizedBox(height: 20), - RowScrollView( - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(widget.search), - const SizedBox(width: 12), - SizedBox( - width: MediaQuery.of(context).size.width * 0.3, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: searchController, - hintText: widget.searchHint, - onEditingComplete: () => cleanAllMods())), - const SizedBox(width: 12), - FloatingActionButton.extended( - onPressed: () => cleanAllMods(), - icon: const Icon(Icons.search), - label: Text(I18n.format('gui.search')), - ), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - I18nText('edit.instance.mods.sort'), - DropdownButton( - value: sortItem, - onChanged: (CurseForgeSortField? newValue) { - setState(() { - sortItem = newValue!; - cleanAllMods(); - }); - }, - items: sortItems - .map>( - (CurseForgeSortField value) { - return DropdownMenuItem( - value: value, - child: I18nText( - 'edit.instance.mods.sort.curseforge.${value.name}', - textAlign: TextAlign.center, - ), - ); - }).toList(), - ), - ], - ), - ...?widget.fitterOptions?.call(cleanAllMods) - ], - ), - ) - ], - ), - content: SizedBox( - height: MediaQuery.of(context).size.height / 2, - width: MediaQuery.of(context).size.width / 2, - child: FutureBuilder>( - future: Future.sync(() async => sortMods(await widget.getModList( - searchController.text.isEmpty ? null : searchController.text, - index, - sortItem))), - builder: (context, snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.isEmpty && - snapshot.connectionState == ConnectionState.done) { - return I18nText(widget.notFound, - style: const TextStyle(fontSize: 30), - textAlign: TextAlign.center); - } else if (snapshot.connectionState != ConnectionState.done && - snapshot.data!.length < 20) { - return const Center(child: RWLLoading()); - } - - return ListView.builder( - controller: scrollController, - shrinkWrap: true, - itemCount: snapshot.data!.length, - itemBuilder: (BuildContext context, int index) { - CurseForgeMod mod = snapshot.data![index]; - String modName = mod.name; - String modDescription = mod.summary; - int curseID = mod.id; - String pageUrl = mod.links.websiteUrl; - - return ListTile( - leading: CurseForgeHandler.getAddonIconWidget(mod.logo), - title: Text(modName), - subtitle: Text(modDescription), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - onPressed: () async { - Util.openUri(pageUrl); - }, - icon: const Icon(Icons.open_in_browser), - tooltip: - I18n.format('edit.instance.mods.page.open'), - ), - const SizedBox(width: 12), - FilledButton.icon( - onPressed: () => widget.onInstall(curseID, mod), - icon: const Icon(Icons.install_desktop), - label: Text(I18n.format('gui.install'))), - ], - ), - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: I18nText(widget.tapNameKey, - args: {"name": modName}, - textAlign: TextAlign.center), - content: I18nText(widget.tapDescriptionKey, - args: {"description": modDescription}, - textAlign: TextAlign.center), - ); - }, - ); - }, - ); - }, - ); - } else { - return const Center(child: RWLLoading()); - } - }), - ), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - tooltip: I18n.format('gui.close'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - } - - /// filter the same curseforge mod id - List sortMods(List sources) { - final List mods = oldAddonList; - - sources.forEach((mod) { - if (!(oldAddonList.any((_) => _.id == mod.id))) { - mods.add(mod); - } - }); - - oldAddonList = mods; - - return mods; - } - - void cleanAllMods() { - setState(() { - index = 0; - oldAddonList.clear(); - }); - } -} - -class CurseForgeAddonPage extends StatefulWidget { - final String title; - final String search; - final String searchHint; - final String notFound; - final String tapNameKey; - final String tapDescriptionKey; - - final Future> Function( - String? fitter, int index, CurseForgeSortField sort) getModList; - final void Function(int curseID, CurseForgeMod mod) onInstall; - - final List Function(VoidCallback cleanAllMods)? fitterOptions; - - const CurseForgeAddonPage( - {Key? key, - required this.title, - required this.search, - required this.searchHint, - required this.notFound, - required this.tapNameKey, - required this.tapDescriptionKey, - required this.getModList, - required this.onInstall, - this.fitterOptions}) - : super(key: key); - - @override - State createState() => _CurseForgeAddonPageState(); -} diff --git a/lib/pages/curseforge_mod_version.dart b/lib/pages/curseforge_mod_version.dart deleted file mode 100644 index 5f03b54ec..000000000 --- a/lib/pages/curseforge_mod_version.dart +++ /dev/null @@ -1,303 +0,0 @@ -import 'dart:io'; -import 'dart:isolate'; - -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/mod/curseforge/curseforge_handler.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/mod_info.dart'; -import 'package:rpmlauncher/model/IO/download_info.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/IO/isolate_option.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart' hide ModLoader; - -import '../widget/rwl_loading.dart'; - -class CurseForgeModVersion extends StatefulWidget { - final int curseID; - final Directory modDir; - final InstanceConfig instanceConfig; - final Map modInfos; - - const CurseForgeModVersion( - {required this.curseID, - required this.modDir, - required this.instanceConfig, - required this.modInfos}); - - @override - State createState() => _CurseForgeModVersionState(); -} - -class _CurseForgeModVersionState extends State { - List installedFiles = []; - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder>( - future: RPMTWApiClient.instance.curseforgeResource.getModFiles( - widget.curseID, - gameVersion: widget.instanceConfig.version, - modLoaderType: widget.instanceConfig.loaderEnum.toCurseForgeType()), - builder: (context, snapshot) { - if (snapshot.hasData) { - List files = CurseForgeHandler.filterModFiles( - snapshot.data!, - widget.instanceConfig.version, - widget.instanceConfig.loaderEnum); - - files.sort((a, b) => DateTime.parse(b.fileDate) - .compareTo(DateTime.parse(a.fileDate))); - - return AlertDialog( - title: Text( - I18n.format("edit.instance.mods.download.select.version")), - content: SizedBox( - height: MediaQuery.of(context).size.height / 3, - width: MediaQuery.of(context).size.width / 3, - child: ListView.builder( - itemCount: files.length, - itemBuilder: (BuildContext context, int fileIndex) { - CurseForgeModFile file = files[fileIndex]; - - return ListTile( - leading: FutureBuilder( - future: installedWidget(file), - builder: (context, snapshot) { - if (snapshot.hasData) { - return snapshot.data!; - } else { - return const CircularProgressIndicator(); - } - }), - title: Text(file.displayName.replaceAll(".jar", ""), - style: const TextStyle(fontSize: 17)), - subtitle: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - CurseForgeHandler.parseReleaseType( - file.releaseType), - Text(Util.formatDate( - DateTime.parse(file.fileDate))), - ], - ), - onTap: () { - installedFiles.forEach((file) { - file.deleteSync(recursive: true); - }); - showDialog( - barrierDismissible: false, - context: context, - builder: (context) => Task( - file, - widget.modDir, - widget.instanceConfig.version, - widget.instanceConfig.loaderEnum, - ), - ); - }, - ); - })), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - tooltip: I18n.format("gui.close"), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - } else { - return const RWLLoading(); - } - }); - } - - Future installedWidget(CurseForgeModFile file) async { - late MapEntry entry; - try { - entry = widget.modInfos.entries.firstWhere( - (entry) => entry.value.murmur2Hash == file.fileFingerprint); - - installedFiles.add(entry.key); - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.check), - Text(I18n.format("edit.instance.mods.installed"), - textAlign: TextAlign.center) - ], - ); - } catch (e) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.close), - Text(I18n.format("edit.instance.mods.uninstalled"), - textAlign: TextAlign.center) - ], - ); - } - } -} - -class Task extends StatefulWidget { - final CurseForgeModFile file; - final Directory modDir; - final String versionID; - final ModLoader loader; - final bool autoClose; - - const Task(this.file, this.modDir, this.versionID, this.loader, - {this.autoClose = false}); - - @override - State createState() => _TaskState(); -} - -class _TaskState extends State { - bool finish = false; - final DownloadInfos _downloadInfos = DownloadInfos.empty(); - - @override - void initState() { - super.initState(); - thread(); - } - - double _progress = 0; - double _progress2 = 0; - - Future getDownloadInfos() async { - if (launcherConfig.autoDownloadModDependencies) { - /// Find required dependencies. - List dependencies = widget.file.dependencies - .where((e) => e.relationType == 3) - .toSet() - .toList(); - - if (dependencies.isNotEmpty) { - for (Dependency dependency in dependencies) { - List? dependencyFiles = - await RPMTWApiClient.instance.curseforgeResource.getModFiles( - dependency.modId, - gameVersion: widget.versionID, - modLoaderType: widget.loader.toCurseForgeType(), - ); - - if (dependencyFiles.isNotEmpty) { - _downloadInfos.add(DownloadInfo( - dependencyFiles.first.downloadUrl, - savePath: join( - widget.modDir.absolute.path, dependencyFiles.first.fileName), - )); - } - } - } - } - - _downloadInfos.add(DownloadInfo(widget.file.downloadUrl, - savePath: join(widget.modDir.absolute.path, widget.file.fileName))); - - return _downloadInfos; - } - - thread() async { - DownloadInfos infos = await getDownloadInfos(); - - ReceivePort progressPort = ReceivePort(); - ReceivePort allProgressPort = ReceivePort(); - - await Isolate.spawn( - downloading, - IsolateOption.create( - [infos, progressPort.sendPort, allProgressPort.sendPort])); - progressPort.listen((message) { - setState(() { - _progress = message; - }); - if (message == 1.0) { - finish = true; - } - }); - allProgressPort.listen((message) { - setState(() { - _progress2 = message; - }); - }); - } - - static downloading(IsolateOption option) async { - option.init(); - - DownloadInfos infos = option.argument[0]; - SendPort port = option.argument[1]; - SendPort port2 = option.argument[2]; - - await infos.downloadAll( - onReceiveProgress: (value) { - port.send(value); - }, - onAllDownloading: (progress) => port2.send(progress), - ); - } - - @override - Widget build(BuildContext context) { - if (_progress == 1.0 && finish) { - if (widget.autoClose) { - WidgetsBinding.instance.addPostFrameCallback((_) async { - await Future.delayed(const Duration(milliseconds: 100)); - if (mounted) { - Navigator.of(context).pop(); - } - }); - return const SizedBox(); - } else { - return AlertDialog( - title: Text(I18n.format("gui.download.done")), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }, - child: Text(I18n.format("gui.close"))) - ], - ); - } - } else { - return AlertDialog( - title: Text( - "${I18n.format("gui.download.ing")} ${widget.file.displayName.replaceAll(".jar", "")}"), - content: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text("${(_progress2 * 100).toStringAsFixed(3)}%"), - LinearProgressIndicator(value: _progress2), - ...(_downloadInfos.infos.length > 1 - ? [ - const SizedBox( - height: 10, - ), - LinearProgressIndicator(value: _progress) - ] - : []) - ], - ), - ); - } - } -} diff --git a/lib/pages/curseforge_modpack_page.dart b/lib/pages/curseforge_modpack_page.dart deleted file mode 100644 index 9c7ea31ae..000000000 --- a/lib/pages/curseforge_modpack_page.dart +++ /dev/null @@ -1,234 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/mod/curseforge/curseforge_handler.dart'; -import 'package:rpmlauncher/pages/curseforge_addon_page.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart'; - -class CurseForgeModpackPage extends StatefulWidget { - const CurseForgeModpackPage({Key? key}) : super(key: key); - - @override - State createState() => _CurseForgeModpackPageState(); -} - -class _CurseForgeModpackPageState extends State { - String? fitterVersion; - - @override - Widget build(BuildContext context) { - return CurseForgeAddonPage( - title: I18n.format('modpack.curseforge.title'), - search: I18n.format('modpack.search'), - searchHint: I18n.format('modpack.search.hint'), - notFound: I18n.format('modpack.found'), - tapNameKey: 'modpack.name', - tapDescriptionKey: 'modpack.description', - getModList: (fitter, index, sort) => - RPMTWApiClient.instance.curseforgeResource.searchMods( - game: CurseForgeGames.minecraft, - index: index, - pageSize: 20, - gameVersion: fitterVersion, - searchFilter: fitter, - classId: 4471, // Modpack - sortField: sort), - onInstall: (curseID, mod) { - List files = - mod.latestFiles.reversed.toList(); - - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text( - I18n.format("edit.instance.mods.download.select.version")), - content: SizedBox( - height: MediaQuery.of(context).size.height / 3, - width: MediaQuery.of(context).size.width / 3, - child: ListView.builder( - itemCount: files.length, - itemBuilder: (context, index) { - CurseForgeModLatestFile file = files[index]; - - if (fitterVersion != null && - !file.gameVersions.any((_) => _ == fitterVersion)) { - return Container(); - } else { - return ListTile( - title: - Text(file.displayName.replaceAll(".zip", "")), - subtitle: CurseForgeHandler.parseReleaseType( - file.releaseType), - onTap: () { - showDialog( - barrierDismissible: false, - context: context, - builder: (context) => - _DownloadModpack(file, mod.logo?.url)); - }, - ); - } - }, - )), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - tooltip: I18n.format("gui.close"), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ); - }, - fitterOptions: (cleanAllMods) => [ - _FitterOptions(cleanAllMods, (version) { - setState(() { - fitterVersion = version; - }); - }) - ]); - } -} - -class _FitterOptions extends StatefulWidget { - final VoidCallback cleanAllMods; - final ValueChanged onChanged; - const _FitterOptions(this.cleanAllMods, this.onChanged, {Key? key}) - : super(key: key); - - @override - State<_FitterOptions> createState() => _FitterOptionsState(); -} - -class _FitterOptionsState extends State<_FitterOptions> { - String defaultVersion = I18n.format('modpack.all_version'); - - late final List versionItems; - late String versionItem; - - @override - void initState() { - versionItems = [defaultVersion]; - versionItem = defaultVersion; - - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - final versions = await RPMTWApiClient.instance.curseforgeResource - .getMinecraftVersions(); - versionItems.addAll(versions.map((e) => e.versionString).toList()); - setState(() {}); - }); - } - - @override - Widget build(BuildContext context) { - return Row(children: [ - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(I18n.format("game.version")), - Builder(builder: (context) { - if (versionItems.length == 1) { - return const Center(child: RWLLoading()); - } else { - return DropdownButton( - value: versionItem, - onChanged: (String? newValue) { - setState(() { - versionItem = newValue!; - widget.cleanAllMods(); - - if (versionItem == defaultVersion) { - widget.onChanged(null); - } else { - widget.onChanged(versionItem); - } - }); - }, - items: - versionItems.map>((String value) { - return DropdownMenuItem( - value: value, - child: Text( - value, - textAlign: TextAlign.center, - ), - ); - }).toList(), - ); - } - }) - ], - ) - ]); - } -} - -class _DownloadModpack extends StatefulWidget { - final CurseForgeModLatestFile file; - final String? iconUrl; - - const _DownloadModpack(this.file, this.iconUrl); - - @override - State<_DownloadModpack> createState() => _DownloadModpackState(); -} - -class _DownloadModpackState extends State<_DownloadModpack> { - late File modpackFile; - - @override - void initState() { - modpackFile = - File(join(Directory.systemTemp.absolute.path, widget.file.fileName)); - - super.initState(); - } - - @override - Widget build(BuildContext context) { - return StreamBuilder( - stream: download(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return CurseForgeHandler.installModpack( - modpackFile, widget.iconUrl); - } else { - final double progress = snapshot.data ?? 0.0; - - return AlertDialog( - title: Text(I18n.format('modpack.downloading')), - content: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text("${(progress * 100).toStringAsFixed(3)}%"), - LinearProgressIndicator(value: progress) - ], - ), - ); - } - }); - } - - Stream download() async* { - yield* Stream.multi((p0) async { - await RPMHttpClient().download(widget.file.downloadUrl, modpackFile.path, - onReceiveProgress: (rec, total) { - p0.add(rec / total); - }); - p0.close(); - }); - } -} diff --git a/lib/pages/curseforge_mods_page.dart b/lib/pages/curseforge_mods_page.dart deleted file mode 100644 index 428ab78ca..000000000 --- a/lib/pages/curseforge_mods_page.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/mod_info.dart'; -import 'package:rpmlauncher/pages/curseforge_addon_page.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/pages/curseforge_mod_version.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart'; - -class CurseForgeModsPage extends StatefulWidget { - final String instanceUUID; - final Map modInfos; - - const CurseForgeModsPage( - {Key? key, required this.instanceUUID, required this.modInfos}) - : super(key: key); - - @override - State createState() => _CurseForgeModsPageState(); -} - -class _CurseForgeModsPageState extends State { - late final InstanceConfig config; - - @override - void initState() { - config = InstanceRepository.instanceConfig(widget.instanceUUID)!; - - super.initState(); - } - - @override - Widget build(BuildContext context) { - return CurseForgeAddonPage( - title: I18n.format('edit.instance.mods.download.curseforge'), - search: I18n.format('edit.instance.mods.download.search'), - searchHint: I18n.format('edit.instance.mods.download.search.hint'), - notFound: I18n.format('mods.filter.notfound'), - tapNameKey: 'edit.instance.mods.list.name', - tapDescriptionKey: 'edit.instance.mods.list.description', - getModList: (fitter, index, sort) => - RPMTWApiClient.instance.curseforgeResource.searchMods( - game: CurseForgeGames.minecraft, - index: index, - pageSize: 20, - gameVersion: config.version, - modLoaderType: - CurseForgeModLoaderType.values.byName(config.loader), - searchFilter: fitter, - classId: 6, // Mods - sortField: sort), - onInstall: (curseID, mod) { - showDialog( - context: context, - builder: (context) { - return CurseForgeModVersion( - curseID: curseID, - modDir: InstanceRepository.getModRootDir(widget.instanceUUID), - instanceConfig: config, - modInfos: widget.modInfos); - }, - ); - }); - } -} diff --git a/lib/pages/modrinth_mod.dart b/lib/pages/modrinth_mod.dart deleted file mode 100644 index 038c0aa92..000000000 --- a/lib/pages/modrinth_mod.dart +++ /dev/null @@ -1,273 +0,0 @@ -import 'dart:io'; - -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/mod/modrinth_handler.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/modrinth_mod_version.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; - -class _ModrinthModState extends State { - late final TextEditingController searchController; - late final ScrollController modScrollController; - late final InstanceConfig instanceConfig; - late final Directory modDir; - - List oldModList = []; - int index = 0; - - final List sortItemsCode = [ - 'relevance', - 'downloads', - 'updated', - 'newest' - ]; - final List sortItems = [ - I18n.format('edit.instance.mods.sort.modrinth.relevance'), - I18n.format('edit.instance.mods.sort.modrinth.downloads'), - I18n.format('edit.instance.mods.sort.modrinth.updated'), - I18n.format('edit.instance.mods.sort.modrinth.newest') - ]; - String sortItem = I18n.format('edit.instance.mods.sort.modrinth.relevance'); - - @override - void initState() { - searchController = TextEditingController(); - modScrollController = ScrollController(); - modDir = InstanceRepository.getModRootDir(widget.instanceUUID); - instanceConfig = InstanceRepository.instanceConfig(widget.instanceUUID)!; - - super.initState(); - - modScrollController.addListener(() { - if (modScrollController.position.maxScrollExtent == - modScrollController.position.pixels) { - // if scroll to bottom - setState(() {}); - } - }); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - scrollable: true, - title: Column( - children: [ - Text(I18n.format('edit.instance.mods.download.modrinth'), - textAlign: TextAlign.center), - const SizedBox(height: 20), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(I18n.format('edit.instance.mods.download.search')), - const SizedBox(width: 12), - Expanded( - child: RPMLTextField( - textAlign: TextAlign.center, - controller: searchController, - hintText: I18n.format( - 'edit.instance.mods.download.search.hint'), - onEditingComplete: () => clearModList())), - const SizedBox(width: 12), - FloatingActionButton.extended( - onPressed: () { - setState(() { - index = 0; - oldModList = []; - }); - }, - icon: const Icon(Icons.search), - label: Text(I18n.format('gui.search')), - ), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(I18n.format('edit.instance.mods.sort')), - DropdownButton( - value: sortItem, - onChanged: (String? newValue) { - clearModList(); - setState(() { - sortItem = newValue!; - }); - }, - items: - sortItems.map>((String value) { - return DropdownMenuItem( - value: value, - child: Text( - value, - textAlign: TextAlign.center, - ), - ); - }).toList(), - ), - ], - ), - ], - ) - ], - ), - content: SizedBox( - height: MediaQuery.of(context).size.height / 2, - width: MediaQuery.of(context).size.width / 2, - child: FutureBuilder( - future: ModrinthHandler.getModList( - instanceConfig.version, - instanceConfig.loader, - searchController, - oldModList, - index, - sortItemsCode[sortItems.indexOf(sortItem)]), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - if (snapshot.data.isEmpty) { - return I18nText('mods.filter.notfound', - style: const TextStyle(fontSize: 30), - textAlign: TextAlign.center); - } - index++; - return ListView.builder( - shrinkWrap: true, - itemCount: snapshot.data!.length, - controller: modScrollController, - itemBuilder: (BuildContext context, int index) { - Map data = snapshot.data[index]; - String modName = data['title']; - String modDescription = data['description']; - String modrinthID = data['project_id']; - String pageUrl = 'https://modrinth.com/mod/$modrinthID'; - String iconUrl = data['icon_url']; - - Widget modIcon; - if (iconUrl.isEmpty) { - modIcon = const Icon(Icons.image, size: 50); - } else { - modIcon = Image.network( - iconUrl, - width: 50, - height: 50, - fit: BoxFit.contain, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded - .toInt() / - loadingProgress.expectedTotalBytes!.toInt() - : null, - ); - }, - ); - } - - return ListTile( - leading: modIcon, - title: Text(modName), - subtitle: Text(modDescription), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - onPressed: () => Util.openUri(pageUrl), - icon: const Icon(Icons.open_in_browser), - tooltip: - I18n.format('edit.instance.mods.page.open'), - ), - const SizedBox( - width: 12, - ), - FilledButton.icon( - onPressed: () async { - showDialog( - context: context, - builder: (context) { - return ModrinthModVersion(modrinthID, - instanceConfig, modDir, modName); - }, - ); - }, - icon: const Icon(Icons.install_desktop), - label: Text(I18n.format('gui.install')), - ), - ], - ), - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text( - I18n.format('edit.instance.mods.list.name', - args: {'name': modName}), - textAlign: TextAlign.center, - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ModrinthHandler.parseSide( - '${I18n.format('gui.side.client')}: ', - 'client_side', - data), - ModrinthHandler.parseSide( - '${I18n.format('gui.side.server')}: ', - 'server_side', - data), - const SizedBox( - height: 12, - ), - Text( - I18n.format( - 'edit.instance.mods.list.description', - args: { - 'description': modDescription - }), - textAlign: TextAlign.center) - ], - ), - ); - }, - ); - }, - ); - }, - ); - } else { - return const Center(child: RWLLoading()); - } - }), - ), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - tooltip: I18n.format('gui.close'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - } - - void clearModList() { - setState(() { - index = 0; - oldModList = []; - }); - } -} - -class ModrinthMod extends StatefulWidget { - final String instanceUUID; - - const ModrinthMod({required this.instanceUUID}); - - @override - State createState() => _ModrinthModState(); -} diff --git a/lib/route/PushTransitions.dart b/lib/route/PushTransitions.dart deleted file mode 100644 index aeee816cf..000000000 --- a/lib/route/PushTransitions.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/material.dart'; - -class PushTransitions extends MaterialPageRoute { - PushTransitions({required WidgetBuilder builder, RouteSettings? settings}) - : super(builder: builder, settings: settings); - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) { - return FadeTransition(opacity: animation, child: child); - } -} diff --git a/lib/route/fade_route.dart b/lib/route/fade_route.dart new file mode 100644 index 000000000..79be9e756 --- /dev/null +++ b/lib/route/fade_route.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class FadeRoute extends PageRouteBuilder { + final WidgetBuilder builder; + FadeRoute({required this.builder, super.settings}) + : super(pageBuilder: (context, animation, secondaryAnimation) { + return builder.call(context); + }, transitionsBuilder: (context, animation, secondaryAnimation, child) { + return FadeTransition( + opacity: animation, + child: child, + ); + }); +} diff --git a/lib/route/generate_route.dart b/lib/route/generate_route.dart index 40c6e1d25..716502c48 100644 --- a/lib/route/generate_route.dart +++ b/lib/route/generate_route.dart @@ -1,52 +1,34 @@ import 'package:flutter/material.dart'; -import 'package:rpmlauncher/route/PushTransitions.dart'; -import 'package:rpmlauncher/route/RPMRouteSettings.dart'; -import 'package:rpmlauncher/screen/account.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/screen/settings.dart'; -import 'package:rpmlauncher/screen/edit.dart'; -import 'package:rpmlauncher/screen/Log.dart'; -import 'package:rpmlauncher/util/data.dart'; +import 'package:rpmlauncher/route/fade_route.dart'; +import 'package:rpmlauncher/route/rpml_route_settings.dart'; +import 'package:rpmlauncher/ui/pages/account_page.dart'; +import 'package:rpmlauncher/ui/pages/home_page.dart'; +import 'package:rpmlauncher/ui/screens/loading_screen.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; Route onGenerateRoute(RouteSettings _) { - RPMRouteSettings settings = RPMRouteSettings.fromRouteSettings(_); + RPMLRouteSettings settings = RPMLRouteSettings.fromRouteSettings(_); + if (settings.name == HomePage.route) { settings.routeName = 'home_page'; - return PushTransitions( + return FadeRoute( settings: settings, builder: (context) => const HomePage()); } - Uri uri = Uri.parse(settings.name!); - if (settings.name!.startsWith('/instance/') && uri.pathSegments.length > 2) { - // '/instance/${instanceUUID}' - String instanceUUID = uri.pathSegments[1]; - - if (settings.name!.startsWith('/instance/$instanceUUID/edit')) { - settings.routeName = 'edit_instance'; - return PushTransitions( - settings: settings, - builder: (context) => EditInstance(instanceUUID: instanceUUID)); - } else if (settings.name!.startsWith('/instance/$instanceUUID/launcher')) { - settings.routeName = 'launcher_instance'; - return PushTransitions( - settings: settings, - builder: (context) => LogScreen(instanceUUID: instanceUUID)); - } + if (settings.name == AccountScreen.route) { + settings.routeName = 'account'; + return FadeRoute( + settings: settings, builder: (context) => const AccountScreen()); } - if (settings.name == SettingScreen.route) { - settings.routeName = 'settings'; - return DialogRoute( + if (settings.name == LoadingScreen.route) { + settings.routeName = 'loading'; + return MaterialPageRoute( settings: settings, - builder: (context) => SettingScreen(), - context: navigator.context); - } else if (settings.name == AccountScreen.route) { - settings.routeName = 'account'; - return PushTransitions( - settings: settings, builder: (context) => AccountScreen()); + builder: (context) => + const SentryScreenshotWidget(child: LoadingScreen())); } - return PushTransitions( - settings: settings, builder: (context) => const HomePage()); + return FadeRoute(settings: settings, builder: (context) => const HomePage()); } diff --git a/lib/route/RPMNavigatorObserver.dart b/lib/route/rpml_navigator_observer.dart similarity index 83% rename from lib/route/RPMNavigatorObserver.dart rename to lib/route/rpml_navigator_observer.dart index b2608a13b..fb89b0de9 100644 --- a/lib/route/RPMNavigatorObserver.dart +++ b/lib/route/rpml_navigator_observer.dart @@ -1,24 +1,24 @@ import 'package:flutter/widgets.dart'; import 'package:rpmlauncher/i18n/launcher_language.dart'; -import 'package:rpmlauncher/route/RPMRouteSettings.dart'; +import 'package:rpmlauncher/route/rpml_route_settings.dart'; import 'package:rpmlauncher/util/data.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; import 'package:window_size/window_size.dart'; -class RPMNavigatorObserver extends NavigatorObserver { +class RPMLNavigatorObserver extends NavigatorObserver { @override void didPop(Route route, Route? previousRoute) { super.didPop(route, previousRoute); try { did(previousRoute!.settings, "Pop"); - } catch (e) {} + } catch (_) {} } void did(RouteSettings settings, String action) { - RPMRouteSettings routeSettings = - RPMRouteSettings.fromRouteSettings(settings); + RPMLRouteSettings routeSettings = + RPMLRouteSettings.fromRouteSettings(settings); String key = "navigator.pages.${routeSettings.routeName ?? "unknown"}"; String i18n = I18n.format(key); @@ -44,6 +44,6 @@ class RPMNavigatorObserver extends NavigatorObserver { super.didPush(route, previousRoute); try { did(route.settings, "Push"); - } catch (e) {} + } catch (_) {} } } diff --git a/lib/route/RPMRouteSettings.dart b/lib/route/rpml_route_settings.dart similarity index 55% rename from lib/route/RPMRouteSettings.dart rename to lib/route/rpml_route_settings.dart index 6468c956c..63c8f24bc 100644 --- a/lib/route/RPMRouteSettings.dart +++ b/lib/route/rpml_route_settings.dart @@ -1,19 +1,19 @@ import 'package:flutter/widgets.dart'; -class RPMRouteSettings extends RouteSettings { +class RPMLRouteSettings extends RouteSettings { String? routeName; - RPMRouteSettings({ + RPMLRouteSettings({ this.routeName, String? name, Object? arguments, }) : super(name: name, arguments: arguments); - factory RPMRouteSettings.fromRouteSettings(RouteSettings settings) { + factory RPMLRouteSettings.fromRouteSettings(RouteSettings settings) { try { - return settings as RPMRouteSettings; + return settings as RPMLRouteSettings; } catch (e) { - return RPMRouteSettings( + return RPMLRouteSettings( name: settings.name, arguments: settings.arguments); } } diff --git a/lib/route/slide_route.dart b/lib/route/slide_route.dart new file mode 100644 index 000000000..167be9b46 --- /dev/null +++ b/lib/route/slide_route.dart @@ -0,0 +1,14 @@ +import 'package:flutter/cupertino.dart'; + +class SlideRoute extends CupertinoPageRoute { + SlideRoute({required super.builder}) : super(); + + @override + Widget buildTransitions(BuildContext context, Animation animation, + Animation secondaryAnimation, Widget child) { + return FadeTransition( + opacity: animation, + child: super + .buildTransitions(context, animation, secondaryAnimation, child)); + } +} diff --git a/lib/screen/DownloadGameDialog.dart b/lib/screen/DownloadGameDialog.dart deleted file mode 100644 index 9ed38cb51..000000000 --- a/lib/screen/DownloadGameDialog.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/Game/MinecraftVersion.dart'; -import 'package:rpmlauncher/widget/AddInstance.dart'; -import 'package:rpmlauncher/widget/FabricVersion.dart'; -import 'package:rpmlauncher/widget/ForgeVersion.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/widget/WIPWidget.dart'; - -class DownloadGameDialog extends StatelessWidget { - final String instanceName; - final MCVersion version; - final ModLoader loader; - final MinecraftSide side; - - const DownloadGameDialog( - this.instanceName, this.version, this.loader, this.side); - - @override - Widget build(BuildContext context) { - if (loader == ModLoader.fabric) { - return FabricVersion(instanceName, version, side); - } else if (loader == ModLoader.forge) { - return ForgeVersion(instanceName, version); - } else if (loader == ModLoader.paper) { - return WiPWidget(); - } else { - return AddInstanceDialog(instanceName, version, loader, null, side); - } - } -} diff --git a/lib/screen/Log.dart b/lib/screen/Log.dart deleted file mode 100644 index e11b4344c..000000000 --- a/lib/screen/Log.dart +++ /dev/null @@ -1,559 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:dart_big5/big5.dart'; -import 'package:flutter/gestures.dart'; -import 'package:io/io.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/handler/window_handler.dart'; -import 'package:rpmlauncher/launcher/Arguments.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeAPI.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeInstallProfile.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/account/Account.dart'; -import 'package:rpmlauncher/model/Game/GameLogs.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/route/PushTransitions.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/util/Process.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/util/data.dart'; - -import 'package:rpmlauncher/widget/dialog/GameCrash.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:window_size/window_size.dart'; - -import '../util/launcher_info.dart'; - -class _LogScreenState extends State { - GameLogs _logs = GameLogs.empty(); - GameLogs logs = GameLogs.empty(); - String errorLog_ = ""; - - bool searching = false; - late TextEditingController _searchController; - late TextEditingController _serverCommandController; - bool scrolling = true; - - final int maxLogLength = launcherConfig.gameLogMaxLineCount; - - Process? process; - late Timer logTimer; - late InstanceConfig instanceConfig; - late Directory instanceDir; - late ScrollController _scrollController; - late bool showGameLogs; - late MinecraftSide side; - Directory? nativesTempDir; - - void killGame() { - if (side.isServer) { - process?.stdin.writeln("/stop"); - } - process?.kill(); - } - - @override - void initState() { - _scrollController = ScrollController( - keepScrollOffset: true, - ); - _searchController = TextEditingController(); - _serverCommandController = TextEditingController(); - - super.initState(); - - instanceDir = InstanceRepository.getInstanceDir(widget.instanceUUID); - instanceConfig = InstanceRepository.instanceConfig(widget.instanceUUID)!; - - setWindowTitle("RPMLauncher - ${instanceConfig.name}"); - - String gameVersionID = instanceConfig.version; - side = instanceConfig.sideEnum; - ModLoader loader = ModLoaderUttily.getByString(instanceConfig.loader); - String? loaderVersion = instanceConfig.loaderVersion; - File argsFile = GameRepository.getArgsFile(gameVersionID, loader, side, - loaderVersion: loaderVersion); - - if (loader == ModLoader.forge && !argsFile.existsSync()) { - File forgeProfileFile = GameRepository.getForgeProfileFile(gameVersionID); - if (forgeProfileFile.existsSync()) { - try { - ForgeInstallProfile profile = ForgeInstallProfile.fromNewJson( - json.decode(forgeProfileFile.readAsStringSync())); - ForgeAPI.handlingArgs( - profile.versionJson, gameVersionID, loaderVersion!); - } catch (e) {} - } - } - - Map argsMeta = json.decode(argsFile.readAsStringSync()); - - Version comparableVersion = instanceConfig.comparableVersion; - - Account account = AccountStorage().getDefault()!; - - File clientFile = GameRepository.getClientJar(gameVersionID); - - int minRam = 512; - int maxRam = - (instanceConfig.javaMaxRam ?? launcherConfig.jvmMaxRam).toInt(); - - int width = launcherConfig.gameWindowWidth; - int height = launcherConfig.gameWindowHeight; - - String libraryFiles = instanceConfig.libraries - .getLibrariesLauncherArgs(side.isClient ? clientFile : null); - - showGameLogs = launcherConfig.showGameLogs; - - List args_ = [ - ...(side.isClient - ? ["-Dminecraft.client.jar=${clientFile.path}"] - : []), //Client Jar - "-Xmn${minRam}m", //最小記憶體 - "-Xmx${maxRam}m", //最大記憶體 - ]; - - args_.addAll((instanceConfig.javaJvmArgs ?? launcherConfig.jvmArgs) - .toList() - .cast()); - - List gameArgs = []; - - if (side.isClient) { - if (comparableVersion < Version(1, 13, 0)) { - args_.addAll(["-cp", libraryFiles]); - } - - if (argsMeta.containsKey('logging') && - argsMeta['logging'].containsKey('client')) { - Map logging = argsMeta['logging']['client']; - if (logging.containsKey('file')) { - Map fileMap = logging['file']; - String sha1 = fileMap['sha1']; - File file = GameRepository.getAssetsObjectFile(sha1); - if (file.existsSync()) { - gameArgs.add(logging['argument'] - .toString() - .replaceAll(r"${path}", file.path)); - } - } - } - - nativesTempDir = GameRepository.getNativesTempDir(); - copyPathSync(GameRepository.getNativesDir(gameVersionID).path, - nativesTempDir!.path); - - if (comparableVersion < Version(1, 13, 0)) { - args_.add("-Djava.library.path=${nativesTempDir!.absolute.path}"); - } - - Map variable = { - r"${auth_player_name}": account.username, - r"${version_name}": gameVersionID, - r"${game_directory}": instanceDir.absolute.path, - r"${assets_root}": GameRepository.getAssetsDir().path, - r"${assets_index_name}": instanceConfig.assetsID, - r"${auth_uuid}": account.uuid, - r"${auth_access_token}": account.accessToken, - r"${user_type}": - "mojang", // 可能是 legacy 或 mojang,但由於RPMLauncher不支援 legacy 帳號登入,所以是 mojang - r"${version_type}": "RPMLauncher_${LauncherInfo.getFullVersion()}", - r"${natives_directory}": nativesTempDir!.absolute.path, - r"${launcher_name}": "RPMLauncher", - r"${launcher_version}": LauncherInfo.getFullVersion(), - r"${classpath}": libraryFiles, - r"${user_properties}": "{}", - - /// Forge Mod Loader - r"${classpath_separator}": Util.getLibrarySeparator(), - r"${library_directory}": GameRepository.getLibraryGlobalDir().path, - }; - - if (loader == ModLoader.fabric || loader == ModLoader.vanilla) { - args_.addAll( - Arguments.getVanilla(argsMeta, variable, comparableVersion)); - } else if (loader == ModLoader.forge) { - args_.addAll(Arguments.getForge(argsMeta, variable, comparableVersion)); - } - gameArgs - .addAll(["--width", width.toString(), "--height", height.toString()]); - } else if (side.isServer) { - // args_.add(argsMeta["mainClass"]); - args_.addAll(["-jar", libraryFiles]); - args_.add("nogui"); - } - - args_.addAll(gameArgs); - - start(args_, gameVersionID); - } - - Future start(List minecraftArgs, String gameVersionID) async { - final List args = []; - final int javaVersion = instanceConfig.javaVersion; - final String javaPath = instanceConfig.storage["java_path_$javaVersion"] ?? - ConfigHelper.get("java_path_$javaVersion"); - - await chmod(javaPath); - - String exec = javaPath; - - String? wrapperCommand = launcherConfig.wrapperCommand; - - if (wrapperCommand != null) { - List commands = wrapperCommand.split(' '); - - if (commands.isNotEmpty) { - exec = commands[0]; - - commands.forEach((cmd) { - if (commands.indexOf(cmd) != 0) { - args.add(cmd); - } - }); - } - } - args.addAll(minecraftArgs); - - process = await Process.start(exec, args, - workingDirectory: instanceDir.absolute.path, - environment: {'APPDATA': dataHome.absolute.path}); - - setState(() {}); - process?.stdout.listen((data) { - late String string; - if (Platform.isWindows) { - string = Big5TransformDecode(data); - } else { - string = utf8.decode(data); - } - if (string.isEmpty) return; - logs.addLog(string); - if (showGameLogs && !searching) { - _logs = logs; - setState(() {}); - } else if (searching) { - _logs = logs - .whereLog((log) => - log.formattedString?.contains(_searchController.text) ?? false) - .toList(); - setState(() {}); - } - }); - process?.stderr.transform(utf8.decoder).listen((data) { - //error - errorLog_ += data; - }); - process?.exitCode.then((code) { - try { - if (nativesTempDir?.existsSync() ?? false) { - nativesTempDir?.deleteSync(recursive: true); - } - } catch (e) {} - - process = null; - instanceConfig.lastPlay = DateTime.now().millisecondsSinceEpoch; - - /// 143 代表手動強制關閉 - bool exitSuccessful = (code == 0 || code == 143) && - // 1.17離開遊戲的時候會有退出代碼 -1 - !(code == -1 && - instanceConfig.comparableVersion >= Version(1, 17, 0)); - logTimer.cancel(); - if (exitSuccessful) { - bool autoCloseGameLogsScreen = launcherConfig.autoCloseGameLogsScreen; - - if (autoCloseGameLogsScreen) { - if (WindowHandler.isMultiWindow) { - WindowHandler.close(); - } else { - navigator.pushNamed('home'); - } - } - } else { - showDialog( - context: navigator.context, - builder: (context) => GameCrash(errorCode: code, errorLog: errorLog_), - ); - } - }); - - const oneSec = Duration(seconds: 1); - logTimer = Timer.periodic(oneSec, (timer) { - instanceConfig.playTime = - instanceConfig.playTime + const Duration(seconds: 1).inMilliseconds; - - if (mounted) { - if (showGameLogs && !searching) { - if (logs.length > maxLogLength) { - //delete log - logs = - logs.getRange(logs.length - maxLogLength, logs.length).toList(); - } - if (_scrollController.hasClients && - _scrollController.position.pixels != - _scrollController.position.maxScrollExtent && - scrolling) { - _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - curve: Curves.easeOut, - duration: const Duration(milliseconds: 300), - ); - } - _logs = logs; - setState(() {}); - } else if (searching) { - _logs = logs - .whereLog((log) => - log.formattedString?.contains(_searchController.text) ?? - false) - .toList(); - setState(() {}); - } - } - }); - } - - @override - void dispose() { - try { - logTimer.cancel(); - _logs.clear(); - logs.clear(); - _searchController.dispose(); - _scrollController.dispose(); - } catch (e) {} - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - centerTitle: true, - leadingWidth: 550, - title: Text(I18n.format("log.game.log.title")), - leading: Row( - children: [ - IconButton( - icon: const Icon(Icons.close_outlined), - tooltip: I18n.format("log.game.kill"), - onPressed: () { - try { - logTimer.cancel(); - killGame(); - - if (nativesTempDir?.existsSync() ?? false) { - nativesTempDir?.deleteSync(recursive: true); - } - } catch (err) {} - if (WindowHandler.isMultiWindow) { - WindowHandler.close(); - } else { - Navigator.of(context).push(PushTransitions( - builder: (context) => const HomePage())); - } - }), - IconButton( - icon: const Icon(Icons.delete), - tooltip: I18n.format("log.game.clear"), - onPressed: () { - logs.clear(); - setState(() {}); - }, - ), - IconButton( - icon: const Icon(Icons.folder), - tooltip: I18n.format('log.folder.main'), - onPressed: () { - Util.openFileManager( - Directory(join(instanceDir.absolute.path, "logs"))); - }, - ), - IconButton( - icon: const Icon(Icons.folder), - tooltip: I18n.format('log.folder.crash'), - onPressed: () { - Util.openFileManager(Directory( - join(instanceDir.absolute.path, "crash-reports"))); - }, - ), - Checkbox( - onChanged: (bool? value) { - setState(() { - showGameLogs = value!; - launcherConfig.showGameLogs = value; - }); - }, - value: showGameLogs, - ), - I18nText("log.game.record"), - Checkbox( - onChanged: (bool? value) { - setState(() { - scrolling = value!; - }); - }, - value: scrolling, - ), - I18nText("log.game.scrolling"), - ], - ), - actions: [ - Container( - alignment: Alignment.center, - width: 250, - height: 250, - child: TextField( - textAlign: TextAlign.center, - controller: _searchController, - onChanged: (value) { - _logs = logs - .whereLog((log) => log.source - .toLowerCase() - .contains(value.toLowerCase())) - .toList(); - searching = value.isNotEmpty; - setState(() {}); - }, - decoration: InputDecoration( - hintText: I18n.format("log.game.search"), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.white12, width: 3.0), - ), - focusedBorder: const OutlineInputBorder( - borderSide: - BorderSide(color: Colors.lightBlue, width: 3.0), - ), - contentPadding: EdgeInsets.zero, - border: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - ), - )) - ], - ), - body: Column( - children: [ - Expanded( - flex: 15, - child: Listener( - onPointerSignal: (pointerSignal) { - if (pointerSignal is PointerScrollEvent) { - if (pointerSignal.scrollDelta.dy < -10 || - pointerSignal.scrollDelta.dy > 10) { - scrolling = false; - } else { - scrolling = true; - } - setState(() {}); - } - }, - child: - _LogView(scrollController: _scrollController, logs: _logs), - ), - ), - ...side.isServer - ? [ - const SizedBox( - height: 12, - ), - Row( - children: [ - const SizedBox( - width: 50, - ), - Expanded( - child: RPMLTextField( - hintText: I18n.format("log.server.command"), - controller: _serverCommandController, - onEditingComplete: () { - if (_serverCommandController.text.isNotEmpty) { - String command = _serverCommandController.text; - - if (!command.startsWith("/")) { - //如果指令不包含 / - command = "/$command"; - } - - process?.stdin.writeln(command); - - _serverCommandController.text = ""; - scrolling = true; - } - }, - ), - ), - const SizedBox( - width: 50, - ), - ], - ), - const SizedBox( - height: 12, - ), - ] - : [] - ], - )); - } -} - -class _LogView extends StatelessWidget { - const _LogView({ - Key? key, - required this.scrollController, - required this.logs, - }) : super(key: key); - - final ScrollController scrollController; - final GameLogs logs; - - @override - Widget build(BuildContext context) { - if (logs.isNotEmpty) { - return ListView.builder( - controller: scrollController, - itemCount: logs.length, - itemBuilder: (context, index) { - GameLog log = logs[index]; - return log.widget; - }); - } else { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const RWLLoading(), - const SizedBox( - height: 12, - ), - I18nText("log.game.log.none", - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 30, - )), - ], - ); - } - } -} - -class LogScreen extends StatefulWidget { - final String instanceUUID; - - const LogScreen({required this.instanceUUID}); - - @override - State createState() => _LogScreenState(); -} diff --git a/lib/screen/RecommendedModpackScreen.dart b/lib/screen/RecommendedModpackScreen.dart deleted file mode 100644 index f7810c7db..000000000 --- a/lib/screen/RecommendedModpackScreen.dart +++ /dev/null @@ -1,218 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/Game/MinecraftVersion.dart'; -import 'package:rpmlauncher/model/Game/RecommendedModpack.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; -import 'package:rpmlauncher/widget/AddInstance.dart'; -import 'package:rpmlauncher/widget/RPMNetworkImage.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmlauncher/widget/WIPWidget.dart'; -import 'package:rpmlauncher/pages/curseforge_mod_version.dart' - as curseforge_version; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart'; - -class RecommendedModpackScreen extends StatefulWidget { - const RecommendedModpackScreen({Key? key}) : super(key: key); - - @override - State createState() => - _RecommendedModpackScreenState(); -} - -class _RecommendedModpackScreenState extends State { - Future get() async { - Response response = await RPMHttpClient().get(recommendedModpack); - - return RecommendedModpacks.fromList( - RPMHttpClient.json(response.data).cast>()); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: get(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - RecommendedModpacks modpacks = snapshot.data; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText( - 'version.recommended_modpack.title', - style: TextStyle(fontSize: 35, color: Colors.yellow[700]), - ), - const SizedBox(height: 15), - Expanded( - child: ListView.builder( - itemCount: modpacks.length, - itemBuilder: (BuildContext context, int index) { - RecommendedModpack modpack = modpacks[index]; - - return Column( - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - width: 20, - ), - Expanded( - child: RPMNetworkImage( - src: modpack.image, - width: 450, - height: 250)), - Expanded( - child: ListTile( - title: Text(modpack.name, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 35)), - subtitle: Text(modpack.description, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 20)), - ), - ), - _OptionWidget(modpack: modpack) - ], - ), - const Divider() - ], - ); - }), - ), - ], - ); - } else { - return const RWLLoading(logo: true); - } - }, - ); - } -} - -class _OptionWidget extends StatelessWidget { - const _OptionWidget({ - Key? key, - required this.modpack, - }) : super(key: key); - - final RecommendedModpack modpack; - - @override - Widget build(BuildContext context) { - List rowWidget = [ - ElevatedButton.icon( - onPressed: () { - if (modpack.type == RecommendedModpackType.instance) { - showDialog( - context: context, - builder: (context) => InstanceTask(modpack: modpack), - ); - } else if (modpack.type == - RecommendedModpackType.curseforgeModpack) { - showDialog(context: context, builder: (context) => WiPWidget()); - } - }, - icon: const Icon(Icons.download), - label: I18nText('gui.install')), - const SizedBox( - width: 20, - ), - ]; - - if (modpack.link != null) { - rowWidget.addAll([ - ElevatedButton.icon( - onPressed: () => Util.openUri(modpack.link!), - icon: const Icon(Icons.link), - label: I18nText('version.recommended_modpack.link')), - const SizedBox(width: 20) - ]); - } - - return RowScrollView( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: rowWidget, - ), - ); - } -} - -class InstanceTask extends StatelessWidget { - const InstanceTask({ - Key? key, - required this.modpack, - }) : super(key: key); - - final RecommendedModpack modpack; - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: MCVersionManifest.getVanilla(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return AddInstanceDialog( - modpack.name, - snapshot.data!.versions.firstWhere( - (version) => version.comparableVersion == modpack.version), - modpack.loader, - modpack.loaderVersion, - MinecraftSide.client, - onInstalled: (instance) { - return Future.sync(() async { - await RPMHttpClient() - .download(modpack.image, join(instance.path, 'icon.png')); - - if (modpack.mods != null) { - for (Map mod in modpack.mods!) { - int cfModId = mod['curseforgeID']; - - List files = await RPMTWApiClient - .instance.curseforgeResource - .getModFiles(cfModId, - gameVersion: instance.config.version, - modLoaderType: - instance.config.loaderEnum.toCurseForgeType()); - - if (files.isEmpty) { - continue; - } - - files.sort((a, b) => DateTime.parse(b.fileDate) - .compareTo(DateTime.parse(a.fileDate))); - - final context = navigator.context; - if (context.mounted) { - await showDialog( - context: context, - builder: (context) => curseforge_version.Task( - files.first, - InstanceRepository.getModRootDir(instance.uuid), - instance.config.version, - instance.config.loaderEnum, - autoClose: true)); - } - } - } - }); - }, - ); - } else { - return const RWLLoading(); - } - }, - ); - } -} diff --git a/lib/screen/Settings.dart b/lib/screen/Settings.dart deleted file mode 100644 index 18cfd0b36..000000000 --- a/lib/screen/Settings.dart +++ /dev/null @@ -1,615 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/i18n/language_selector.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/util/launcher_path.dart'; -import 'package:rpmlauncher/util/theme.dart'; -import 'package:rpmlauncher/util/updater.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/memory_slider.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmlauncher/widget/settings/java_path.dart'; -import 'package:rpmlauncher/widget/settings/jvm_args_settings.dart'; -import 'package:rpmlauncher/widget/settings/theme_selector.dart'; - -class _SettingScreenState extends State { - Color get primaryColor => ThemeUtil.getTheme().colorScheme.primary; - - TextEditingController gameWindowWidthController = TextEditingController(); - TextEditingController gameWindowHeightController = TextEditingController(); - TextEditingController wrapperCommandController = TextEditingController(); - TextEditingController gameLogMaxLineCountController = TextEditingController(); - - late bool checkAssetsIntegrity; - late bool showGameLogs; - late bool autoDownloadModDependencies; - late bool autoFullScreen; - late bool checkAccountValidity; - late bool autoCloseGameLogsScreen; - late bool discordRichPresence; - - String? backgroundPath; - - VersionTypes updateChannel = launcherConfig.updateChannel; - - int _selectedIndex = 0; - - @override - void initState() { - checkAccountValidity = launcherConfig.checkAccountValidity; - autoCloseGameLogsScreen = launcherConfig.autoCloseGameLogsScreen; - checkAssetsIntegrity = launcherConfig.checkAssetsIntegrity; - showGameLogs = launcherConfig.showGameLogs; - autoDownloadModDependencies = launcherConfig.autoDownloadModDependencies; - autoFullScreen = launcherConfig.autoFullScreen; - discordRichPresence = launcherConfig.discordRichPresence; - - gameWindowWidthController.text = launcherConfig.gameWindowWidth.toString(); - gameWindowHeightController.text = - launcherConfig.gameWindowHeight.toString(); - gameLogMaxLineCountController.text = - launcherConfig.gameLogMaxLineCount.toString(); - wrapperCommandController.text = launcherConfig.wrapperCommand ?? ''; - super.initState(); - } - - @override - void dispose() { - gameWindowWidthController.dispose(); - gameWindowHeightController.dispose(); - wrapperCommandController.dispose(); - gameLogMaxLineCountController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Dialog( - clipBehavior: Clip.antiAlias, - insetPadding: - const EdgeInsets.symmetric(horizontal: 100.0, vertical: 60.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - NavigationRail( - selectedIndex: _selectedIndex, - onDestinationSelected: (int index) { - setState(() { - _selectedIndex = index; - }); - }, - labelType: NavigationRailLabelType.all, - leading: Padding( - padding: const EdgeInsets.fromLTRB(0, 10, 0, 20), - child: FloatingActionButton( - tooltip: I18n.format('gui.close'), - elevation: 0, - onPressed: () { - navigator.pop(); - }, - child: const Icon(Icons.close), - ), - ), - destinations: [ - NavigationRailDestination( - icon: const Icon(Icons.code_rounded), - selectedIcon: const Icon(Icons.code), - label: I18nText('settings.java.title'), - ), - NavigationRailDestination( - icon: const Icon(Icons.web_asset_rounded), - selectedIcon: const Icon(Icons.web_asset), - label: I18nText('settings.appearance.title'), - ), - NavigationRailDestination( - icon: const Icon(Icons.settings_outlined), - selectedIcon: const Icon(Icons.settings), - label: I18nText('settings.advanced.title'), - ), - NavigationRailDestination( - icon: const Icon(Icons.bug_report_outlined), - selectedIcon: const Icon(Icons.bug_report), - label: I18nText('settings.debug.title'), - ), - ], - ), - const VerticalDivider(thickness: 1, width: 1), - Flexible( - child: Align( - alignment: Alignment.topCenter, - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - AppBar( - title: I18nText('settings.title'), - centerTitle: true, - leading: const SizedBox(), - ), - const SizedBox(height: 8), - if (_selectedIndex == 0) const _JavaSettings(), - if (_selectedIndex == 1) const _AppearanceSettings(), - if (_selectedIndex == 2) const _AdvancedSettings(), - if (_selectedIndex == 3) const _DebugOption(), - const SizedBox(height: 18) - ], - ), - ), - ), - ) - ], - ), - ), - ); - } -} - -class SettingScreen extends StatefulWidget { - static const String route = '/settings'; - - @override - State createState() => _SettingScreenState(); -} - -class _JavaSettings extends StatefulWidget { - const _JavaSettings(); - - @override - State<_JavaSettings> createState() => _JavaSettingsState(); -} - -class _JavaSettingsState extends State<_JavaSettings> { - @override - Widget build(BuildContext context) { - final titleStyle = Theme.of(context).textTheme.titleLarge; - - return SafeArea( - child: Column(children: [ - I18nText( - 'settings.java.path', - style: titleStyle, - textAlign: TextAlign.center, - ), - const JavaPathSettings(), - const Divider(), - SwitchListTile( - value: launcherConfig.autoInstallJava, - onChanged: (value) { - setState(() { - launcherConfig.autoInstallJava = value; - }); - }, - title: Text( - I18n.format('settings.java.auto'), - style: titleStyle, - textAlign: TextAlign.center, - ), - ), - const Divider(), - MemorySlider( - value: launcherConfig.jvmMaxRam, - onChanged: (memory) { - launcherConfig.jvmMaxRam = memory; - }), - const Divider(), - JVMArgsSettings( - value: launcherConfig.jvmArgs, - onChanged: (value) { - launcherConfig.jvmArgs = value; - }) - ])); - } -} - -class _AppearanceSettings extends StatefulWidget { - const _AppearanceSettings(); - - @override - State<_AppearanceSettings> createState() => _AppearanceSettingsState(); -} - -class _AppearanceSettingsState extends State<_AppearanceSettings> { - late TextEditingController gameWindowWidthController; - late TextEditingController gameWindowHeightController; - - @override - void initState() { - gameWindowWidthController = - TextEditingController(text: launcherConfig.gameWindowWidth.toString()); - gameWindowHeightController = - TextEditingController(text: launcherConfig.gameWindowHeight.toString()); - super.initState(); - } - - @override - void dispose() { - gameWindowWidthController.dispose(); - gameWindowHeightController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final titleStyle = Theme.of(context).textTheme.titleLarge; - - return Column( - children: [ - LanguageSelectorWidget( - onChanged: () => setState(() {}), - ), - const Divider(), - Text( - I18n.format('settings.appearance.theme'), - style: titleStyle, - ), - const SizedBox(height: 12), - const ThemeSelector(), - const Divider(), - Text( - I18n.format('settings.appearance.background.title'), - style: titleStyle, - ), - Text( - launcherConfig.backgroundImageFile?.path ?? - I18n.format('gui.default'), - style: const TextStyle(fontSize: 18), - textAlign: TextAlign.center), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - OutlinedButton( - onPressed: () async { - final result = - await FilePicker.platform.pickFiles(type: FileType.image); - if (result != null) { - PlatformFile file = result.files.single; - launcherConfig.backgroundImageFile = File(file.path!); - } - setState(() {}); - }, - child: - Text(I18n.format('settings.appearance.background.pick'))), - const SizedBox(width: 10), - OutlinedButton( - onPressed: () { - launcherConfig.backgroundImageFile = null; - setState(() {}); - }, - child: - Text(I18n.format('settings.appearance.background.reset'))), - ], - ), - const Divider(), - Text( - I18n.format('settings.appearance.window.size.title'), - style: titleStyle, - ), - const SizedBox( - height: 12, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 280, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: gameWindowWidthController, - hintText: '854', - verify: (value) => int.tryParse(value) != null, - onChanged: (value) async { - launcherConfig.gameWindowWidth = int.tryParse(value) ?? 854; - }, - ), - ), - const SizedBox(width: 12), - const Icon(Icons.clear), - const SizedBox(width: 12), - SizedBox( - width: 280, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: gameWindowHeightController, - hintText: '480', - verify: (value) => int.tryParse(value) != null, - onChanged: (value) async { - launcherConfig.gameWindowHeight = int.tryParse(value) ?? 480; - }, - ), - ), - ], - ) - ], - ); - } -} - -class _AdvancedSettings extends StatefulWidget { - const _AdvancedSettings(); - - @override - State<_AdvancedSettings> createState() => _AdvancedSettingsState(); -} - -class _AdvancedSettingsState extends State<_AdvancedSettings> { - late TextEditingController gameLogMaxLineCountController; - late TextEditingController wrapperCommandController; - - @override - void initState() { - gameLogMaxLineCountController = TextEditingController( - text: launcherConfig.gameLogMaxLineCount.toString()); - wrapperCommandController = - TextEditingController(text: launcherConfig.wrapperCommand); - - super.initState(); - } - - @override - void dispose() { - gameLogMaxLineCountController.dispose(); - wrapperCommandController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final titleStyle = Theme.of(context).textTheme.titleLarge; - - return Column( - children: [ - I18nText('settings.advanced.tips', - style: Theme.of(context) - .textTheme - .headlineSmall - ?.copyWith(color: Colors.red), - textAlign: TextAlign.center), - const Divider(), - ListTile( - title: I18nText( - 'settings.advanced.datahome', - style: titleStyle, - textAlign: TextAlign.center, - ), - subtitle: SelectableText(dataHome.absolute.path, - textAlign: TextAlign.center, - style: TextStyle( - color: Theme.of(context).hintColor, - )), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - OutlinedButton.icon( - onPressed: () async { - final path = await FilePicker.platform.getDirectoryPath(); - - if (path != null) { - launcherConfig.launcherDataDir = Directory(path); - setState(() {}); - - if (context.mounted) { - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => - const _ChangeDataHomeSuccessful()); - } - } - }, - icon: const Icon(Icons.folder), - label: I18nText('settings.advanced.datahome.change')), - const SizedBox(width: 12), - OutlinedButton.icon( - onPressed: () { - launcherConfig.launcherDataDir = - LauncherPath.defaultDataHome; - setState(() {}); - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => - const _ChangeDataHomeSuccessful()); - }, - icon: const Icon(Icons.restore), - label: I18nText('settings.advanced.datahome.restore')) - ], - ), - ), - const Divider(), - SwitchListTile( - value: launcherConfig.checkAssetsIntegrity, - onChanged: (value) { - setState(() { - launcherConfig.checkAssetsIntegrity = value; - }); - }, - title: I18nText('settings.advanced.assets.check', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.showGameLogs, - onChanged: (value) { - setState(() { - launcherConfig.showGameLogs = value; - }); - }, - title: I18nText('settings.advanced.show_log', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.autoDownloadModDependencies, - onChanged: (value) { - setState(() { - launcherConfig.autoDownloadModDependencies = value; - }); - }, - title: I18nText('settings.advanced.auto_dependencies', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.autoFullScreen, - onChanged: (value) { - setState(() { - launcherConfig.autoFullScreen = value; - }); - }, - title: I18nText('settings.advanced.auto_full_screen', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.checkAccountValidity, - onChanged: (value) { - setState(() { - launcherConfig.checkAccountValidity = value; - }); - }, - title: I18nText('settings.advanced.validate_account', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.autoCloseGameLogsScreen, - onChanged: (value) { - setState(() { - launcherConfig.autoCloseGameLogsScreen = value; - }); - }, - title: I18nText('settings.advanced.auto_close_log_screen', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.discordRichPresence, - onChanged: (value) { - setState(() { - launcherConfig.discordRichPresence = value; - }); - }, - title: I18nText('settings.advanced.discord_rpc', - style: titleStyle, textAlign: TextAlign.center), - ), - const Divider(), - I18nText('settings.advanced.update_channel', - style: titleStyle, textAlign: TextAlign.center), - SegmentedButton( - segments: [ - ButtonSegment( - value: VersionTypes.stable, - icon: const Icon(Icons.check_circle), - label: Text(Updater.toI18nString(VersionTypes.stable)), - ), - ButtonSegment( - value: VersionTypes.dev, - icon: const Icon(Icons.bug_report), - label: Text(Updater.toI18nString(VersionTypes.dev)), - ), - ], - selected: {launcherConfig.updateChannel}, - onSelectionChanged: (newSelection) { - setState(() { - launcherConfig.updateChannel = newSelection.first; - }); - }, - ), - const Divider(), - ListTile( - title: I18nText('settings.advanced.max.log', - style: titleStyle, textAlign: TextAlign.center), - trailing: SizedBox( - width: 300, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: gameLogMaxLineCountController, - verify: (value) => int.tryParse(value) != null, - hintText: '300', - onChanged: (value) async { - launcherConfig.gameLogMaxLineCount = int.parse(value); - }, - ), - ), - ), - const SizedBox(height: 12), - ListTile( - title: I18nText('settings.advanced.wrapper_command', - style: titleStyle, textAlign: TextAlign.center), - trailing: SizedBox( - width: 300, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: wrapperCommandController, - hintText: 'Executable program', - onChanged: (value) { - launcherConfig.wrapperCommand = value.isEmpty ? null : value; - }, - ), - ), - ), - ], - ); - } -} - -class _ChangeDataHomeSuccessful extends StatelessWidget { - const _ChangeDataHomeSuccessful({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: I18nText('settings.advanced.datahome.change.successful'), - actions: [ - OkClose( - onOk: () { - Util.exit(0); - }, - ) - ], - ); - } -} - -class _DebugOption extends StatefulWidget { - const _DebugOption(); - - @override - State<_DebugOption> createState() => _DebugOptionState(); -} - -class _DebugOptionState extends State<_DebugOption> { - @override - Widget build(BuildContext context) { - return Column( - children: [ - I18nText('settings.advanced.tips', - style: Theme.of(context) - .textTheme - .headlineSmall - ?.copyWith(color: Colors.red), - textAlign: TextAlign.center), - const Divider(), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FloatingActionButton.extended( - onPressed: () async { - LauncherPath.currentConfigHome.deleteSync(recursive: true); - if (dataHome.existsSync()) { - dataHome.deleteSync(recursive: true); - } - - await Util.exit(0); - }, - label: I18nText('settings.debug.delete_all_data', - style: Theme.of(context).textTheme.titleLarge)), - ], - ), - ], - ); - } -} diff --git a/lib/screen/about.dart b/lib/screen/about.dart deleted file mode 100644 index 138e1de35..000000000 --- a/lib/screen/about.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:line_icons/line_icons.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; - -import 'package:rpmlauncher/util/data.dart'; - -class AboutScreenState extends State { - final TextStyle title_ = const TextStyle(fontSize: 20); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(I18n.format('homepage.about')), - centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - tooltip: I18n.format('gui.back'), - onPressed: () { - navigator.pop(); - }, - ), - ), - body: ListView( - children: [ - const SizedBox( - height: 12, - ), - Text(I18n.format('about.dev.frame'), - style: title_, textAlign: TextAlign.center), - Text(I18n.format('about.dev.language'), - style: title_, textAlign: TextAlign.center), - Text( - '${I18n.format('about.version.title')} ${LauncherInfo.getFullVersion()}', - style: title_, - textAlign: TextAlign.center), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('${I18n.format('about.version.type')} ', - style: title_, textAlign: TextAlign.center), - LauncherInfo.getVersionTypeText(), - ], - ), - const SizedBox(height: 12), - Text(I18n.format('about.link'), - style: const TextStyle(fontSize: 25, color: Colors.blue), - textAlign: TextAlign.center), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - onPressed: () { - Util.openUri(LauncherInfo.homePageUrl); - }, - icon: const Icon(LineIcons.home), - tooltip: I18n.format('homepage.website'), - ), - IconButton( - onPressed: () { - Util.openUri(LauncherInfo.githubRepoUrl); - }, - icon: const Icon(LineIcons.github), - tooltip: I18n.format('about.github'), - ), - IconButton( - onPressed: () { - Util.openUri(LauncherInfo.discordUrl); - }, - icon: const Icon(LineIcons.discord), - tooltip: I18n.format('about.discord'), - ), - IconButton( - icon: const Icon(Icons.book_outlined), - onPressed: () { - showLicensePage( - applicationName: LauncherInfo.getUpperCaseName(), - applicationVersion: LauncherInfo.getFullVersion(), - applicationIcon: Image.asset('assets/images/Logo.png'), - context: context, - ); - }, - tooltip: I18n.format('about.license.show'), - ), - ], - ), - const SizedBox( - height: 12, - ), - Center( - child: ElevatedButton( - onPressed: () { - // Rickroll easter egg - bool value = Random().nextBool(); - if (value) { - Util.openUri('https://youtu.be/dQw4w9WgXcQ'); - } else { - Util.openUri('https://youtu.be/dMTy6C4UiQ4'); - } - }, - child: I18nText('about.rickrolling'))) - ], - ), - persistentFooterButtons: const [ - Center( - child: - Text('Copyright © The RPMTW Team 2021-2022 All Right Reserved.'), - ) - ], - ); - } -} - -class AboutScreen extends StatefulWidget { - @override - AboutScreenState createState() => AboutScreenState(); -} diff --git a/lib/screen/account.dart b/lib/screen/account.dart deleted file mode 100644 index 8269da5e5..000000000 --- a/lib/screen/account.dart +++ /dev/null @@ -1,270 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/account/mojang_account_handler.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/model/account/Account.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/widget/dialog/CheckDialog.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmlauncher/util/launcher_path.dart'; - -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; -import 'ms_oauth_login.dart'; - -class _AccountScreenState extends State { - int? chooseIndex; - - @override - void initState() { - chooseIndex = AccountStorage().getIndex(); - super.initState(); - LauncherPath.currentConfigHome.watch(recursive: true).listen((event) { - if (absolute(event.path) == - absolute(GameRepository.getAccountFile().path) && - mounted) { - setState(() {}); - } - }); - } - - TextStyle title_ = const TextStyle( - fontSize: 20.0, - ); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(I18n.format('account.title')), - centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - tooltip: I18n.format('gui.back'), - onPressed: () { - navigator.pop(); - }, - ), - ), - body: Container( - height: double.infinity, - width: double.infinity, - alignment: Alignment.center, - child: Builder( - builder: (context) { - if (AccountStorage().hasAccount) { - return ListView.builder( - itemBuilder: (context, index) { - final account = AccountStorage().getByIndex(index); - - return ListTile( - tileColor: chooseIndex == index - ? Theme.of(context).colorScheme.onInverseSurface - : null, - onTap: () { - chooseIndex = index; - AccountStorage().setIndex(index); - if (mounted) { - setState(() {}); - } - }, - title: - Text(account.username, textAlign: TextAlign.center), - subtitle: I18nText('account.type', - args: { - 'account_type': account.type.name.toCapitalized() - }, - textAlign: TextAlign.center), - leading: SizedBox( - width: 50, height: 50, child: account.imageWidget), - minLeadingWidth: 50, - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Icons.contact_page), - tooltip: I18n.format('account.skin.tooltip'), - onPressed: () { - showDialog( - context: context, - builder: (context) { - return _UploadSkinDialog(account: account); - }); - }, - ), - IconButton( - icon: const Icon(Icons.delete), - tooltip: I18n.format('account.delete.tooltip'), - onPressed: () { - showDialog( - context: context, - builder: (context) { - return CheckDialog( - title: I18n.format( - 'account.delete.tooltip'), - message: I18n.format( - 'account.delete.content'), - onPressedOK: (context) { - Navigator.of(context).pop(); - AccountStorage().removeByIndex(index); - if (mounted) { - setState(() {}); - } - }); - }); - }, - ), - ], - )); - }, - itemCount: AccountStorage().getCount(), - ); - } else { - return I18nText('account.delete.notfound', - style: const TextStyle(fontSize: 30)); - } - }, - ), - ), - floatingActionButton: FloatingActionButton.extended( - icon: const Icon(Icons.add), - onPressed: () { - showDialog( - context: context, - builder: (context) => MSLoginWidget(), - ); - }, - label: I18nText( - 'account.add.title', - textAlign: TextAlign.center, - style: title_, - ), - ), - ); - } -} - -class _UploadSkinDialog extends StatefulWidget { - const _UploadSkinDialog({ - Key? key, - required this.account, - }) : super(key: key); - - final Account account; - - @override - State<_UploadSkinDialog> createState() => _UploadSkinDialogState(); -} - -class _UploadSkinDialogState extends State<_UploadSkinDialog> { - final List skinTypeItems = [ - I18n.format('account.skin.variant.classic'), - I18n.format('account.skin.variant.slim') - ]; - late String skinTypeItem; - - @override - void initState() { - skinTypeItem = skinTypeItems.first; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(I18n.format('gui.tips.info'), textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText('account.skin.tips', textAlign: TextAlign.center), - DropdownButton( - value: skinTypeItem, - onChanged: (String? newValue) { - skinTypeItem = newValue!; - setState(() {}); - }, - items: skinTypeItems.map>((String value) { - return DropdownMenuItem( - value: value, - child: Text( - value, - ), - ); - }).toList(), - ), - ], - ), - actions: [ - TextButton( - onPressed: () async { - final FilePickerResult? result = - await FilePicker.platform.pickFiles(type: FileType.image); - - if (result != null) { - PlatformFile file = result.files.single; - - if (!mounted) return; - Navigator.pop(context); - showDialog( - context: context, - builder: (context) { - return FutureBuilder( - future: MojangHandler.updateSkin( - widget.account.accessToken, - File(file.path!), - skinTypeItem), - builder: (context, snapshot) { - if (snapshot.hasData) { - if (snapshot.data == true) { - return AlertDialog( - title: Text(I18n.format('gui.tips.info')), - content: I18nText('account.upload.success'), - actions: const [OkClose()], - ); - } else { - return AlertDialog( - title: I18nText('gui.error.info'), - content: I18nText('account.upload.success'), - actions: const [OkClose()], - ); - } - } else { - return AlertDialog( - title: I18nText('account.upload.uploading'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: const [ - SizedBox( - height: 10, - ), - RWLLoading(), - SizedBox( - height: 10, - ), - ], - ), - ); - } - }); - }); - } - }, - child: I18nText('account.skin.file.select')), - ], - ); - } -} - -class AccountScreen extends StatefulWidget { - static const String route = '/account'; - static Future push(BuildContext context) { - return Navigator.of(context).pushNamed(route); - } - - @override - State createState() => _AccountScreenState(); -} diff --git a/lib/screen/check_assets.dart b/lib/screen/check_assets.dart deleted file mode 100644 index 30626f0a6..000000000 --- a/lib/screen/check_assets.dart +++ /dev/null @@ -1,147 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'dart:isolate'; - -import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; -import 'package:path/path.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/handler/window_handler.dart'; -import 'package:rpmlauncher/launcher/CheckData.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/IO/isolate_option.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/util/data.dart'; - -class _CheckAssetsScreenState extends State { - double checkAssetsProgress = 0.0; - - @override - void initState() { - super.initState(); - - final InstanceConfig instanceConfig = - InstanceRepository.instanceConfig(basename(widget.instanceDir.path))!; - - if (launcherConfig.checkAssetsIntegrity && - instanceConfig.sideEnum.isClient) { - //是否檢查資源檔案完整性 - thread(instanceConfig); - } else { - checkAssetsProgress = 1.0; - check(); - } - } - - void check() { - if (checkAssetsProgress == 1.0) { - Navigator.pop(this.context); - WindowHandler.createSubWindow( - this.context, - '/instance/${InstanceRepository.getUUIDByDir(widget.instanceDir)}/launcher', - ); - } else { - setState(() {}); - } - } - - Future thread(InstanceConfig config) async { - ReceivePort port = ReceivePort(); - port.listen((message) { - if (mounted) { - checkAssetsProgress = double.parse(message.toString()); - check(); - } - }); - - await compute(instanceAssets, IsolateOption.create(config, ports: [port])); - } - - static instanceAssets(IsolateOption option) async { - option.init(); - InstanceConfig config = option.argument; - - int totalAssetsFiles; - int doneAssetsFiles = 0; - List downloads = []; - String assetsID = config.assetsID; - File indexFile = File( - join(dataHome.absolute.path, 'assets', 'indexes', '$assetsID.json')); - - if (!indexFile.existsSync()) { - //如果沒有資源索引檔案則下載 - MinecraftMeta meta = await Util.getVanillaVersionMeta(config.version); - String assetsIndexUrl = meta['assetIndex']['url']; - - Response response = await RPMHttpClient().get(assetsIndexUrl, - options: Options(responseType: ResponseType.json)); - if (response.statusCode == 200) { - indexFile.createSync(recursive: true); - indexFile.writeAsStringSync(json.encode(response.data)); - } - } - - Directory assetsObjectDir = - Directory(join(dataHome.absolute.path, 'assets', 'objects')); - Map indexJson = json.decode(indexFile.readAsStringSync()); - Map objects = indexJson['objects'].cast(); - - totalAssetsFiles = objects.keys.length; - - for (var i in objects.keys) { - String hash = objects[i]!['hash'].toString(); - File assetsFile = - File(join(assetsObjectDir.absolute.path, hash.substring(0, 2), hash)); - if (assetsFile.existsSync() && - CheckData.checkSha1Sync(assetsFile, hash)) { - doneAssetsFiles++; - option.sendData(doneAssetsFiles / totalAssetsFiles); - } else { - downloads.add(hash); - option.sendData(doneAssetsFiles / totalAssetsFiles); - } - } - if (doneAssetsFiles < totalAssetsFiles) { - downloads.forEach((assetsHash) async { - File file = File(join(assetsObjectDir.absolute.path, - assetsHash.substring(0, 2), assetsHash)) - ..createSync(recursive: true); - await http - .get(Uri.parse( - 'https://resources.download.minecraft.net/${assetsHash.substring(0, 2)}/$assetsHash')) - .then((response) async { - await file.writeAsBytes(response.bodyBytes); - }); - }); - option.sendData(doneAssetsFiles / totalAssetsFiles); - } - } - - @override - Widget build(BuildContext context) { - return Center( - child: AlertDialog( - title: Text(I18n.format('launcher.assets.check'), - textAlign: TextAlign.center), - content: LinearProgressIndicator( - value: checkAssetsProgress, - ), - )); - } -} - -class CheckAssetsScreen extends StatefulWidget { - final Directory instanceDir; - - const CheckAssetsScreen({required this.instanceDir}); - - @override - State createState() => _CheckAssetsScreenState(); -} diff --git a/lib/screen/edit.dart b/lib/screen/edit.dart deleted file mode 100644 index 8f1af5191..000000000 --- a/lib/screen/edit.dart +++ /dev/null @@ -1,718 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:archive/archive.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:line_icons/line_icons.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/handler/window_handler.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/UI/ViewOptions.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/screen/instance_independent_setting.dart'; -import 'package:rpmlauncher/util/theme.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/view/Edit/WorldView.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; -import 'package:rpmlauncher/widget/DeleteFileWidget.dart'; -import 'package:rpmlauncher/widget/FileSwitchBox.dart'; -import 'package:rpmlauncher/view/Edit/mods_view.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/view/OptionsView.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmlauncher/widget/ShaderpackSourceSelection.dart'; -import 'package:rpmlauncher/widget/WIPWidget.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:window_size/window_size.dart'; - -import '../util/util.dart'; - -class EditInstance extends StatefulWidget { - final String instanceUUID; - - const EditInstance({required this.instanceUUID}); - - @override - State createState() => _EditInstanceState(); -} - -class _EditInstanceState extends State { - late Instance instance; - - String get instanceUUID => widget.instanceUUID; - Directory get instanceDir => instance.directory; - InstanceConfig get instanceConfig => instance.config; - - late Directory screenshotDir; - late Directory resourcePackDir; - late Directory shaderpackDir; - late Directory modRootDir; - late Directory worldRootDir; - - int selectedIndex = 0; - - late int chooseIndex; - - TextEditingController nameController = TextEditingController(); - - late StreamSubscription screenshotDirEvent; - - late ThemeData theme; - late Color primaryColor; - - @override - void initState() { - instance = Instance.fromUUID(instanceUUID)!; - setWindowTitle("RPMLauncher - ${instance.name}"); - chooseIndex = 0; - screenshotDir = InstanceRepository.getScreenshotRootDir(instanceUUID); - resourcePackDir = InstanceRepository.getResourcePackRootDir(instanceUUID); - worldRootDir = InstanceRepository.getWorldRootDir(instanceUUID); - modRootDir = InstanceRepository.getModRootDir(instanceUUID); - nameController.text = instanceConfig.name; - shaderpackDir = InstanceRepository.getShaderpackRootDir(instanceUUID); - - primaryColor = ThemeUtil.getTheme().colorScheme.primary; - - super.initState(); - - Util.createFolderOptimization(screenshotDir); - Util.createFolderOptimization(worldRootDir); - Util.createFolderOptimization(resourcePackDir); - Util.createFolderOptimization(shaderpackDir); - Util.createFolderOptimization(modRootDir); - - screenshotDirEvent = screenshotDir.watch().listen((event) { - if (!screenshotDir.existsSync()) screenshotDirEvent.cancel(); - setState(() {}); - }); - } - - @override - void dispose() { - screenshotDirEvent.cancel(); - nameController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(I18n.format("edit.instance.title")), - centerTitle: true, - leading: Builder(builder: (context) { - if (WindowHandler.isMultiWindow) { - return IconButton( - icon: const Icon(Icons.close), - tooltip: I18n.format("gui.close"), - onPressed: () { - WindowHandler.close(); - }, - ); - } else { - return IconButton( - icon: const Icon(Icons.arrow_back), - tooltip: I18n.format("gui.back"), - onPressed: () { - screenshotDirEvent.cancel(); - navigator.pop(); - }, - ); - } - }), - ), - body: OptionsView( - gripSize: 3, - optionWidgets: (setState) { - return [ - ListView( - children: [ - instance.imageWidget(width: 150, height: 150), - const SizedBox( - height: 12, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () async { - final result = await FilePicker.platform - .pickFiles(type: FileType.image); - if (result == null) return; - File file = File(result.files.single.path!); - file.copySync( - join(instanceDir.absolute.path, "icon.png")); - }, - child: Text( - I18n.format( - "edit.instance.homepage.instance.image"), - style: const TextStyle(fontSize: 18), - )), - ], - ), - const SizedBox( - height: 12, - ), - Row( - children: [ - const SizedBox( - width: 12, - ), - Text( - I18n.format("edit.instance.homepage.instance.name"), - style: const TextStyle(fontSize: 18), - ), - Expanded( - child: RPMLTextField( - controller: nameController, - textAlign: TextAlign.center, - hintText: I18n.format( - "edit.instance.homepage.instance.enter"), - onChanged: (value) { - setState(() {}); - }, - ), - ), - const SizedBox( - width: 12, - ), - ElevatedButton( - onPressed: () { - if (nameController.text.isNotEmpty) { - instanceConfig.name = nameController.text; - } else { - ScaffoldMessenger.of(navigator.context) - .showSnackBar(SnackBar( - behavior: SnackBarBehavior.floating, - margin: const EdgeInsets.all(50), - content: I18nText( - "edit.instance.homepage.instance.name.empty", - style: const TextStyle( - fontFamily: 'font'), - ))); - } - setState(() {}); - }, - child: Text( - I18n.format("gui.save"), - style: const TextStyle(fontSize: 18), - )), - const SizedBox( - width: 12, - ), - ], - ), - const SizedBox(height: 24), - Text( - I18n.format('edit.instance.homepage.info.title'), - style: const TextStyle(fontSize: 20), - textAlign: TextAlign.center, - ), - const SizedBox(height: 12), - RowScrollView( - child: Row( - children: [ - infoCard(I18n.format("game.version"), - instanceConfig.version), - infoCard(I18n.format("version.list.mod.loader"), - instanceConfig.loaderEnum.i18nString), - Stack( - children: [ - infoCard( - I18n.format( - 'edit.instance.homepage.info.loader.version'), - instanceConfig.loaderVersion ?? "", - show: instanceConfig.loaderEnum != - ModLoader.vanilla), - Positioned( - top: 5, - right: 10, - child: IconButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => WiPWidget()); - }, - icon: const Icon(Icons.settings), - iconSize: 25, - tooltip: I18n.format( - 'edit.instance.homepage.info.loader.version.change'), - ), - // bottom: 10, - ) - ], - ), - infoCard( - I18n.format( - 'edit.instance.homepage.info.mod.count'), - modRootDir - .listSync() - .where((file) => - extension(file.path, 2) - .contains('.jar') && - file is File) - .length - .toString(), - show: instanceConfig.loaderEnum != - ModLoader.vanilla), - infoCard( - I18n.format( - 'edit.instance.homepage.info.play.last'), - instanceConfig.lastPlayLocalString), - infoCard( - I18n.format( - 'edit.instance.homepage.info.play.time'), - Util.formatDuration(Duration( - milliseconds: instanceConfig.playTime))), - ], - ), - ) - ], - ), - ModsView(Instance.fromUUID(instanceUUID)!), - WorldView(worldRootDir: worldRootDir), - OptionPage( - mainWidget: FutureBuilder( - future: screenshotDir.list().toList(), - builder: (context, - AsyncSnapshot> snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.isEmpty) { - return Center( - child: Text( - I18n.format('edit.instance.screenshot.found'), - style: const TextStyle(fontSize: 30), - )); - } - return GridView.builder( - itemCount: snapshot.data!.length, - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 5), - controller: ScrollController(), - itemBuilder: (context, index) { - Widget imageWidget = const Icon(Icons.image); - File imageFile = File(snapshot.data![index].path); - try { - if (imageFile.existsSync()) { - imageWidget = Image.file(imageFile); - } - } on TypeError { - return Container(); - } - return Card( - child: InkWell( - onTap: () {}, - onDoubleTap: () { - Util.openFileManager(imageFile); - chooseIndex = index; - setState(() {}); - }, - child: GridTile( - child: Column( - children: [ - Expanded(child: imageWidget), - Text(imageFile.path - .toString() - .split(Platform.pathSeparator) - .last), - ], - ), - ), - ), - ); - }, - ); - } else if (snapshot.hasError) { - return const Center(child: Text("No snapshot found")); - } else { - return const Center(child: RWLLoading()); - } - }, - ), - actions: [ - IconButton( - icon: const Icon(Icons.folder), - onPressed: () { - Util.openFileManager(screenshotDir); - }, - tooltip: I18n.format('edit.instance.screenshot.folder'), - ), - ], - ), - OptionPage( - mainWidget: FutureBuilder( - future: shaderpackDir - .list() - .where( - (file) => extension(file.path, 2).contains('.zip')) - .toList(), - builder: (context, - AsyncSnapshot> snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.isEmpty) { - return Center( - child: I18nText( - "edit.instance.shaderpack.found.not", - style: const TextStyle(fontSize: 30), - )); - } - return ListView.builder( - itemCount: snapshot.data!.length, - controller: ScrollController(), - itemBuilder: (context, index) { - return ListTile( - title: Text(basename(snapshot.data![index].path) - .replaceAll('.zip', "") - .replaceAll('.disable', "")), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - FileSwitchBox( - file: File(snapshot.data![index].path)), - DeleteFileWidget( - tooltip: I18n.format( - 'edit.instance.shaderpack.delete'), - message: I18n.format( - 'edit.instance.shaderpack.delete.message'), - onDeleted: () { - setState(() {}); - }, - fileSystemEntity: snapshot.data![index]) - ], - ), - ); - }, - ); - } else if (snapshot.hasError) { - return Center(child: Text(snapshot.error.toString())); - } else { - return const Center(child: RWLLoading()); - } - }, - ), - actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - IconButton( - icon: const Icon(Icons.add), - onPressed: () { - showDialog( - context: context, - builder: (context) => - ShaderpackSourceSelection(instanceUUID)); - }, - tooltip: I18n.format('edit.instance.shaderpack.add'), - ), - IconButton( - icon: const Icon(Icons.folder), - onPressed: () { - Util.openFileManager(shaderpackDir); - }, - tooltip: - I18n.format('edit.instance.shaderpack.folder'), - ), - ], - ) - ], - ), - Stack( - children: [ - FutureBuilder( - future: resourcePackDir - .list() - .where((file) => - extension(file.path, 2).contains('.zip')) - .toList(), - builder: (context, - AsyncSnapshot> snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.isEmpty) { - return Center( - child: I18nText( - "edit.instance.resourcepack.found.not", - style: const TextStyle(fontSize: 30), - )); - } - return ListView.builder( - itemCount: snapshot.data!.length, - controller: ScrollController(), - itemBuilder: (context, index) { - File file = File(snapshot.data![index].path); - - Future unzip() async { - final bytes = await file.readAsBytes(); - return ZipDecoder().decodeBytes(bytes); - } - - return FutureBuilder( - future: unzip(), - builder: (context, - AsyncSnapshot snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.files.any((file) => - file - .toString() - .startsWith("pack.mcmeta"))) { - Map? packMeta; - - try { - packMeta = json.decode(utf8.decode( - snapshot.data! - .findFile('pack.mcmeta') - ?.content)); - } on FormatException {} - - ArchiveFile? packImage = - snapshot.data!.findFile('pack.png'); - return DecoratedBox( - decoration: BoxDecoration( - border: Border.all( - color: Colors.white12)), - child: InkWell( - onTap: () { - showDialog( - context: context, - builder: (context) { - if (packMeta != null) { - return AlertDialog( - title: I18nText( - "edit.instance.resourcepack.info.title", - textAlign: TextAlign - .center), - content: Column( - mainAxisAlignment: - MainAxisAlignment - .center, - mainAxisSize: - MainAxisSize.min, - children: [ - I18nText( - "edit.instance.resourcepack.info.description", - args: { - "description": - packMeta['pack'] - [ - 'description'] ?? - "" - }, - ), - I18nText( - "edit.instance.resourcepack.info.format", - args: { - "format": packMeta[ - 'pack'] - [ - 'pack_format'] - .toString() - }, - ), - ], - ), - actions: const [ - OkClose() - ], - ); - } else { - return AlertDialog( - title: I18nText( - "edit.instance.resourcepack.info.title"), - content: I18nText( - "edit.instance.resourcepack.info.none")); - } - }); - }, - child: Column( - children: [ - const SizedBox( - height: 8, - ), - ListTile( - leading: ClipRRect( - borderRadius: - BorderRadius.circular( - 50), - child: packImage == null - ? const Icon( - Icons.image) - : Image.memory( - packImage.content), - ), - title: Text( - basename(file.path) - .replaceAll( - '.zip', "") - .replaceAll( - '.disable', "")), - subtitle: Builder( - builder: (context) { - if (packMeta?['pack'] - ['description'] != - null) { - return Text(packMeta![ - 'pack'] - ['description'] - .toString()); - } else { - return const SizedBox(); - } - }), - trailing: Row( - mainAxisSize: - MainAxisSize.min, - children: [ - FileSwitchBox(file: file), - DeleteFileWidget( - tooltip: I18n.format( - 'edit.instance.resourcepack.info.delete'), - message: I18n.format( - 'edit.instance.resourcepack.info.delete.message'), - onDeleted: () { - setState(() {}); - }, - fileSystemEntity: - file) - ], - ), - ), - const SizedBox( - height: 8, - ), - ], - ), - ), - ); - } else { - return Container(); - } - } else { - return const RWLLoading(); - } - }); - }, - ); - } else if (snapshot.hasError) { - return Center(child: Text(snapshot.error.toString())); - } else { - return const Center(child: RWLLoading()); - } - }, - ), - Positioned( - bottom: 10, - right: 10, - child: IconButton( - icon: const Icon(Icons.folder), - onPressed: () { - Util.openFileManager(resourcePackDir); - }, - tooltip: I18n.format( - 'edit.instance.resourcepack.info.folder'), - ), - ) - ], - ), - InstanceIndependentSetting(instanceConfig: instanceConfig), - ]; - }, - options: () { - return ViewOptions([ - ViewOptionTile( - title: I18n.format("homepage"), - icon: const Icon( - Icons.home_outlined, - ), - description: - I18n.format('edit.instance.homepage.description')), - ViewOptionTile( - title: I18n.format("edit.instance.mods.title"), - icon: const Icon( - Icons.add_box_outlined, - ), - description: I18n.format('edit.instance.mods.description'), - show: instanceConfig.loaderEnum != ModLoader.vanilla), - ViewOptionTile( - title: I18n.format("edit.instance.world.title"), - icon: const Icon( - Icons.public_outlined, - ), - description: I18n.format('edit.instance.world.description'), - show: instanceConfig.sideEnum.isClient, - ), - ViewOptionTile( - title: I18n.format("edit.instance.screenshot.title"), - icon: const Icon( - Icons.screenshot_outlined, - ), - description: - I18n.format('edit.instance.screenshot.description'), - show: instanceConfig.sideEnum.isClient, - ), - ViewOptionTile( - title: I18n.format('edit.instance.shaderpack.title'), - icon: const Icon( - Icons.hd, - ), - description: - I18n.format('edit.instance.shaderpack.description'), - show: instanceConfig.sideEnum.isClient, - ), - ViewOptionTile( - title: I18n.format('edit.instance.resourcepack.title'), - icon: const Icon(LineIcons.penSquare), - description: - I18n.format('edit.instance.resourcepack.description'), - show: instanceConfig.sideEnum.isClient, - ), - ViewOptionTile( - title: I18n.format('edit.instance.settings.title'), - icon: const Icon(Icons.settings), - description: - I18n.format('edit.instance.settings.description')), - ]); - })); - } - - Widget infoCard(String title, String values, {bool show = true}) { - if (show) { - return Stack(children: [ - Card( - margin: const EdgeInsets.all(8), - shape: - RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - color: Colors.deepPurpleAccent, - child: Row( - children: [ - const SizedBox(width: 20), - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 20), - Text(title, - style: const TextStyle( - fontSize: 20, color: Colors.greenAccent), - textAlign: TextAlign.center), - Text(values, - style: const TextStyle(fontSize: 30), - textAlign: TextAlign.center), - const SizedBox(height: 20), - ], - ), - const SizedBox(width: 20), - ], - ), - ), - ]); - } else { - return const SizedBox.shrink(); - } - } -} diff --git a/lib/screen/ftb_modpack.dart b/lib/screen/ftb_modpack.dart deleted file mode 100644 index c053eaacc..000000000 --- a/lib/screen/ftb_modpack.dart +++ /dev/null @@ -1,555 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/mod/FTB/Handler.dart'; -import 'package:rpmlauncher/mod/FTB/ModPackClient.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/route/PushTransitions.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:uuid/uuid.dart'; - -import 'package:rpmlauncher/util/data.dart'; - -class _FTBModPackState extends State { - TextEditingController searchController = TextEditingController(); - ScrollController modPackScrollController = ScrollController(); - - List versionItems = []; - String versionItem = I18n.format('modpack.all_version'); - - @override - void dispose() { - searchController.dispose(); - modPackScrollController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - scrollable: true, - title: Column( - children: [ - I18nText("modpack.ftb.title", textAlign: TextAlign.center), - const SizedBox( - height: 20, - ), - RowScrollView( - child: Row( - children: [ - Text(I18n.format('modpack.search')), - const SizedBox( - width: 12, - ), - SizedBox( - width: 500, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: searchController, - hintText: I18n.format('modpack.search.hint'), - ), - ), - const SizedBox( - width: 12, - ), - ElevatedButton( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(Colors.deepPurpleAccent)), - onPressed: () { - setState(() {}); - }, - child: Text(I18n.format("gui.search")), - ), - const SizedBox( - width: 12, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(I18n.format("game.version")), - FutureBuilder( - future: FTBHandler.getVersions(), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - versionItems = [I18n.format('modpack.all_version')]; - versionItems.addAll(snapshot.data); - - return DropdownButton( - value: versionItem, - onChanged: (String? newValue) { - setState(() { - versionItem = newValue!; - }); - }, - items: versionItems.map>( - (String value) { - return DropdownMenuItem( - value: value, - child: Text( - value, - textAlign: TextAlign.center, - ), - ); - }).toList(), - ); - } else { - return const Center(child: RWLLoading()); - } - }) - ], - ), - ], - ), - ) - ], - ), - content: SizedBox( - height: MediaQuery.of(context).size.height / 2, - width: MediaQuery.of(context).size.width / 2, - child: FutureBuilder( - future: FTBHandler.getModPackList(), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.isEmpty) { - return Text(I18n.format('modpack.found'), - style: const TextStyle(fontSize: 30), - textAlign: TextAlign.center); - } - - return ListView.builder( - controller: modPackScrollController, - shrinkWrap: true, - itemCount: snapshot.data!.length, - itemBuilder: (BuildContext context, int index) { - return FutureBuilder( - future: RPMHttpClient().get( - "$ftbModPackAPI/modpack/${snapshot.data![index]}"), - builder: - (context, AsyncSnapshot modpackSnapshot) { - if (modpackSnapshot.hasData) { - Map data = - RPMHttpClient.json(modpackSnapshot.data!.data); - - if (data['status'] == 'error') { - return Container(); - } - - bool versionCkeck = versionItem == - I18n.format('modpack.all_version') - ? true - : (data['tags'] == null - ? false - : (data['tags'].any((tag) => tag['name'] - .toString() - .contains(versionItem)))); - - bool nameSearchCheck = - searchController.text.isNotEmpty - ? data['name'] - .toString() - .toLowerCase() - .contains( - searchController.text.toLowerCase()) - : true; - - String name = data["name"]; - String modDescription = data["synopsis"]; - String url = FTBHandler.getWebUrlFromName(name); - int modpackID = data["id"]; - - if (versionCkeck && nameSearchCheck) { - return ListTile( - leading: Image.network( - data["art"][0]["url"], - width: 50, - height: 50, - fit: BoxFit.contain, - loadingBuilder: - (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return CircularProgressIndicator( - value: - loadingProgress.expectedTotalBytes != - null - ? loadingProgress - .cumulativeBytesLoaded - .toInt() / - loadingProgress - .expectedTotalBytes! - .toInt() - : null, - ); - }, - ), - title: Text(name), - subtitle: Text(modDescription), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - onPressed: () => Util.openUri(url), - tooltip: I18n.format( - 'edit.instance.mods.page.open'), - icon: - const Icon(Icons.open_in_browser)), - const SizedBox(width: 12), - ElevatedButton( - child: Text(I18n.format("gui.install")), - onPressed: () { - List versions = data['versions']; - versions.sort((a, b) => a['updated']); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(I18n.format( - "edit.instance.mods.download.select.version")), - content: SizedBox( - height: MediaQuery.of(context) - .size - .height / - 3, - width: MediaQuery.of(context) - .size - .width / - 3, - child: ListView.builder( - itemCount: - versions.length, - itemBuilder: (BuildContext - context, - int versionsIndex) { - return FutureBuilder( - future: FTBHandler - .getVersionInfo( - modpackID, - versions[ - versionsIndex] - ["id"]), - builder: (context, - AsyncSnapshot - snapshot) { - if (snapshot - .hasData) { - Map versionInfo = - snapshot - .data; - return ListTile( - title: Text( - versionInfo[ - "name"]), - subtitle: FTBHandler - .parseReleaseType( - versionInfo[ - "type"]), - onTap: () { - showDialog( - barrierDismissible: - false, - context: - context, - builder: - (context) => - AddFTBModpack( - versionInfo: - versionInfo, - packData: - data, - ), - ); - }, - ); - } else { - return Row( - mainAxisAlignment: - MainAxisAlignment - .center, - children: const [ - RWLLoading() - ], - ); - } - }); - })), - actions: [ - IconButton( - icon: const Icon( - Icons.close_sharp), - tooltip: - I18n.format("gui.close"), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ); - }, - ), - ], - ), - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: I18nText( - 'modpack.name', - args: {"name": name}, - ), - content: I18nText( - 'modpack.description', - args: {"description": modDescription}, - ), - ); - }, - ); - }, - ); - } else { - return Container(); - } - } else { - return const ListTile( - title: Center(child: RWLLoading())); - } - }); - }, - ); - } else { - return const Center(child: RWLLoading()); - } - }), - ), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - tooltip: I18n.format("gui.close"), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - } -} - -class FTBModPack extends StatefulWidget { - @override - State createState() => _FTBModPackState(); -} - -class AddFTBModpack extends StatefulWidget { - final Map versionInfo; - final Map packData; - - const AddFTBModpack({required this.versionInfo, required this.packData}); - - @override - State createState() => _AddFTBModpackState(); -} - -class _AddFTBModpackState extends State { - TextEditingController nameController = TextEditingController(); - - @override - void initState() { - nameController.text = widget.packData["name"]; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - scrollable: true, - title: I18nText("modpack.add.title", textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Text(I18n.format("edit.instance.homepage.instance.name"), - style: - const TextStyle(fontSize: 18, color: Colors.amberAccent)), - Expanded( - child: RPMLTextField( - controller: nameController, - textAlign: TextAlign.center, - onChanged: (value) { - setState(() {}); - }, - ), - ) - ], - ), - const SizedBox( - height: 12, - ), - I18nText( - 'modpack.name', - args: {"name": widget.packData["name"]}, - ), - I18nText( - 'modpack.version', - args: {"version": widget.versionInfo["name"]}, - ), - I18nText( - 'modpack.version.game', - args: {"game_version": widget.versionInfo["targets"][1]["version"]}, - ), - ], - ), - actions: [ - TextButton( - child: Text(I18n.format("gui.cancel")), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text(I18n.format("gui.confirm")), - onPressed: () { - navigator.pop(); - navigator.push( - PushTransitions(builder: (context) => const HomePage())); - - String versionID = widget.versionInfo["targets"][1]["version"]; - - showDialog( - context: context, - barrierDismissible: false, - builder: (context) { - return FutureBuilder( - future: Util.getVanillaVersionMeta(versionID), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return Task( - meta: snapshot.data!, - versionInfo: widget.versionInfo, - packData: widget.packData, - instanceName: nameController.text, - versionID: versionID); - } else { - return const Center(child: RWLLoading()); - } - }); - }); - }) - ], - ); - } -} - -class Task extends StatefulWidget { - final MinecraftMeta meta; - final Map versionInfo; - final Map packData; - final String instanceName; - final String versionID; - - const Task( - {Key? key, - required this.meta, - required this.versionInfo, - required this.packData, - required this.instanceName, - required this.versionID}) - : super(key: key); - - @override - State createState() => _TaskState(); -} - -class _TaskState extends State { - @override - void initState() { - installingState.finish = false; - installingState.nowEvent = I18n.format('version.list.downloading.ready'); - - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - String uuid = const Uuid().v4(); - - String loaderID = widget.versionInfo["targets"][0]["name"]; - bool isFabric = loaderID.startsWith(ModLoader.fabric.name); - String loaderVersionID = widget.versionInfo["targets"][0]["version"]; - - InstanceConfig config = InstanceConfig( - uuid: uuid, - name: widget.instanceName, - side: MinecraftSide.client, - version: widget.versionID, - loader: (isFabric ? ModLoader.fabric : ModLoader.forge).name, - javaVersion: widget.meta.javaVersion, - loaderVersion: loaderVersionID, - assetsID: widget.meta["assets"]); - - config.createConfigFile(); - - await RPMHttpClient().download( - widget.packData['art'][0]['url'], - join(GameRepository.getInstanceRootDir().absolute.path, - widget.instanceName, "icon.png")); - - Util.javaCheckDialog( - hasJava: () => FTBModPackClient.createClient( - instanceUUID: uuid, - meta: widget.meta, - versionInfo: widget.versionInfo, - packData: widget.packData, - setState: setState), - allJavaVersions: config.needJavaVersion); - }); - } - - @override - Widget build(BuildContext context) { - if (installingState.finish && - installingState.downloadInfos.progress == 1.0) { - return AlertDialog( - title: Text(I18n.format("gui.download.done")), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text(I18n.format("gui.close"))) - ], - ); - } else { - return WillPopScope( - onWillPop: () => Future.value(false), - child: AlertDialog( - title: Text(installingState.nowEvent, textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - LinearProgressIndicator( - value: installingState.downloadInfos.progress, - ), - Text( - "${(installingState.downloadInfos.progress * 100).toStringAsFixed(2)}%") - ], - ), - ), - ); - } - } -} diff --git a/lib/screen/home_page.dart b/lib/screen/home_page.dart deleted file mode 100644 index ae9b499b4..000000000 --- a/lib/screen/home_page.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:line_icons/line_icons.dart'; -import 'package:rpmlauncher/model/Game/MinecraftNews.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/route/PushTransitions.dart'; -import 'package:rpmlauncher/screen/about.dart'; -import 'package:rpmlauncher/screen/settings.dart'; -import 'package:rpmlauncher/screen/version_selection.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/updater.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/view/instance_view.dart'; -import 'package:rpmlauncher/view/MinecraftNewsView.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; -import 'package:rpmlauncher/widget/AccountManageAction.dart'; -import 'package:rpmlauncher/widget/dialog/quick_setup.dart'; -import 'package:rpmlauncher/widget/dialog/UpdaterDialog.dart'; -import 'package:rpmlauncher/widget/keep_alive_wrapper.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/NewFeaturesWidget.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; - -class HomePage extends StatefulWidget { - static const String route = '/'; - final int initialPage; - - const HomePage({Key? key, this.initialPage = 0}) : super(key: key); - - @override - State createState() => _HomePageState(); -} - -class _HomePageState extends State { - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - if (!launcherConfig.isInit && mounted) { - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => const QuickSetup()); - } else { - Updater.checkForUpdate(Updater.fromConfig()).then((info) { - if (info.needUpdate && mounted) { - showDialog( - context: context, - builder: (context) => UpdaterDialog(info: info)); - } - }); - } - }); - } - - @override - Widget build(BuildContext context) { - return DefaultTabController( - initialIndex: widget.initialPage, - length: 3, - child: Scaffold( - appBar: AppBar( - centerTitle: true, - leadingWidth: 250, - leading: RowScrollView( - center: false, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - IconButton( - tooltip: I18n.format('homepage.website'), - onPressed: () { - Util.openUri(LauncherInfo.homePageUrl); - }, - icon: Image.asset('assets/images/Logo.png', scale: 4), - ), - IconButton( - tooltip: I18n.format('gui.settings'), - icon: const Icon(Icons.settings), - onPressed: () { - navigator.pushNamed(SettingScreen.route); - }, - ), - IconButton( - tooltip: I18n.format('homepage.data.folder.open'), - icon: const Icon(Icons.folder), - onPressed: () { - Util.openFileManager(dataHome); - }, - ), - IconButton( - tooltip: I18n.format('homepage.about'), - icon: const Icon(Icons.info), - onPressed: () { - Navigator.push( - context, - PushTransitions(builder: (context) => AboutScreen()), - ); - }, - ), - IconButton( - icon: const Icon(Icons.change_circle), - tooltip: I18n.format('homepage.update'), - onPressed: () { - showDialog( - context: context, - builder: (context) => FutureBuilder( - future: - Updater.checkForUpdate(Updater.fromConfig()), - builder: (context, snapshot) { - if (snapshot.hasData) { - VersionInfo info = snapshot.data!; - if (info.needUpdate) { - return UpdaterDialog(info: snapshot.data!); - } else { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText('updater.check.none'), - const Icon(Icons.done_outlined, - size: 30), - ], - ), - actions: const [OkClose()], - ); - } - } else { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText('updater.check.checking'), - const SizedBox( - width: 30.0, - height: 30.0, - child: FittedBox(child: RWLLoading()), - ), - ], - ), - ); - } - })); - }, - ), - ], - ), - ), - title: Text( - LauncherInfo.getUpperCaseName(), - ), - bottom: TabBar(tabs: [ - Tab( - icon: const Icon(Icons.sports_esports), - text: I18n.format('homepage.tabs.instance')), - Tab( - icon: const NewFeaturesWidget(child: Icon(LineIcons.server)), - text: I18n.format('homepage.tabs.server')), - Tab( - icon: const Icon(Icons.notifications), - text: I18n.format('homepage.tabs.news')), - ]), - actions: const [ - AccountManageButton(), - ], - ), - body: TabBarView( - children: [ - const KeepAliveWrapper( - child: InstanceView(side: MinecraftSide.client)), - const KeepAliveWrapper( - child: InstanceView(side: MinecraftSide.server)), - KeepAliveWrapper( - child: FutureBuilder( - future: MinecraftNews.fromWeb(), - builder: (context, snapshot) { - if (snapshot.hasData) { - final news = snapshot.data!; - - return MinecraftNewsView(news: news); - } else { - return const RWLLoading(); - } - }, - ), - ), - ], - ), - floatingActionButton: const _FloatingAction(), - ), - ); - } -} - -class _FloatingAction extends StatefulWidget { - const _FloatingAction({ - Key? key, - }) : super(key: key); - - @override - State<_FloatingAction> createState() => _FloatingActionState(); -} - -class _FloatingActionState extends State<_FloatingAction> { - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - DefaultTabController.of(context).addListener(() { - setState(() {}); - }); - }); - } - - @override - Widget build(BuildContext context) { - int index = DefaultTabController.of(context).index; - if (index == 0) { - return FloatingActionButton( - heroTag: null, - onPressed: () { - Navigator.push( - context, - PushTransitions( - builder: (context) => const VersionSelection( - side: MinecraftSide.client, - ))); - }, - tooltip: I18n.format('version.list.instance.add'), - child: const Icon(Icons.add), - ); - } else if (index == 1) { - return FloatingActionButton( - heroTag: null, - onPressed: () { - Navigator.push( - context, - PushTransitions( - builder: (context) => const VersionSelection( - side: MinecraftSide.server, - ))); - }, - tooltip: I18n.format('version.list.instance.add.server'), - child: const Icon(Icons.add), - ); - } else { - return const SizedBox.shrink(); - } - } -} diff --git a/lib/screen/install_curseforge_modpack.dart b/lib/screen/install_curseforge_modpack.dart deleted file mode 100644 index a6b2ea6e0..000000000 --- a/lib/screen/install_curseforge_modpack.dart +++ /dev/null @@ -1,242 +0,0 @@ -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/mod/curseforge/curseforge_modapck_client.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/route/PushTransitions.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:archive/archive.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:uuid/uuid.dart'; - -import 'package:rpmlauncher/util/data.dart'; - -class InstallCurseForgeModpack extends StatefulWidget { - final Map manifest; - final Archive archive; - final String? iconUrl; - - const InstallCurseForgeModpack( - {required this.manifest, required this.archive, this.iconUrl}); - - @override - State createState() => - _InstallCurseForgeModpackState(); -} - -class _InstallCurseForgeModpackState extends State { - late TextEditingController nameController; - - @override - void initState() { - nameController = TextEditingController(); - - super.initState(); - - nameController.text = widget.manifest['name']; - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - scrollable: true, - title: I18nText('modpack.add.title', textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Text(I18n.format('edit.instance.homepage.instance.name'), - style: - const TextStyle(fontSize: 18, color: Colors.amberAccent)), - Expanded( - child: RPMLTextField( - controller: nameController, - textAlign: TextAlign.center, - ), - ) - ], - ), - const SizedBox( - height: 12, - ), - I18nText( - 'modpack.name', - args: {'name': widget.manifest['name']}, - ), - I18nText( - 'modpack.version', - args: { - 'version': - widget.manifest['version'] ?? I18n.format('gui.unknown') - }, - ), - I18nText( - 'modpack.version.game', - args: {'game_version': widget.manifest['minecraft']['version']}, - ), - I18nText( - 'modpack.author', - args: { - 'author': widget.manifest['author'] ?? I18n.format('gui.unknown') - }, - ) - ], - ), - actions: [ - TextButton( - child: Text(I18n.format('gui.cancel')), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text(I18n.format('gui.confirm')), - onPressed: () async { - navigator.push( - PushTransitions(builder: (context) => const HomePage())); - - String versionID = widget.manifest['minecraft']['version']; - - showDialog( - context: context, - builder: (BuildContext context) { - return FutureBuilder( - future: Util.getVanillaVersionMeta(versionID), - builder: (context, snapshot) { - if (snapshot.hasData) { - return _InstallTask( - meta: snapshot.data!, - versionID: versionID, - instanceName: nameController.text, - manifest: widget.manifest, - archive: widget.archive, - iconUrl: widget.iconUrl, - ); - } else if (snapshot.hasError) { - return Text(snapshot.error.toString()); - } else { - return const Center(child: RWLLoading()); - } - }); - }); - }) - ], - ); - } -} - -class _InstallTask extends StatefulWidget { - final MinecraftMeta meta; - final String versionID; - final String instanceName; - final Map manifest; - final Archive archive; - final String? iconUrl; - - const _InstallTask({ - required this.meta, - required this.versionID, - required this.manifest, - required this.instanceName, - required this.archive, - required this.iconUrl, - }); - - @override - State<_InstallTask> createState() => _InstallTaskState(); -} - -class _InstallTaskState extends State<_InstallTask> { - @override - void initState() { - super.initState(); - installingState.finish = false; - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - final String loaderID = - widget.manifest['minecraft']['modLoaders'][0]['id']; - - final ModLoader loader; - if (loaderID.startsWith(ModLoader.fabric.name)) { - loader = ModLoader.fabric; - } else { - loader = ModLoader.forge; - } - - final String loaderVersion = loaderID.split('${loader.name}-').join(''); - final String uuid = const Uuid().v4(); - - final InstanceConfig config = InstanceConfig( - uuid: uuid, - name: widget.instanceName, - side: MinecraftSide.client, - version: widget.versionID, - loader: loader.name, - javaVersion: widget.meta.javaVersion, - loaderVersion: loaderVersion, - assetsID: widget.meta['assets']); - - config.createConfigFile(); - - if (widget.iconUrl != null) { - // Download the icon file of the modpack - String path = - join(GameRepository.getInstanceRootDir().path, uuid, 'icon.png'); - await RPMHttpClient().download(widget.iconUrl!, path); - } - - Util.javaCheckDialog( - hasJava: () => CurseForgeModpackClient.createClient( - setState: setState, - meta: widget.meta, - instance: Instance.fromUUID(uuid)!, - manifest: widget.manifest, - archive: widget.archive), - allJavaVersions: config.needJavaVersion); - }); - } - - @override - Widget build(BuildContext context) { - if (installingState.finish && - installingState.downloadInfos.progress == 1.0) { - return AlertDialog( - contentPadding: const EdgeInsets.all(16.0), - title: Text(I18n.format('gui.download.done')), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text(I18n.format('gui.close'))) - ], - ); - } else { - return WillPopScope( - onWillPop: () => Future.value(false), - child: AlertDialog( - title: Text(installingState.nowEvent, textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - LinearProgressIndicator( - value: installingState.downloadInfos.progress, - ), - Text( - '${(installingState.downloadInfos.progress * 100).toStringAsFixed(2)}%') - ], - ), - ), - ); - } - } -} diff --git a/lib/screen/instance_independent_setting.dart b/lib/screen/instance_independent_setting.dart deleted file mode 100644 index 45a292a20..000000000 --- a/lib/screen/instance_independent_setting.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/screen/settings.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; -import 'package:rpmlauncher/widget/dialog/CheckDialog.dart'; -import 'package:rpmlauncher/widget/memory_slider.dart'; -import 'package:rpmlauncher/widget/settings/jvm_args_settings.dart'; - -class InstanceIndependentSetting extends StatefulWidget { - final InstanceConfig instanceConfig; - - const InstanceIndependentSetting({Key? key, required this.instanceConfig}) - : super(key: key); - - @override - State createState() => - _InstanceIndependentSettingState(); -} - -class _InstanceIndependentSettingState - extends State { - late TextEditingController jvmArgsController; - - late double javaMaxRam; - late int javaVersion; - late String? javaPath; - - @override - void initState() { - jvmArgsController = TextEditingController(); - javaVersion = widget.instanceConfig.javaVersion; - javaMaxRam = widget.instanceConfig.javaMaxRam ?? launcherConfig.jvmMaxRam; - javaPath = widget.instanceConfig.storage["java_path_$javaVersion"]; - - super.initState(); - } - - @override - void dispose() { - jvmArgsController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final titleStyle = Theme.of(context).textTheme.titleLarge; - - return ListTile( - title: Column(children: [ - const SizedBox( - height: 20, - ), - RowScrollView( - child: Row(mainAxisSize: MainAxisSize.min, children: [ - ElevatedButton( - child: I18nText( - "edit.instance.settings.global", - style: const TextStyle(fontSize: 20), - ), - onPressed: () { - navigator.pushNamed(SettingScreen.route); - }, - ), - const SizedBox( - width: 20, - ), - ElevatedButton( - child: I18nText( - "edit.instance.settings.reset", - style: const TextStyle(fontSize: 18), - ), - onPressed: () { - showDialog( - context: context, - builder: (context) { - return CheckDialog( - title: I18n.format('edit.instance.settings.reset'), - message: - I18n.format('edit.instance.settings.reset.message'), - onPressedOK: (context) { - widget.instanceConfig.storage - .removeItem("java_path_$javaVersion"); - widget.instanceConfig.javaMaxRam = null; - widget.instanceConfig.javaJvmArgs = null; - javaMaxRam = launcherConfig.jvmMaxRam; - jvmArgsController.text = ""; - setState(() {}); - Navigator.pop(context); - }, - ); - }); - }, - ), - ]), - ), - const SizedBox( - height: 20, - ), - I18nText( - "edit.instance.settings.title", - style: const TextStyle(color: Colors.red, fontSize: 30), - ), - const SizedBox( - height: 25, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - width: 18, - ), - Column( - children: [ - I18nText( - "settings.java.path", - style: const TextStyle( - fontSize: 20.0, - color: Colors.lightBlue, - ), - textAlign: TextAlign.center, - ), - Text(javaPath ?? I18n.format("gui.default")), - ], - ), - const SizedBox( - width: 12, - ), - ElevatedButton( - onPressed: () { - Util.openJavaSelectScreen(context).then((value) { - if (value[0]) { - widget.instanceConfig.storage - .setItem("java_path_$javaVersion", value[1]); - javaPath = value[1]; - setState(() {}); - } - }); - }, - child: Text( - I18n.format("settings.java.path.select"), - style: const TextStyle(fontSize: 18), - )), - ]), - MemorySlider( - value: javaMaxRam, - onChanged: (memory) { - widget.instanceConfig.javaMaxRam = memory; - }), - I18nText( - 'settings.java.jvm.args', - style: titleStyle, - textAlign: TextAlign.center, - ), - JVMArgsSettings( - value: widget.instanceConfig.javaJvmArgs ?? [], - onChanged: (value) { - widget.instanceConfig.javaJvmArgs = value; - }) - ])); - } -} diff --git a/lib/screen/settings.dart b/lib/screen/settings.dart deleted file mode 100644 index 18cfd0b36..000000000 --- a/lib/screen/settings.dart +++ /dev/null @@ -1,615 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/i18n/language_selector.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/util/launcher_path.dart'; -import 'package:rpmlauncher/util/theme.dart'; -import 'package:rpmlauncher/util/updater.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/memory_slider.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmlauncher/widget/settings/java_path.dart'; -import 'package:rpmlauncher/widget/settings/jvm_args_settings.dart'; -import 'package:rpmlauncher/widget/settings/theme_selector.dart'; - -class _SettingScreenState extends State { - Color get primaryColor => ThemeUtil.getTheme().colorScheme.primary; - - TextEditingController gameWindowWidthController = TextEditingController(); - TextEditingController gameWindowHeightController = TextEditingController(); - TextEditingController wrapperCommandController = TextEditingController(); - TextEditingController gameLogMaxLineCountController = TextEditingController(); - - late bool checkAssetsIntegrity; - late bool showGameLogs; - late bool autoDownloadModDependencies; - late bool autoFullScreen; - late bool checkAccountValidity; - late bool autoCloseGameLogsScreen; - late bool discordRichPresence; - - String? backgroundPath; - - VersionTypes updateChannel = launcherConfig.updateChannel; - - int _selectedIndex = 0; - - @override - void initState() { - checkAccountValidity = launcherConfig.checkAccountValidity; - autoCloseGameLogsScreen = launcherConfig.autoCloseGameLogsScreen; - checkAssetsIntegrity = launcherConfig.checkAssetsIntegrity; - showGameLogs = launcherConfig.showGameLogs; - autoDownloadModDependencies = launcherConfig.autoDownloadModDependencies; - autoFullScreen = launcherConfig.autoFullScreen; - discordRichPresence = launcherConfig.discordRichPresence; - - gameWindowWidthController.text = launcherConfig.gameWindowWidth.toString(); - gameWindowHeightController.text = - launcherConfig.gameWindowHeight.toString(); - gameLogMaxLineCountController.text = - launcherConfig.gameLogMaxLineCount.toString(); - wrapperCommandController.text = launcherConfig.wrapperCommand ?? ''; - super.initState(); - } - - @override - void dispose() { - gameWindowWidthController.dispose(); - gameWindowHeightController.dispose(); - wrapperCommandController.dispose(); - gameLogMaxLineCountController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Dialog( - clipBehavior: Clip.antiAlias, - insetPadding: - const EdgeInsets.symmetric(horizontal: 100.0, vertical: 60.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - NavigationRail( - selectedIndex: _selectedIndex, - onDestinationSelected: (int index) { - setState(() { - _selectedIndex = index; - }); - }, - labelType: NavigationRailLabelType.all, - leading: Padding( - padding: const EdgeInsets.fromLTRB(0, 10, 0, 20), - child: FloatingActionButton( - tooltip: I18n.format('gui.close'), - elevation: 0, - onPressed: () { - navigator.pop(); - }, - child: const Icon(Icons.close), - ), - ), - destinations: [ - NavigationRailDestination( - icon: const Icon(Icons.code_rounded), - selectedIcon: const Icon(Icons.code), - label: I18nText('settings.java.title'), - ), - NavigationRailDestination( - icon: const Icon(Icons.web_asset_rounded), - selectedIcon: const Icon(Icons.web_asset), - label: I18nText('settings.appearance.title'), - ), - NavigationRailDestination( - icon: const Icon(Icons.settings_outlined), - selectedIcon: const Icon(Icons.settings), - label: I18nText('settings.advanced.title'), - ), - NavigationRailDestination( - icon: const Icon(Icons.bug_report_outlined), - selectedIcon: const Icon(Icons.bug_report), - label: I18nText('settings.debug.title'), - ), - ], - ), - const VerticalDivider(thickness: 1, width: 1), - Flexible( - child: Align( - alignment: Alignment.topCenter, - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - AppBar( - title: I18nText('settings.title'), - centerTitle: true, - leading: const SizedBox(), - ), - const SizedBox(height: 8), - if (_selectedIndex == 0) const _JavaSettings(), - if (_selectedIndex == 1) const _AppearanceSettings(), - if (_selectedIndex == 2) const _AdvancedSettings(), - if (_selectedIndex == 3) const _DebugOption(), - const SizedBox(height: 18) - ], - ), - ), - ), - ) - ], - ), - ), - ); - } -} - -class SettingScreen extends StatefulWidget { - static const String route = '/settings'; - - @override - State createState() => _SettingScreenState(); -} - -class _JavaSettings extends StatefulWidget { - const _JavaSettings(); - - @override - State<_JavaSettings> createState() => _JavaSettingsState(); -} - -class _JavaSettingsState extends State<_JavaSettings> { - @override - Widget build(BuildContext context) { - final titleStyle = Theme.of(context).textTheme.titleLarge; - - return SafeArea( - child: Column(children: [ - I18nText( - 'settings.java.path', - style: titleStyle, - textAlign: TextAlign.center, - ), - const JavaPathSettings(), - const Divider(), - SwitchListTile( - value: launcherConfig.autoInstallJava, - onChanged: (value) { - setState(() { - launcherConfig.autoInstallJava = value; - }); - }, - title: Text( - I18n.format('settings.java.auto'), - style: titleStyle, - textAlign: TextAlign.center, - ), - ), - const Divider(), - MemorySlider( - value: launcherConfig.jvmMaxRam, - onChanged: (memory) { - launcherConfig.jvmMaxRam = memory; - }), - const Divider(), - JVMArgsSettings( - value: launcherConfig.jvmArgs, - onChanged: (value) { - launcherConfig.jvmArgs = value; - }) - ])); - } -} - -class _AppearanceSettings extends StatefulWidget { - const _AppearanceSettings(); - - @override - State<_AppearanceSettings> createState() => _AppearanceSettingsState(); -} - -class _AppearanceSettingsState extends State<_AppearanceSettings> { - late TextEditingController gameWindowWidthController; - late TextEditingController gameWindowHeightController; - - @override - void initState() { - gameWindowWidthController = - TextEditingController(text: launcherConfig.gameWindowWidth.toString()); - gameWindowHeightController = - TextEditingController(text: launcherConfig.gameWindowHeight.toString()); - super.initState(); - } - - @override - void dispose() { - gameWindowWidthController.dispose(); - gameWindowHeightController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final titleStyle = Theme.of(context).textTheme.titleLarge; - - return Column( - children: [ - LanguageSelectorWidget( - onChanged: () => setState(() {}), - ), - const Divider(), - Text( - I18n.format('settings.appearance.theme'), - style: titleStyle, - ), - const SizedBox(height: 12), - const ThemeSelector(), - const Divider(), - Text( - I18n.format('settings.appearance.background.title'), - style: titleStyle, - ), - Text( - launcherConfig.backgroundImageFile?.path ?? - I18n.format('gui.default'), - style: const TextStyle(fontSize: 18), - textAlign: TextAlign.center), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - OutlinedButton( - onPressed: () async { - final result = - await FilePicker.platform.pickFiles(type: FileType.image); - if (result != null) { - PlatformFile file = result.files.single; - launcherConfig.backgroundImageFile = File(file.path!); - } - setState(() {}); - }, - child: - Text(I18n.format('settings.appearance.background.pick'))), - const SizedBox(width: 10), - OutlinedButton( - onPressed: () { - launcherConfig.backgroundImageFile = null; - setState(() {}); - }, - child: - Text(I18n.format('settings.appearance.background.reset'))), - ], - ), - const Divider(), - Text( - I18n.format('settings.appearance.window.size.title'), - style: titleStyle, - ), - const SizedBox( - height: 12, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 280, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: gameWindowWidthController, - hintText: '854', - verify: (value) => int.tryParse(value) != null, - onChanged: (value) async { - launcherConfig.gameWindowWidth = int.tryParse(value) ?? 854; - }, - ), - ), - const SizedBox(width: 12), - const Icon(Icons.clear), - const SizedBox(width: 12), - SizedBox( - width: 280, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: gameWindowHeightController, - hintText: '480', - verify: (value) => int.tryParse(value) != null, - onChanged: (value) async { - launcherConfig.gameWindowHeight = int.tryParse(value) ?? 480; - }, - ), - ), - ], - ) - ], - ); - } -} - -class _AdvancedSettings extends StatefulWidget { - const _AdvancedSettings(); - - @override - State<_AdvancedSettings> createState() => _AdvancedSettingsState(); -} - -class _AdvancedSettingsState extends State<_AdvancedSettings> { - late TextEditingController gameLogMaxLineCountController; - late TextEditingController wrapperCommandController; - - @override - void initState() { - gameLogMaxLineCountController = TextEditingController( - text: launcherConfig.gameLogMaxLineCount.toString()); - wrapperCommandController = - TextEditingController(text: launcherConfig.wrapperCommand); - - super.initState(); - } - - @override - void dispose() { - gameLogMaxLineCountController.dispose(); - wrapperCommandController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final titleStyle = Theme.of(context).textTheme.titleLarge; - - return Column( - children: [ - I18nText('settings.advanced.tips', - style: Theme.of(context) - .textTheme - .headlineSmall - ?.copyWith(color: Colors.red), - textAlign: TextAlign.center), - const Divider(), - ListTile( - title: I18nText( - 'settings.advanced.datahome', - style: titleStyle, - textAlign: TextAlign.center, - ), - subtitle: SelectableText(dataHome.absolute.path, - textAlign: TextAlign.center, - style: TextStyle( - color: Theme.of(context).hintColor, - )), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - OutlinedButton.icon( - onPressed: () async { - final path = await FilePicker.platform.getDirectoryPath(); - - if (path != null) { - launcherConfig.launcherDataDir = Directory(path); - setState(() {}); - - if (context.mounted) { - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => - const _ChangeDataHomeSuccessful()); - } - } - }, - icon: const Icon(Icons.folder), - label: I18nText('settings.advanced.datahome.change')), - const SizedBox(width: 12), - OutlinedButton.icon( - onPressed: () { - launcherConfig.launcherDataDir = - LauncherPath.defaultDataHome; - setState(() {}); - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => - const _ChangeDataHomeSuccessful()); - }, - icon: const Icon(Icons.restore), - label: I18nText('settings.advanced.datahome.restore')) - ], - ), - ), - const Divider(), - SwitchListTile( - value: launcherConfig.checkAssetsIntegrity, - onChanged: (value) { - setState(() { - launcherConfig.checkAssetsIntegrity = value; - }); - }, - title: I18nText('settings.advanced.assets.check', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.showGameLogs, - onChanged: (value) { - setState(() { - launcherConfig.showGameLogs = value; - }); - }, - title: I18nText('settings.advanced.show_log', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.autoDownloadModDependencies, - onChanged: (value) { - setState(() { - launcherConfig.autoDownloadModDependencies = value; - }); - }, - title: I18nText('settings.advanced.auto_dependencies', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.autoFullScreen, - onChanged: (value) { - setState(() { - launcherConfig.autoFullScreen = value; - }); - }, - title: I18nText('settings.advanced.auto_full_screen', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.checkAccountValidity, - onChanged: (value) { - setState(() { - launcherConfig.checkAccountValidity = value; - }); - }, - title: I18nText('settings.advanced.validate_account', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.autoCloseGameLogsScreen, - onChanged: (value) { - setState(() { - launcherConfig.autoCloseGameLogsScreen = value; - }); - }, - title: I18nText('settings.advanced.auto_close_log_screen', - style: titleStyle, textAlign: TextAlign.center), - ), - SwitchListTile( - value: launcherConfig.discordRichPresence, - onChanged: (value) { - setState(() { - launcherConfig.discordRichPresence = value; - }); - }, - title: I18nText('settings.advanced.discord_rpc', - style: titleStyle, textAlign: TextAlign.center), - ), - const Divider(), - I18nText('settings.advanced.update_channel', - style: titleStyle, textAlign: TextAlign.center), - SegmentedButton( - segments: [ - ButtonSegment( - value: VersionTypes.stable, - icon: const Icon(Icons.check_circle), - label: Text(Updater.toI18nString(VersionTypes.stable)), - ), - ButtonSegment( - value: VersionTypes.dev, - icon: const Icon(Icons.bug_report), - label: Text(Updater.toI18nString(VersionTypes.dev)), - ), - ], - selected: {launcherConfig.updateChannel}, - onSelectionChanged: (newSelection) { - setState(() { - launcherConfig.updateChannel = newSelection.first; - }); - }, - ), - const Divider(), - ListTile( - title: I18nText('settings.advanced.max.log', - style: titleStyle, textAlign: TextAlign.center), - trailing: SizedBox( - width: 300, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: gameLogMaxLineCountController, - verify: (value) => int.tryParse(value) != null, - hintText: '300', - onChanged: (value) async { - launcherConfig.gameLogMaxLineCount = int.parse(value); - }, - ), - ), - ), - const SizedBox(height: 12), - ListTile( - title: I18nText('settings.advanced.wrapper_command', - style: titleStyle, textAlign: TextAlign.center), - trailing: SizedBox( - width: 300, - child: RPMLTextField( - textAlign: TextAlign.center, - controller: wrapperCommandController, - hintText: 'Executable program', - onChanged: (value) { - launcherConfig.wrapperCommand = value.isEmpty ? null : value; - }, - ), - ), - ), - ], - ); - } -} - -class _ChangeDataHomeSuccessful extends StatelessWidget { - const _ChangeDataHomeSuccessful({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: I18nText('settings.advanced.datahome.change.successful'), - actions: [ - OkClose( - onOk: () { - Util.exit(0); - }, - ) - ], - ); - } -} - -class _DebugOption extends StatefulWidget { - const _DebugOption(); - - @override - State<_DebugOption> createState() => _DebugOptionState(); -} - -class _DebugOptionState extends State<_DebugOption> { - @override - Widget build(BuildContext context) { - return Column( - children: [ - I18nText('settings.advanced.tips', - style: Theme.of(context) - .textTheme - .headlineSmall - ?.copyWith(color: Colors.red), - textAlign: TextAlign.center), - const Divider(), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FloatingActionButton.extended( - onPressed: () async { - LauncherPath.currentConfigHome.deleteSync(recursive: true); - if (dataHome.existsSync()) { - dataHome.deleteSync(recursive: true); - } - - await Util.exit(0); - }, - label: I18nText('settings.debug.delete_all_data', - style: Theme.of(context).textTheme.titleLarge)), - ], - ), - ], - ); - } -} diff --git a/lib/screen/version_selection.dart b/lib/screen/version_selection.dart deleted file mode 100644 index fb84e44e1..000000000 --- a/lib/screen/version_selection.dart +++ /dev/null @@ -1,394 +0,0 @@ -import 'dart:io'; -import 'package:file_picker/file_picker.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/mod/curseforge/curseforge_handler.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/Game/MinecraftVersion.dart'; -import 'package:rpmlauncher/pages/curseforge_modpack_page.dart'; -import 'package:rpmlauncher/screen/ftb_modpack.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/screen/RecommendedModpackScreen.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/util/theme.dart'; -import 'package:rpmlauncher/widget/dialog/UnSupportedForgeVersion.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; -import 'package:split_view/split_view.dart'; - -import 'package:rpmlauncher/util/data.dart'; -import 'DownloadGameDialog.dart'; - -class VersionSelection extends StatefulWidget { - final MinecraftSide side; - - const VersionSelection({Key? key, required this.side}) : super(key: key); - - @override - State createState() => _VersionSelectionState(); -} - -class _VersionSelectionState extends State { - int _selectedIndex = 0; - bool showRelease = true; - bool showSnapshot = false; - bool versionManifestLoading = true; - TextEditingController versionSearchController = TextEditingController(); - - String modLoaderName = I18n.format("version.list.mod.loader.vanilla"); - late List _widgetOptions; - - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - versionSearchController.dispose(); - super.dispose(); - } - - void _onItemTapped(int index) { - setState(() { - _selectedIndex = index; - }); - } - - @override - Widget build(BuildContext context) { - _widgetOptions = [ - SplitView( - gripSize: 3, - controller: SplitViewController(weights: [0.83]), - viewMode: SplitViewMode.Horizontal, - children: [ - FutureBuilder( - future: MCVersionManifest.formLoaderType( - ModLoaderUttily.getByI18nString(modLoaderName)), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - versionManifestLoading = - snapshot.connectionState != ConnectionState.done; - - if (!versionManifestLoading && snapshot.hasData) { - List versions = snapshot.data!.versions; - List formattedVersions = []; - formattedVersions = versions.where((version) { - bool inputVersionID = - version.id.contains(versionSearchController.text); - switch (version.type.name) { - case "release": - return showRelease && inputVersionID; - case "snapshot": - return showSnapshot && inputVersionID; - default: - return false; - } - }).toList(); - - return ListView.builder( - itemCount: formattedVersions.length, - itemBuilder: (context, index) { - final MCVersion version = formattedVersions[index]; - return ListTile( - title: Text(version.id), - onTap: () { - ModLoader loader = - ModLoaderUttily.getByI18nString(modLoaderName); - - // TODO: 支援啟動 Forge 遠古版本 - if (loader == ModLoader.forge && - version.comparableVersion < Version(1, 7, 0)) { - showDialog( - context: context, - builder: (context) => UnSupportedForgeVersion( - gameVersion: version.id)); - } else { - showDialog( - context: context, - builder: (context) { - return DownloadGameDialog( - "${loader.name.toCapitalized()}-${version.id}", - version, - loader, - widget.side); - }); - } - }, - ); - }); - } else if (snapshot.hasError) { - return Text(snapshot.error.toString()); - } else { - return const Center(child: RWLLoading()); - } - }), - Column( - children: [ - const SizedBox(height: 10), - SizedBox( - height: 45, - width: 200, - child: TextField( - controller: versionSearchController, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 15), - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: I18n.format("version.list.filter"), - ), - onEditingComplete: () { - setState(() {}); - }, - ), - ), - Text( - I18n.format("version.list.mod.loader"), - style: const TextStyle( - fontSize: 22.0, fontWeight: FontWeight.bold), - ), - SizedBox( - width: 100, - child: DropdownButton( - value: modLoaderName, - style: const TextStyle(color: Colors.lightBlue), - onChanged: (String? value) { - setState(() { - modLoaderName = value!; - }); - }, - isExpanded: true, - items: ModLoader.values - .where((e) => - e.supportInstall() && - e.supportedSides().any((e) => e == widget.side)) - .map((e) => e.i18nString) - .map>((String value) { - return DropdownMenuItem( - value: value, - alignment: Alignment.center, - child: Text(value, - style: const TextStyle( - fontSize: 17.5, fontFamily: 'font'), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis), - ); - }).toList(), - ), - ), - Text( - I18n.format("version.list.type"), - style: const TextStyle( - fontSize: 22.0, fontWeight: FontWeight.bold), - ), - ListTile( - leading: Checkbox( - onChanged: (bool? value) { - setState(() { - showRelease = value!; - }); - }, - value: showRelease, - ), - title: Text( - I18n.format("version.list.show.release"), - style: const TextStyle( - fontSize: 18, - ), - ), - ), - ListTile( - leading: Checkbox( - onChanged: (bool? value) { - setState(() { - showSnapshot = value!; - }); - }, - value: showSnapshot, - ), - title: Text( - I18n.format("version.list.show.snapshot"), - style: const TextStyle( - fontSize: 18, - ), - ), - ), - ], - ), - ], - ), - ListView( - children: [ - Text(I18n.format('modpack.install'), - style: const TextStyle(fontSize: 30, color: Colors.lightBlue), - textAlign: TextAlign.center), - Text(I18n.format('modpack.source'), - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 20)), - const SizedBox( - height: 12, - ), - Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - InkWell( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 60, - height: 60, - child: Image.asset("assets/images/CurseForge.png")), - const SizedBox( - width: 12, - ), - Text(I18n.format('modpack.from.curseforge'), - style: const TextStyle(fontSize: 20)), - ], - ), - onTap: () { - showDialog( - context: context, - builder: (context) => const CurseForgeModpackPage()); - }, - ), - const SizedBox( - height: 12, - ), - InkWell( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 60, - height: 60, - child: Image.asset("assets/images/FTB.png")), - const SizedBox( - width: 12, - ), - Text(I18n.format('modpack.from.ftb'), - style: const TextStyle(fontSize: 20)), - ], - ), - onTap: () { - showDialog( - context: context, builder: (context) => FTBModPack()); - }, - ), - const SizedBox( - height: 12, - ), - InkWell( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - Icons.computer, - size: 60, - ), - const SizedBox( - width: 12, - ), - Text(I18n.format('modpack.import'), - style: const TextStyle(fontSize: 20)), - ], - ), - onTap: () async { - final FilePickerResult? result = await FilePicker.platform - .pickFiles( - dialogTitle: I18n.format('modpack.file'), - type: FileType.custom, - allowedExtensions: [ - 'zip', - ]); - - if (result == null) { - return; - } - File file = File(result.files.single.path!); - - if (context.mounted) { - showDialog( - context: context, - builder: (context) => - CurseForgeHandler.installModpack(file)); - } - }, - ), - ], - )) - ], - ), - const RecommendedModpackScreen() - ]; - return Scaffold( - appBar: AppBar( - title: I18nText("version.list.instance.type"), - centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - tooltip: I18n.format("gui.back"), - onPressed: () { - navigator.pop(); - }, - ), - ), - body: _widgetOptions.elementAt(_selectedIndex), - bottomNavigationBar: widget.side == MinecraftSide.client - ? NavigationBar( - destinations: [ - NavigationDestination( - icon: SizedBox( - width: 30, - height: 30, - child: Image.asset("assets/images/Minecraft.png")), - label: 'Minecraft', - tooltip: ""), - NavigationDestination( - icon: const SizedBox( - width: 30, height: 30, child: Icon(Icons.folder)), - label: I18n.format('modpack.title'), - tooltip: ""), - NavigationDestination( - icon: const SizedBox( - width: 30, height: 30, child: Icon(Icons.reviews)), - tooltip: "", - label: I18n.format('version.recommended_modpack.title')), - ], - selectedIndex: _selectedIndex, - backgroundColor: - ThemeUtil.getThemeByConfig() == LauncherTheme.dark - ? Colors.black12.withAlpha(15) - : null, - onDestinationSelected: _onItemTapped, - ) - : SizedBox( - height: 60, - child: DecoratedBox( - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: Theme.of(context).colorScheme.background, - width: 0.2)), - ), - child: InkWell( - child: Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Column( - children: [ - SizedBox( - width: 30, - height: 30, - child: Image.asset("assets/images/Minecraft.png")), - const Text('Minecraft'), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/task/async_sub_task.dart b/lib/task/async_sub_task.dart new file mode 100644 index 000000000..9f051d8e3 --- /dev/null +++ b/lib/task/async_sub_task.dart @@ -0,0 +1,3 @@ +import 'package:rpmlauncher/task/basic_task.dart'; + +abstract class AsyncSubTask extends BasicTask {} diff --git a/lib/task/basic_task.dart b/lib/task/basic_task.dart new file mode 100644 index 000000000..08a0ee1a3 --- /dev/null +++ b/lib/task/basic_task.dart @@ -0,0 +1,259 @@ +import 'dart:async'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:quiver/iterables.dart'; +import 'package:rpmlauncher/task/fetch_task.dart'; +import 'package:rpmlauncher/task/task.dart'; +import 'package:rpmlauncher/task/async_sub_task.dart'; +import 'package:rpmlauncher/task/task_size.dart'; +import 'package:rpmlauncher/task/task_status.dart'; +import 'package:rpmlauncher/util/data.dart'; +import 'package:rpmlauncher/util/logger.dart'; +import 'package:uuid/uuid.dart'; + +/// You can extends this class to create a task. +abstract class BasicTask extends Equatable + with ChangeNotifier + implements Task { + // Private variables + TaskStatus _status = TaskStatus.queued; + double _progress = 0.0; + List _postSubTasks = []; + List _preSubTasks = []; + String? _message; + Object? _error; + R? _result; + + @protected + set status(value) => _status = value; + + @protected + set message(value) => _message = value; + + @protected + set progress(value) => _progress = value; + + @protected + set preSubTasks(value) => _preSubTasks = value; + + @protected + set postSubTasks(value) => _postSubTasks = value; + + @protected + set error(value) => _error = value; + + @protected + set result(value) => _result = value; + + BasicTask(); + + factory BasicTask.function(FutureOr Function() function, + {required String name, + required TaskSize size, + List preSubTasks = const [], + List postSubTasks = const []}) { + return _FunctionTask( + function: function, + name: name, + size: size, + preSubTasks: preSubTasks, + postSubTasks: postSubTasks); + } + + @override + final String id = const Uuid().v4(); + + @override + TaskStatus get status => _status; + + @override + bool get isFinished => + _status == TaskStatus.success || _status == TaskStatus.failed; + + @override + double get progress => _progress; + + @override + double get totalProgress { + final subTasks = allSubTask; + if (subTasks.isEmpty) { + return progress; + } + + final current = progress * size.weight + + subTasks + .map((e) => e.totalProgress * e.size.weight) + .reduce((value, element) => value + element); + final total = size.weight + + subTasks + .map((e) => e.size.weight) + .reduce((value, element) => value + element); + final totalProgress = current / total; + + return totalProgress; + } + + @override + List get postSubTasks => _postSubTasks; + + @override + List get preSubTasks => _preSubTasks; + + @override + List get allSubTask { + final tasks = [...preSubTasks, ...postSubTasks]; + + if (tasks.isEmpty) { + return []; + } else { + return tasks.expand((e) => e.preSubTasks + e.postSubTasks).toList(); + } + } + + @override + String? get message => _message; + + @override + Object? get error => _error; + + @override + R? get result => _result; + + @override + Future run() async { + _status = TaskStatus.running; + + try { + await preExecute(); + await runSubTasks(preSubTasks); + _result = await execute(); + await runSubTasks(postSubTasks); + await postExecute(); + allSubTask.whereType().forEach((e) => e.downloadSpeed = 0); + _status = TaskStatus.success; + setProgress(1.0); + } catch (e, st) { + _error = e; + _status = TaskStatus.failed; + logger.error(ErrorType.task, error, stackTrace: st); + } + + await dispose(); + + return _result; + } + + @override + @protected + void setStatus(TaskStatus status) { + _status = status; + notifyListeners(); + } + + @override + @protected + void setProgress(double progress) { + if (progress < 0 || progress > 1.0) { + throw Exception('Progress should be between 0.0 and 1.0'); + } + + _progress = progress; + notifyListeners(); + } + + @override + @protected + void addPostSubTask(Task task) { + _postSubTasks.add(task); + } + + @override + @protected + void addPreSubTask(Task task) { + _preSubTasks.add(task); + } + + @override + @protected + void setMessage(String? message) { + _message = message; + notifyListeners(); + } + + @override + @protected + Future preExecute() async {} + + @override + @protected + Future execute(); + + @override + @protected + Future postExecute() async {} + + @protected + Future runSubTasks(List subTasks) async { + final asyncTasks = subTasks.whereType(); + final syncTasks = subTasks.where((e) => e is! AsyncSubTask); + + if (asyncTasks.isNotEmpty) { + /// Max 15 async tasks can run at the same time. + final list = partition(asyncTasks, 15); + + for (final tasks in list) { + await Future.wait(tasks.map(_runSubTask)); + } + } + + for (final task in syncTasks) { + await _runSubTask(task); + } + } + + Future _runSubTask(Task task) async { + task.addListener(() { + if (task.message != null) { + setMessage(task.message); + } + }); + await task.run(); + notifyListeners(); + } + + @override + Future dispose() async { + super.dispose(); + } + + @override + List get props => + [name, id, status, progress, message, error, result]; +} + +class _FunctionTask extends BasicTask { + final FutureOr Function() function; + + @override + final String name; + + @override + final TaskSize size; + + @override + final List preSubTasks; + + @override + final List postSubTasks; + + _FunctionTask( + {required this.function, + required this.name, + required this.size, + this.preSubTasks = const [], + this.postSubTasks = const []}); + + @override + Future execute() async => await function(); +} diff --git a/lib/task/fetch_task.dart b/lib/task/fetch_task.dart new file mode 100644 index 000000000..7eff4bd97 --- /dev/null +++ b/lib/task/fetch_task.dart @@ -0,0 +1,75 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:rpmlauncher/task/async_sub_task.dart'; +import 'package:rpmlauncher/task/task_size.dart'; +import 'package:rpmlauncher/util/io_util.dart'; +import 'package:rpmlauncher/util/rpml_http_client.dart'; + +class FetchTask extends AsyncSubTask { + final String url; + final String path; + final String? hash; + final int? fileSize; + + FetchTask({required this.url, required this.path, this.hash, this.fileSize}); + + int _oldTotalDownloaded = 0; + int _newTotalDownloaded = 0; + int downloadSpeed = 0; + + void _init() { + Timer.periodic(const Duration(seconds: 1), (timer) async { + if (isFinished) { + timer.cancel(); + // If the file size is very small, the download speed will be 0, so we set it to its size. + if (downloadSpeed == 0 && + fileSize != null && + _newTotalDownloaded != 0) { + downloadSpeed = fileSize! ~/ 2; + await Future.delayed(const Duration(milliseconds: 500)); + downloadSpeed = fileSize! ~/ 2; + await Future.delayed(const Duration(milliseconds: 500)); + } + downloadSpeed = 0; + return; + } + + downloadSpeed = _newTotalDownloaded - _oldTotalDownloaded; + _oldTotalDownloaded = _newTotalDownloaded; + }); + } + + void _setFetchProgress(int downloaded, int total) { + if (total != -1) { + setProgress(downloaded / (fileSize ?? total)); + } + + _newTotalDownloaded = downloaded; + } + + @override + Future execute() async { + _init(); + final file = File(path); + + if (hash != null && IOUtil.isCachedFileSha1(file, hash!)) { + setProgress(1); + return; + } + + await httpClient.download( + url, + path, + onReceiveProgress: (received, total) { + _setFetchProgress(received, total); + }, + ); + } + + @override + String get name => 'fetch_task ($url)'; + + @override + TaskSize get size => TaskSize.tiny; +} diff --git a/lib/task/isolate_task.dart b/lib/task/isolate_task.dart new file mode 100644 index 000000000..306281086 --- /dev/null +++ b/lib/task/isolate_task.dart @@ -0,0 +1,72 @@ +import 'dart:async'; +import 'dart:isolate'; + +import 'package:rpmlauncher/model/io/isolate_option.dart'; +import 'package:rpmlauncher/task/basic_task.dart'; +import 'package:rpmlauncher/task/task.dart'; +import 'package:rpmlauncher/task/task_status.dart'; + +/// A task running in the [Isolate]. +abstract class IsolateTask extends BasicTask { + @override + Future run() { + return _runInIsolate(); + } + + Future _runInIsolate() async { + final updatePort = ReceivePort(); + final exitPort = ReceivePort(); + final errorPort = ReceivePort(); + final Completer completer = Completer(); + + updatePort.listen((task) { + if (task is Task) { + message = task.message; + status = task.status; + progress = task.progress; + error = task.error; + result = task.result; + preSubTasks = task.preSubTasks; + postSubTasks = task.postSubTasks; + } + + notifyListeners(); + }); + + exitPort.listen((_) { + completer.complete(result); + setStatus(TaskStatus.success); + }); + + errorPort.listen((_) { + final String error = _[0]; + final StackTrace? stackTrace = + _[1] != null ? StackTrace.fromString(_[1]) : null; + + completer.completeError(error, stackTrace); + setStatus(TaskStatus.failed); + }); + + final option = IsolateOption.create(this, ports: [updatePort.sendPort]); + final isolate = + await Isolate.spawn((IsolateOption> option) async { + option.init(); + final task = option.argument; + + task.addListener(() { + option.sendData(task); + }); + + await task._superRun(); + }, option, debugName: '${name}_$id'); + + isolate.addOnExitListener(exitPort.sendPort); + isolate.addErrorListener(errorPort.sendPort); + + return await completer.future; + } + + Future _superRun() { + return super.run(); + } +} diff --git a/lib/task/task.dart b/lib/task/task.dart new file mode 100644 index 000000000..679c1a5e9 --- /dev/null +++ b/lib/task/task.dart @@ -0,0 +1,73 @@ +import 'package:flutter/foundation.dart'; +import 'package:rpmlauncher/task/task_size.dart'; +import 'package:rpmlauncher/task/task_status.dart'; + +/// Disposable task abstract class. +abstract class Task extends Listenable { + String get name; + + String get id; + + TaskSize get size; + + /// Default status is [TaskStatus.queued]. + /// Also see [TaskStatus]. + TaskStatus get status; + + bool get isFinished; + + /// The value of progress should be between 0.0 and 1.0. + /// If the value is null, it means the task is not running or **unable to calculate** the progress. + double get progress; + + /// Calculate the total progress of this task and all sub-tasks. + /// This task and all sub-tasks each take up 50% of the total progress. + double get totalProgress; + + /// The list of sub-tasks should be executed **after** this task. + /// If this task failed, it would not be executed. + List get postSubTasks; + + /// The list of sub-tasks should be executed **before** this task. + /// If the sub-tasks failed, this task would not be executed. + List get preSubTasks; + + List get allSubTask; + + /// Represents the message of the current task execution stage. + String? get message; + + /// Will be null if the task is not failed. + Object? get error; + + R? get result; + + /// Run the task and run all sub-tasks. + /// + /// The task will be executed in the following order: + /// 1. [preExecute] + /// 1.1. [preSubTasks] + /// 2. [execute] + /// 2.1. [postSubTasks] (if the task is successful) + /// 3. [postExecute] (if the task is successful) + Future run(); + + void setStatus(TaskStatus status); + + void setProgress(double progress); + + void addPostSubTask(Task task); + + void addPreSubTask(Task task); + + void setMessage(String? message); + + /// Run before the task is executed. + Future preExecute(); + + /// The method to execute the task should return a result. + Future execute(); + + /// Run after the task is executed successfully. + Future postExecute(); +} diff --git a/lib/task/task_manager.dart b/lib/task/task_manager.dart new file mode 100644 index 000000000..358d7aee3 --- /dev/null +++ b/lib/task/task_manager.dart @@ -0,0 +1,39 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/task/fetch_task.dart'; +import 'package:rpmlauncher/task/task.dart'; + +final taskManager = TaskManager()..init(); + +class TaskManager extends ChangeNotifier { + /// Tasks managed by this class. + static final List _tasks = []; + + int downloadSpeed = 0; + + void init() { + Timer.periodic(const Duration(seconds: 1), (timer) { + final tasks = _tasks.expand((e) => e.allSubTask).whereType(); + downloadSpeed = tasks.fold(0, (p, e) => p + e.downloadSpeed); + + notifyListeners(); + }); + } + + /// Submit a task to run. + Future submit(Task task) async { + _tasks.add(task); + await task.run(); + notifyListeners(); + } + + /// Remove a task. + void remove(Task task) { + _tasks.remove(task); + } + + List getAll() { + return _tasks; + } +} diff --git a/lib/task/task_size.dart b/lib/task/task_size.dart new file mode 100644 index 000000000..b68edc1f4 --- /dev/null +++ b/lib/task/task_size.dart @@ -0,0 +1,14 @@ +enum TaskSize { + tiny(1), + small(2), + medium(4), + large(16), + xLarge(256); + + /// The weight of a task size. + /// The larger the weight, the more resources, and time the task will consume. + /// The weight of a task size is used to calculate the total progress of it. + final int weight; + + const TaskSize(this.weight); +} diff --git a/lib/task/task_status.dart b/lib/task/task_status.dart new file mode 100644 index 000000000..cb32930f5 --- /dev/null +++ b/lib/task/task_status.dart @@ -0,0 +1,14 @@ +/// Task status +/// +/// The status of a task can be one of the following: +/// +/// * [queued] - The task is ready to be executed. +/// * [running] - The task is currently running. +/// * [success] - The task has been successfully executed. +/// * [failed] - The task has been failed. +enum TaskStatus { + queued, + running, + success, + failed +} diff --git a/lib/widget/dialog/CheckDialog.dart b/lib/ui/dialog/check_dialog.dart similarity index 98% rename from lib/widget/dialog/CheckDialog.dart rename to lib/ui/dialog/check_dialog.dart index bb9c21f18..1a5431b8f 100644 --- a/lib/widget/dialog/CheckDialog.dart +++ b/lib/ui/dialog/check_dialog.dart @@ -8,6 +8,7 @@ class CheckDialog extends StatelessWidget { final void Function(BuildContext context)? onPressedCancel; const CheckDialog({ + super.key, required this.title, this.message, required this.onPressedOK, diff --git a/lib/ui/dialog/download_manger_dialog.dart b/lib/ui/dialog/download_manger_dialog.dart new file mode 100644 index 000000000..7c9d65fc2 --- /dev/null +++ b/lib/ui/dialog/download_manger_dialog.dart @@ -0,0 +1,396 @@ +import 'dart:async'; + +import 'package:blur/blur.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:quiver/iterables.dart'; +import 'package:rpmlauncher/i18n/i18n.dart'; +import 'package:rpmlauncher/task/task.dart'; +import 'package:rpmlauncher/task/task_manager.dart'; +import 'package:rpmlauncher/task/task_status.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/widget/round_divider.dart'; +import 'package:uuid/uuid.dart'; + +class DownloadMangerDialog extends StatefulWidget { + const DownloadMangerDialog({super.key}); + + static Future show(BuildContext context) async { + await showGeneralDialog( + context: context, + transitionDuration: const Duration(milliseconds: 400), + barrierDismissible: true, + barrierLabel: 'download_manager_dialog${const Uuid().v4()}', + pageBuilder: (context, animation, secondaryAnimation) { + return SlideTransition( + position: Tween( + begin: const Offset(-1, 0), + end: Offset.zero, + ).chain(CurveTween(curve: Curves.easeInOut)).animate(animation), + child: const DownloadMangerDialog()); + }); + } + + @override + State createState() => _DownloadMangerDialogState(); +} + +class _DownloadMangerDialogState extends State { + final int maxHistory = 60; + late final List downloadSpeedHistory = + List.generate(maxHistory, (_) => 0); + + @override + void initState() { + super.initState(); + + taskManager.addListener(() { + if (mounted) { + downloadSpeedHistory.add(taskManager.downloadSpeed); + + if (downloadSpeedHistory.length > maxHistory) { + downloadSpeedHistory.removeRange( + 0, downloadSpeedHistory.length - maxHistory); + } + setState(() {}); + } else { + taskManager.removeListener(() {}); + } + }); + } + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.transparent, + surfaceTintColor: Colors.transparent, + alignment: Alignment.topLeft, + insetPadding: EdgeInsets.zero, + child: Container( + constraints: + BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.35), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.only(right: 15), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + context.theme.backgroundColor, + context.theme.backgroundColor.withOpacity(0.8), + ], + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), blurRadius: 25), + ], + ), + child: Blur( + blur: 3, + colorOpacity: 0, + overlay: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + IntrinsicHeight( + child: Row( + children: [ + const Icon(Icons.downloading_rounded, size: 50), + Padding( + padding: const EdgeInsets.all(12), + child: RoundDivider( + size: 1.5, + color: context.theme.subTextColor)), + Text( + '下載管理', + style: TextStyle( + fontSize: 23, + color: context.theme.textColor), + ), + ], + ), + ), + const SizedBox(height: 12), + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + color: context.theme.dialogBackgroundColor, + child: Padding( + padding: const EdgeInsets.all(15), + child: Column( + children: [ + IntrinsicHeight( + child: Row( + children: [ + Column( + children: [ + Text('下載速率', + style: TextStyle( + color: context + .theme.primaryColor, + fontSize: 16)), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: + '${taskManager.downloadSpeed ~/ 1024} KiB', + style: TextStyle( + color: context + .theme.textColor, + )), + TextSpan( + text: ' / 秒', + style: TextStyle( + color: context.theme + .subTextColor, + fontWeight: + FontWeight.bold)), + ], + ), + ), + ], + ), + const Padding( + padding: EdgeInsets.symmetric( + horizontal: 12, vertical: 5), + child: RoundDivider(size: 1.5), + ), + Column( + children: [ + Text('預計時間', + style: TextStyle( + color: context + .theme.primaryColor, + fontSize: 16)), + const Text('無法計算'), + ], + ), + ], + ), + ), + _buildChart(downloadSpeedHistory) + ], + ), + ), + ), + ), + const SizedBox(height: 12), + Expanded( + child: ListView( + children: [ + _TaskList( + title: '進行中', + tasks: taskManager.getAll().where( + (e) => e.status == TaskStatus.running)), + _TaskList( + title: '排程中', + tasks: taskManager.getAll().where( + (e) => e.status == TaskStatus.queued)), + _TaskList( + title: '已完成', + tasks: taskManager + .getAll() + .where((e) => e.isFinished)), + ], + )), + ], + ), + ), + child: Container(), + ), + ), + ), + Align( + alignment: Alignment.topRight, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: ClipRRect( + borderRadius: BorderRadius.circular(50), + child: Container( + color: context.theme.dialogBackgroundColor, + width: 90, + height: 45, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + tooltip: '下載設定', + onPressed: () {}, + icon: Icon(Icons.tune_rounded, + color: context.theme.primaryColor)), + IconButton( + tooltip: I18n.format('gui.close'), + onPressed: () { + Navigator.pop(context); + }, + icon: const Icon(Icons.menu_open_rounded)) + ], + ), + ), + ), + ), + ) + ], + ), + ), + ); + } + + SizedBox _buildChart(List speedHistory) { + final spots = speedHistory + .asMap() + .entries + .map((e) => FlSpot(e.key.toDouble(), e.value.toDouble())) + .toList(); + + return SizedBox( + height: MediaQuery.of(context).size.height * 0.2, + child: LineChart(LineChartData( + gridData: FlGridData(show: false), + titlesData: FlTitlesData(show: false), + borderData: FlBorderData(show: false), + lineTouchData: LineTouchData(enabled: false), + clipData: FlClipData.all(), + maxY: max(speedHistory)! * 1.2, + maxX: maxHistory.toDouble(), + lineBarsData: [ + LineChartBarData( + spots: spots, + isCurved: true, + color: context.theme.primaryColor, + barWidth: 2, + isStrokeCapRound: true, + dotData: FlDotData(show: false), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + context.theme.primaryColor, + context.theme.primaryColor.withOpacity(0), + ], + ), + ), + ), + ], + )), + ); + } +} + +class _TaskList extends StatefulWidget { + final String title; + final Iterable tasks; + const _TaskList({required this.title, required this.tasks}); + + @override + State<_TaskList> createState() => __TaskListState(); +} + +class __TaskListState extends State<_TaskList> { + @override + Widget build(BuildContext context) { + if (widget.tasks.isEmpty) return Container(); + + return Column( + children: [ + Row( + children: [ + Text(widget.title), + const SizedBox(width: 5), + Text('(${widget.tasks.length})', + style: const TextStyle( + color: Color(0XFF7D7D7D), fontWeight: FontWeight.w600)), + ], + ), + const SizedBox(height: 10), + Wrap( + runSpacing: 10, + children: widget.tasks + .map((e) => _TaskTile( + task: e, + onRemove: () { + taskManager.remove(e); + setState(() {}); + }, + )) + .toList(), + ), + const SizedBox(height: 12), + ], + ); + } +} + +class _TaskTile extends StatelessWidget { + final Task task; + final VoidCallback onRemove; + const _TaskTile({required this.task, required this.onRemove}); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + color: context.theme.dialogBackgroundColor, + child: Padding( + padding: EdgeInsets.fromLTRB(15, task.isFinished ? 3 : 11, 5, 15), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + task.name, + style: TextStyle( + fontSize: 17, + color: context.theme.textColor, + fontWeight: FontWeight.w500), + ), + if (task.isFinished) + IconButton( + onPressed: onRemove, + iconSize: 20, + tooltip: '移除', + icon: Icon(Icons.cancel_rounded, + color: context.theme.subTextColor), + ) + ], + ), + Padding( + padding: const EdgeInsets.only(right: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(task.message ?? ''), + const SizedBox(height: 5), + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: TweenAnimationBuilder( + duration: const Duration(milliseconds: 250), + curve: Curves.easeInOut, + tween: Tween( + begin: 0, + end: task.totalProgress, + ), + builder: (context, value, child) => + LinearProgressIndicator( + value: value, + color: context.theme.primaryColor), + ), + ), + ], + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/widget/dialog/quick_setup.dart b/lib/ui/dialog/quick_setup.dart similarity index 96% rename from lib/widget/dialog/quick_setup.dart rename to lib/ui/dialog/quick_setup.dart index 007c3894e..a84c9a415 100644 --- a/lib/widget/dialog/quick_setup.dart +++ b/lib/ui/dialog/quick_setup.dart @@ -4,8 +4,8 @@ import 'package:rpmlauncher/i18n/language_selector.dart'; import 'package:rpmlauncher/util/data.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/LinkText.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/link_text.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/on_close.dart'; class QuickSetup extends StatefulWidget { const QuickSetup({ diff --git a/lib/widget/dialog/UpdaterDialog.dart b/lib/ui/dialog/updater_dialog.dart similarity index 95% rename from lib/widget/dialog/UpdaterDialog.dart rename to lib/ui/dialog/updater_dialog.dart index 97f491281..65ed72ba6 100644 --- a/lib/widget/dialog/UpdaterDialog.dart +++ b/lib/ui/dialog/updater_dialog.dart @@ -4,10 +4,10 @@ import 'package:flutter/material.dart'; import 'package:rpmlauncher/config/config.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/Process.dart'; +import 'package:rpmlauncher/util/process_util.dart'; import 'package:rpmlauncher/util/updater.dart'; import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/on_close.dart'; class UpdaterDialog extends StatelessWidget { VersionInfo info; @@ -79,7 +79,7 @@ class UpdaterDialog extends StatelessWidget { } else { if (Platform.isLinux) { if (LauncherInfo.isSnapcraftApp) { - xdgOpen( + ProcessUtil.xdgOpen( "snap://rpmlauncher?channel=latest/${launcherConfig.updateChannel == VersionTypes.stable ? "stable" : "beta"}"); } else if (LauncherInfo.isFlatpakApp) { Util.openUri( diff --git a/lib/ui/pages/account_page.dart b/lib/ui/pages/account_page.dart new file mode 100644 index 000000000..d23c4d634 --- /dev/null +++ b/lib/ui/pages/account_page.dart @@ -0,0 +1,150 @@ +import 'package:path/path.dart'; +import 'package:rpmlauncher/launcher/game_repository.dart'; +import 'package:rpmlauncher/model/account/account.dart'; +import 'package:rpmlauncher/i18n/i18n.dart'; +import 'package:rpmlauncher/ui/dialog/check_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/util/launcher_path.dart'; + +import 'package:rpmlauncher/util/data.dart'; +import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; +import 'ms_oauth_login.dart'; + +class _AccountScreenState extends State { + int? chooseIndex; + + @override + void initState() { + chooseIndex = AccountStorage.getIndex(); + super.initState(); + LauncherPath.currentConfigHome.watch(recursive: true).listen((event) { + if (absolute(event.path) == + absolute(GameRepository.getAccountFile().path) && + mounted) { + setState(() {}); + } + }); + } + + TextStyle title_ = const TextStyle( + fontSize: 20.0, + ); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(I18n.format('account.title')), + centerTitle: true, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + tooltip: I18n.format('gui.back'), + onPressed: () { + navigator.pop(); + }, + ), + ), + body: Container( + height: double.infinity, + width: double.infinity, + alignment: Alignment.center, + child: Builder( + builder: (context) { + if (AccountStorage.hasAccount) { + return ListView.builder( + itemBuilder: (context, index) { + final account = AccountStorage.getByIndex(index); + + return ListTile( + tileColor: chooseIndex == index + ? Theme.of(context).colorScheme.onInverseSurface + : null, + onTap: () { + chooseIndex = index; + AccountStorage.setIndex(index); + if (mounted) { + setState(() {}); + } + }, + title: + Text(account.username, textAlign: TextAlign.center), + subtitle: I18nText('account.type', + args: { + 'account_type': account.type.name.toCapitalized() + }, + textAlign: TextAlign.center), + leading: SizedBox( + width: 50, height: 50, child: account.imageWidget), + minLeadingWidth: 50, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.contact_page), + tooltip: I18n.format('account.skin.tooltip'), + onPressed: () {}, + ), + IconButton( + icon: const Icon(Icons.delete), + tooltip: I18n.format('account.delete.tooltip'), + onPressed: () { + showDialog( + context: context, + builder: (context) { + return CheckDialog( + title: I18n.format( + 'account.delete.tooltip'), + message: I18n.format( + 'account.delete.content'), + onPressedOK: (context) { + Navigator.of(context).pop(); + AccountStorage.removeByIndex(index); + if (mounted) { + setState(() {}); + } + }); + }); + }, + ), + ], + )); + }, + itemCount: AccountStorage.getCount(), + ); + } else { + return I18nText('account.delete.notfound', + style: const TextStyle(fontSize: 30)); + } + }, + ), + ), + floatingActionButton: FloatingActionButton.extended( + icon: const Icon(Icons.add), + onPressed: () { + showDialog( + context: context, + builder: (context) => const MSLoginWidget(), + ); + }, + label: I18nText( + 'account.add.title', + textAlign: TextAlign.center, + style: title_, + ), + ), + ); + } +} + +class AccountScreen extends StatefulWidget { + static const String route = '/account'; + + const AccountScreen({super.key}); + + static Future push(BuildContext context) { + return Navigator.of(context).pushNamed(route); + } + + @override + State createState() => _AccountScreenState(); +} diff --git a/lib/ui/pages/collection/choose_loader_page.dart b/lib/ui/pages/collection/choose_loader_page.dart new file mode 100644 index 000000000..df338bfcc --- /dev/null +++ b/lib/ui/pages/collection/choose_loader_page.dart @@ -0,0 +1,355 @@ +import 'package:blur/blur.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; +import 'package:rpmlauncher/model/game/loader.dart'; +import 'package:rpmlauncher/route/slide_route.dart'; +import 'package:rpmlauncher/ui/pages/collection/choose_version_page.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/view/row_scroll_view.dart'; +import 'package:rpmlauncher/ui/widget/blur_block.dart'; +import 'package:rpmlauncher/ui/widget/round_divider.dart'; +import 'package:rpmlauncher/ui/widget/rpml_button.dart'; +import 'package:rpmlauncher/ui/widget/rpml_tool_bar.dart'; + +class ChooseLoaderPage extends StatefulWidget { + const ChooseLoaderPage({super.key}); + + Future show(BuildContext context) { + return Navigator.push(context, SlideRoute(builder: (context) => this)); + } + + @override + State createState() => _ChooseLoaderPageState(); +} + +class _ChooseLoaderPageState extends State { + GameLoader? selected; + bool firstShow = true; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20), + child: Column( + children: [ + Row( + children: [ + const Icon(Icons.offline_bolt_rounded, size: 50), + const SizedBox(width: 12), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + const Text('載入器類型', style: TextStyle(fontSize: 30)), + Text('選擇用於建立您的收藏的載入器類型', + style: TextStyle( + color: context.theme.primaryColor, fontSize: 15)) + ]) + ], + ), + const SizedBox(height: 15), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: BlurBlock( + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 15, horizontal: 22), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _Loader( + name: '原版', + loader: GameLoader.vanilla, + selected: selected, + onSelectedChange: (loader) { + setState(() { + selected = loader; + firstShow = false; + }); + }, + firstShow: firstShow), + _Loader( + name: 'Forge', + loader: GameLoader.forge, + selected: selected, + onSelectedChange: (loader) { + setState(() { + selected = loader; + firstShow = false; + }); + }, + firstShow: firstShow), + _Loader( + name: 'Fabric', + loader: GameLoader.fabric, + selected: selected, + onSelectedChange: (loader) { + setState(() { + selected = loader; + firstShow = false; + }); + }, + firstShow: firstShow), + _Loader( + name: 'Quilt', + loader: GameLoader.quilt, + selected: selected, + onSelectedChange: (loader) { + setState(() { + selected = loader; + firstShow = false; + }); + }, + firstShow: firstShow), + ], + ), + ), + ), + ), + ), + const RPMLToolBar(), + ], + ), + ); + } +} + +class _Loader extends StatefulWidget { + final String name; + final GameLoader loader; + final GameLoader? selected; + final ValueChanged onSelectedChange; + final bool firstShow; + + const _Loader( + {required this.name, + required this.loader, + required this.selected, + required this.onSelectedChange, + required this.firstShow}); + + @override + State<_Loader> createState() => __LoaderState(); +} + +class __LoaderState extends State<_Loader> with SingleTickerProviderStateMixin { + late final AnimationController _animationController; + late final Animation _animation; + + @override + void initState() { + super.initState(); + + _animationController = AnimationController( + duration: const Duration(milliseconds: 300), vsync: this); + _animation = _animationController.drive(IntTween(begin: 1500, end: 7000)); + const spring = SpringDescription( + mass: 1, + stiffness: 320, + damping: 40, + ); + final simulation = SpringSimulation(spring, 0, 1, 0); + _animationController.animateWith(simulation); + _animation.addListener(() => setState(() {})); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isSelected = widget.selected == widget.loader; + final bool showContent = widget.selected == null || isSelected; + + final gradient = LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + context.theme.mainColor.withOpacity(isSelected ? 0.21 : 0.3), + context.theme.mainColor.withOpacity(isSelected ? 0.7 : 0.95) + ]); + + final content = Stack( + // We use a key to make sure the animation is triggered. + key: ValueKey(_animation.value.hashCode + widget.selected.hashCode), + children: [ + Center( + child: _buildBoxShadow( + color: isSelected + ? context.theme.primaryColor.withOpacity(0.8) + : context.theme.mainColor.withOpacity(0.6), + blur: isSelected ? 60 : 30, + offset: isSelected ? Offset.zero : const Offset(5, 5), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Image.asset(widget.loader.getIconAssets(), + width: isSelected ? 150 : 100, + height: isSelected ? 150 : 100), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: BlurBlock( + constraints: BoxConstraints(maxHeight: isSelected ? 105 : 85), + colorOpacity: 0.5, + child: Builder(builder: (context) { + if (isSelected) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.all(25), + child: SizedBox( + height: 80, + child: _buildBoxShadow( + child: RoundDivider( + size: 5, color: context.theme.primaryColor), + blur: 10, + )), + ), + _buildBoxShadow( + child: Text(widget.name, + style: const TextStyle( + fontSize: 35, fontWeight: FontWeight.w600)), + blur: 10, + color: context.theme.primaryColor.withOpacity(0.15), + offset: const Offset(5, 5)), + ], + ), + Expanded( + child: RowScrollView( + alignment: Alignment.centerRight, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(20), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + _buildBoxShadow( + child: const RPMLButton( + label: '安裝最新版', + width: 200, + height: 80, + labelType: RPMLButtonLabelType.text, + ), + blur: 15), + const SizedBox(width: 10), + RPMLButton( + label: '選擇更多版本', + isOutline: true, + width: 160, + height: 80, + backgroundBlur: 5, + labelType: RPMLButtonLabelType.text, + onPressed: () { + ChooseVersionPage(loader: widget.loader) + .show(context); + }, + ), + ], + ), + ), + ], + ), + ), + ) + ], + ); + } else { + return Text(widget.name, + style: const TextStyle( + fontSize: 30, fontWeight: FontWeight.w600)); + } + }), + ), + ), + ], + ); + + return Expanded( + flex: isSelected ? _animation.value : 1500, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: GestureDetector( + onTap: () async { + if (isSelected) { + _animationController.reverse().whenComplete(() { + widget.onSelectedChange(null); + }); + } else { + _animationController.reset(); + _animationController.forward(); + widget.onSelectedChange(widget.loader); + } + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(widget.loader.getBackgroundAssets()), + fit: BoxFit.cover)), + child: Stack( + children: [ + Blur( + colorOpacity: 0, + blur: showContent ? 2.5 : 10, + child: Container( + decoration: BoxDecoration(gradient: gradient), + ), + ), + if (!showContent) + Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Image.asset(widget.loader.getIconAssets(), + width: 75, height: 75), + ), + ), + if (showContent) + AnimatedSwitcher( + duration: + Duration(milliseconds: widget.firstShow ? 0 : 300), + switchInCurve: Curves.easeIn, + switchOutCurve: Curves.easeOut, + transitionBuilder: (child, animation) { + return FadeTransition( + opacity: animation, + child: SizeTransition( + axis: Axis.horizontal, + axisAlignment: -1.0, + sizeFactor: animation, + child: child)); + }, + child: content) + ], + ), + ), + ), + ), + ), + ); + } + + Widget _buildBoxShadow( + {required Widget child, + required double blur, + Color? color, + Offset offset = Offset.zero}) { + return Container( + decoration: BoxDecoration(boxShadow: [ + BoxShadow( + color: color ?? context.theme.primaryColor.withOpacity(0.4), + blurRadius: blur, + offset: offset) + ], borderRadius: BorderRadius.circular(10)), + child: child, + ); + } +} diff --git a/lib/ui/pages/collection/choose_version_page.dart b/lib/ui/pages/collection/choose_version_page.dart new file mode 100644 index 000000000..d797b987e --- /dev/null +++ b/lib/ui/pages/collection/choose_version_page.dart @@ -0,0 +1,250 @@ +import 'package:blur/blur.dart'; +import 'package:dyn_mouse_scroll/dyn_mouse_scroll.dart'; +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/api/mojang_meta_api.dart'; +import 'package:rpmlauncher/handler/game_version_handler.dart'; +import 'package:rpmlauncher/model/game/loader.dart'; +import 'package:rpmlauncher/model/game/version/mc_version.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_manifest.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_type.dart'; +import 'package:rpmlauncher/route/slide_route.dart'; +import 'package:rpmlauncher/ui/pages/collection/create_collection_page.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/widget/blur_block.dart'; +import 'package:rpmlauncher/ui/widget/rpml_button.dart'; +import 'package:rpmlauncher/ui/widget/rpml_tool_bar.dart'; + +class ChooseVersionPage extends StatefulWidget { + final GameLoader loader; + const ChooseVersionPage({super.key, required this.loader}); + + Future show(BuildContext context) { + return Navigator.of(context).push(SlideRoute(builder: (context) => this)); + } + + @override + State createState() => _ChooseVersionPageState(); +} + +class _ChooseVersionPageState extends State { + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20), + child: Column( + children: [ + Row( + children: [ + const Icon(Icons.view_in_ar_rounded, size: 50), + const SizedBox(width: 12), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + const Text('遊戲版本', style: TextStyle(fontSize: 30)), + Text('選擇用於建立您的收藏的 Minecraft 遊戲版本', + style: TextStyle( + color: context.theme.primaryColor, fontSize: 15)) + ]) + ], + ), + const SizedBox(height: 15), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: BlurBlock( + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 25, horizontal: 22), + child: Row( + children: [ + Expanded( + child: FutureBuilder( + future: MojangMetaAPI.getVersionManifest(), + builder: (context, snapshot) { + final data = snapshot.data; + if (data != null) { + final versions = data.versions.where( + (e) => e.type == MCVersionType.release); + final mainVersions = versions + .map((e) => GameVersionHandler.parse(e.id)) + .map((e) => '${e.major}.${e.minor}') + .toSet() // Remove duplicate + .toList(); + + return DynMouseScroll( + builder: (context, controller, physics) { + return ListView.builder( + controller: controller, + physics: physics, + itemCount: mainVersions.length, + itemBuilder: (context, index) { + final mainID = mainVersions[index]; + final versionList = versions + .where((e) => e.id.contains(mainID)) + .toList(); + + versionList.sort((a, b) => + GameVersionHandler.parse(b.id) + .compareTo( + GameVersionHandler.parse( + a.id))); + + return Padding( + padding: const EdgeInsets.all(8), + child: _MainVersionTile( + mainID: mainID, + versionList: versionList), + ); + }); + }); + } else { + return const Center( + child: CircularProgressIndicator()); + } + }), + ), + const SizedBox(width: 12), + ClipRRect( + borderRadius: BorderRadius.circular(15), + child: Container( + width: MediaQuery.of(context).size.width / 5, + color: context.theme.dialogBackgroundColor + .withOpacity(0.8), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 18, horizontal: 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('還是個Minecraft新手嗎?', + style: TextStyle(fontSize: 22)), + const SizedBox(height: 8), + Text( + '如果您是初次體驗 Minecraft,我們建議您直接安裝最新版本,藉此從零開始體驗 Minecraft 的遊戲世界!', + style: TextStyle( + color: context.theme.subTextColor)) + ]), + ), + ), + ) + ], + ), + ), + ), + ), + ), + RPMLToolBar(actions: [ + RPMLButton( + label: '返回上一頁', + isOutline: true, + icon: const Icon(Icons.low_priority_rounded), + onPressed: () { + Navigator.pop(context); + }, + ), + ]), + ], + ), + ); + } +} + +class _MainVersionTile extends StatefulWidget { + final String mainID; + final List versionList; + + const _MainVersionTile({required this.mainID, required this.versionList}); + + @override + State<_MainVersionTile> createState() => _MainVersionTileState(); +} + +class _MainVersionTileState extends State<_MainVersionTile> { + late ImageProvider backgroundImage; + + @override + void initState() { + backgroundImage = AssetImage('assets/images/versions/${widget.mainID}.png'); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + double height = 115; + double width = double.infinity; + + return ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Stack( + children: [ + Blur( + blur: 5, + colorOpacity: 0.5, + blurColor: context.theme.mainColor, + child: Image( + height: height, + width: width, + image: backgroundImage, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset('assets/images/background.png', + height: height, fit: BoxFit.cover, width: width); + }, + ), + ), + SizedBox( + height: height, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('${widget.mainID}.x', + style: const TextStyle( + fontSize: 30, fontWeight: FontWeight.bold)), + DecoratedBox( + decoration: BoxDecoration( + border: Border.all( + color: context.theme.primaryColor, width: 3), + borderRadius: BorderRadius.circular(10)), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RPMLButton( + height: 60, + width: 120, + label: '安裝', + labelType: RPMLButtonLabelType.text, + borderRadius: const BorderRadius.horizontal( + left: Radius.circular(10)), + onPressed: () { + CreateCollectionPage( + loader: GameLoader.vanilla, + version: widget.versionList.first, + image: backgroundImage) + .show(context); + }, + ), + RPMLButton( + height: 60, + width: 60, + color: Colors.transparent, + label: '更多版本', + icon: const Icon(Icons.auto_awesome_motion_outlined, + size: 25), + isOutline: true, + borderRadius: const BorderRadius.horizontal( + right: Radius.circular(10)), + onPressed: () {}, + ), + ], + ), + ) + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/pages/collection/create_collection_page.dart b/lib/ui/pages/collection/create_collection_page.dart new file mode 100644 index 000000000..ccffbbe2f --- /dev/null +++ b/lib/ui/pages/collection/create_collection_page.dart @@ -0,0 +1,151 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/model/game/loader.dart'; +import 'package:rpmlauncher/model/game/version/mc_version.dart'; +import 'package:rpmlauncher/route/slide_route.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/widget/blur_block.dart'; +import 'package:rpmlauncher/ui/widget/rpml_button.dart'; +import 'package:rpmlauncher/ui/widget/rpml_tool_bar.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/rpml_text_field.dart'; + +class CreateCollectionPage extends StatefulWidget { + final GameLoader loader; + final MCVersion version; + final ImageProvider image; + + const CreateCollectionPage( + {super.key, + required this.image, + required this.loader, + required this.version}); + + Future show(BuildContext context) { + return Navigator.of(context).push(SlideRoute(builder: (context) => this)); + } + + @override + State createState() => _CreateCollectionPageState(); +} + +class _CreateCollectionPageState extends State { + late final TextEditingController nameController; + final String defaultName = '新的自訂收藏'; + + @override + void initState() { + nameController = TextEditingController(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20), + child: Column( + children: [ + Row( + children: [ + const Icon(Icons.display_settings_rounded, size: 50), + const SizedBox(width: 12), + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + const Text('收藏資訊', style: TextStyle(fontSize: 30)), + Text('設定您要建立的收藏資訊', + style: TextStyle( + color: context.theme.primaryColor, fontSize: 15)) + ]) + ], + ), + const SizedBox(height: 15), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: BlurBlock( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 22), + child: Column( + children: [ + Expanded( + flex: 10, + child: BlurBlock( + color: context.theme.dialogBackgroundColor, + colorOpacity: 0.8, + borderRadius: BorderRadius.circular(15), + child: Row( + children: [ + Expanded( + flex: 3, + child: DecoratedBox( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: context.theme.mainColor + .withOpacity(0.3), + blurRadius: 20, + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(15), + child: Image( + image: widget.image, + height: double.infinity, + fit: BoxFit.cover), + ), + ), + ), + Expanded( + flex: 11, + child: Column( + children: [ + const Text('收藏名稱'), + RPMLTextField( + hintText: defaultName, + controller: nameController, + borderRadius: + const BorderRadius.horizontal( + right: Radius.circular(15)), + ) + ], + )), + const SizedBox(width: 28) + ], + ), + ), + ), + const SizedBox(height: 12), + Expanded( + flex: 9, + child: BlurBlock( + color: context.theme.dialogBackgroundColor, + colorOpacity: 0.8, + borderRadius: BorderRadius.circular(15), + child: const Row(), + ), + ) + ], + ), + )))), + RPMLToolBar(actions: [ + RPMLButton( + label: '返回上一頁', + isOutline: true, + icon: const Icon(Icons.low_priority_rounded), + onPressed: () { + Navigator.pop(context); + }, + ), + ]), + ], + ), + ); + } +} + +/* + Navigator.of(context).pushNamed(LauncherInfo.route); + taskManager.submit(GameInstallTask( + displayName: nameController.text, + loader: widget.loader, + version: widget.version)); +*/ \ No newline at end of file diff --git a/lib/ui/pages/collection_page.dart b/lib/ui/pages/collection_page.dart new file mode 100644 index 000000000..b4a5d3cc7 --- /dev/null +++ b/lib/ui/pages/collection_page.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/pages/collection/choose_loader_page.dart'; +import 'package:rpmlauncher/ui/widget/blur_block.dart'; +import 'package:rpmlauncher/ui/widget/rpml_button.dart'; +import 'package:rpmlauncher/ui/widget/rpml_tool_bar.dart'; + +class CollectionPage extends StatefulWidget { + const CollectionPage({super.key}); + + @override + State createState() => _CollectionPageState(); +} + +class _CollectionPageState extends State { + final _navigatorKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return Navigator( + key: _navigatorKey, + onGenerateRoute: (settings) { + return MaterialPageRoute( + builder: (context) => const _CollectionMainPage()); + }, + ); + } +} + +class _CollectionMainPage extends StatefulWidget { + const _CollectionMainPage(); + + @override + State<_CollectionMainPage> createState() => __CollectionMainPageState(); +} + +class __CollectionMainPageState extends State<_CollectionMainPage> { + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20), + child: Column( + children: [ + const Row( + children: [ + Icon(Icons.interests_rounded, size: 50), + SizedBox(width: 12), + Text('收藏庫', style: TextStyle(fontSize: 42)), + ], + ), + // TODO: category + const SizedBox(height: 50), + Expanded(child: _buildCollections()), + RPMLToolBar( + label: '建立自訂收藏', + onPressed: () { + const ChooseLoaderPage().show(context); + }, + icon: const Icon(Icons.loupe_rounded), + actions: [ + RPMLButton( + label: '選取多個', + isOutline: true, + icon: const Icon(Icons.done_all), + onPressed: () {}, + ), + RPMLButton( + label: '選取全部', + isOutline: true, + icon: const Icon(Icons.select_all), + onPressed: () {}, + ) + ], + ), + ], + ), + ); + } + + Widget _buildCollections() { + return const Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: BlurBlock(child: Column()), + ); + } +} diff --git a/lib/ui/pages/home_page.dart b/lib/ui/pages/home_page.dart new file mode 100644 index 000000000..92bf08976 --- /dev/null +++ b/lib/ui/pages/home_page.dart @@ -0,0 +1,77 @@ +import 'package:blur/blur.dart'; +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/pages/collection_page.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/widget/rpml_app_bar.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/background.dart'; + +class HomePage extends StatefulWidget { + static const String route = '/'; + + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + late final PageController controller; + + @override + void initState() { + controller = PageController(initialPage: 1); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + const Background(), + Container( + constraints: const BoxConstraints.expand(), + decoration: BoxDecoration( + gradient: LinearGradient(colors: [ + context.theme.mainColor.withOpacity(0.95), + Colors.transparent + ], begin: Alignment.topCenter, end: Alignment.bottomCenter)), + child: Blur( + blur: 7, + colorOpacity: 0, + child: Container(), + ), + ), + SafeArea( + child: Material( + color: Colors.transparent, + child: Row( + children: [ + RPMLAppBar( + onIndexChanged: (index) { + controller.animateToPage(index, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut); + }, + ), + Expanded( + child: PageView.builder( + controller: controller, + scrollDirection: Axis.vertical, + physics: const NeverScrollableScrollPhysics(), + itemCount: 5, + itemBuilder: (context, index) { + switch (index) { + case 1: + return const CollectionPage(); + default: + return const Text('Not Implemented'); + } + }), + ) + ], + ), + )) + ], + ); + } +} diff --git a/lib/screen/ms_oauth_login.dart b/lib/ui/pages/ms_oauth_login.dart similarity index 88% rename from lib/screen/ms_oauth_login.dart rename to lib/ui/pages/ms_oauth_login.dart index c73d1f2de..9cc76066f 100644 --- a/lib/screen/ms_oauth_login.dart +++ b/lib/ui/pages/ms_oauth_login.dart @@ -6,15 +6,13 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; import 'package:oauth2/oauth2.dart' as oauth2; import 'package:oauth2/oauth2.dart'; import 'package:rpmlauncher/account/microsoft_account_handler.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/on_close.dart'; /// 僅在測試中使用 @visibleForTesting @@ -25,6 +23,8 @@ final _authorizationEndpoint = final _tokenEndpoint = Uri.parse('https://login.live.com/oauth20_token.srf'); class MSLoginWidget extends StatefulWidget { + const MSLoginWidget({super.key}); + @override State createState() => _MSLoginState(); } @@ -69,14 +69,14 @@ class _MSLoginState extends State { } else { return AlertDialog( title: I18nText('account.add.microsoft.waiting'), - content: Column( + content: const Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, - children: const [ + children: [ SizedBox( height: 10, ), - RWLLoading(), + CircularProgressIndicator(), SizedBox( height: 10, ) @@ -99,7 +99,6 @@ class _MSLoginState extends State { LauncherInfo.microsoftClientID, //Client ID _authorizationEndpoint, _tokenEndpoint, - httpClient: _JsonAcceptingHttpClient(), ); Uri authorizationUrl = grant.getAuthorizationUrl(redirectUrl, scopes: ['XboxLive.signin', 'offline_access']); @@ -123,13 +122,3 @@ class _MSLoginState extends State { return params; } } - -class _JsonAcceptingHttpClient extends http.BaseClient { - final _httpClient = http.Client(); - - @override - Future send(http.BaseRequest request) { - request.headers['Accept'] = 'application/json'; - return _httpClient.send(request); - } -} diff --git a/lib/screen/loading_screen.dart b/lib/ui/screens/loading_screen.dart similarity index 55% rename from lib/screen/loading_screen.dart rename to lib/ui/screens/loading_screen.dart index 37a32b046..a8cb280af 100644 --- a/lib/screen/loading_screen.dart +++ b/lib/ui/screens/loading_screen.dart @@ -1,31 +1,37 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:dart_discord_rpc/dart_discord_rpc.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_window_close/flutter_window_close.dart'; +import 'package:lottie/lottie.dart'; import 'package:path/path.dart'; +import 'package:rpmlauncher/config/config.dart'; import 'package:rpmlauncher/database/database.dart'; import 'package:rpmlauncher/function/analytics.dart'; import 'package:rpmlauncher/handler/window_handler.dart'; -import 'package:rpmlauncher/screen/main_screen.dart'; -import 'package:rpmlauncher/config/config.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; +import 'package:rpmlauncher/model/account/account.dart'; +import 'package:rpmlauncher/ui/screens/main_screen.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/theme/theme_provider.dart'; +import 'package:rpmlauncher/ui/dialog/check_dialog.dart'; +import 'package:rpmlauncher/ui/dialog/updater_dialog.dart'; +import 'package:rpmlauncher/ui/dialog/quick_setup.dart'; +import 'package:rpmlauncher/util/data.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/util/theme.dart'; +import 'package:rpmlauncher/util/updater.dart'; import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmlauncher/widget/dialog/CheckDialog.dart'; import 'package:rpmlauncher_plugin/rpmlauncher_plugin.dart'; import 'package:rpmtw_api_client/rpmtw_api_client.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; - -import 'package:rpmlauncher/model/account/Account.dart'; +import 'package:synchronized/extension.dart'; class LoadingScreen extends StatefulWidget { + static const String route = '/loading'; const LoadingScreen({Key? key}) : super(key: key); @override @@ -33,36 +39,124 @@ class LoadingScreen extends StatefulWidget { } class _LoadingScreenState extends State { - bool isLoading = true; + final loadingStopwatch = Stopwatch(); + + final List tips = [ + 'rpmlauncher.tips.1', + 'rpmlauncher.tips.2', + 'rpmlauncher.tips.3', + ]; + late final String tip; @override void initState() { - super.initState(); + loadingStopwatch.start(); + loadingStopwatch.synchronized(() async { + while (true) { + // Loading end + if (!loadingStopwatch.isRunning) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + if (!launcherConfig.isInit && mounted) { + showDialog( + context: navigator.context, + barrierDismissible: false, + builder: (context) => const QuickSetup()); + } else { + Updater.checkForUpdate(Updater.fromConfig()).then((info) { + if (info.needUpdate && mounted) { + showDialog( + context: navigator.context, + builder: (context) => UpdaterDialog(info: info)); + } + }); + } + }); + break; + } + if (mounted) { + setState(() {}); + } + await Future.delayed(const Duration(milliseconds: 100)); + } + }); + tip = tips.elementAt(Random().nextInt(tips.length)); + + super.initState(); loading(); } @override Widget build(BuildContext context) { - if (isLoading) { - return DynamicThemeBuilder( - builder: (context, theme) => MaterialApp( - debugShowCheckedModeBanner: false, - theme: theme, - home: const Material( - child: RWLLoading(animations: true, logo: true)), - )); + double loadingProgress = loadingStopwatch.elapsedMilliseconds / 2800; + + if (loadingProgress > 1) { + loadingProgress = 1; + } + + if (loadingStopwatch.isRunning) { + return ThemeProvider(builder: (context, theme) { + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: LauncherTheme.getMaterialTheme(context), + home: Material( + child: SafeArea( + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(20.0), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + I18nText('rpmlauncher.tips.title', + style: TextStyle( + fontSize: 15, + fontStyle: FontStyle.italic, + color: context.theme.subTextColor)), + const SizedBox(height: 5), + I18nText(tip, + style: TextStyle( + fontSize: 18, + color: context.theme.textColor), + textAlign: TextAlign.left), + ]), + ], + ), + ), + Align( + alignment: Alignment.center, + child: Container( + alignment: Alignment.center, + child: FractionallySizedBox( + heightFactor: 0.6, + child: Lottie.asset( + 'assets/images/loading_animation.json', + ), + ), + ), + ), + ], + ), + ), + ), + ); + }); } else { return const MainScreen(); } } Future loading() async { + await Future.delayed(const Duration(milliseconds: 800 * 2)); logger.info('Loading'); await WindowHandler.init(); await Data.argsInit(); RPMTWApiClient.init(); await Database.init(); + await AccountStorage.init(); if (!kTestMode) { if (WindowHandler.isMainWindow) { try { @@ -129,13 +223,15 @@ class _LoadingScreenState extends State { options.dsn = 'https://18a8e66bd35c444abc0a8fa5b55843d7@o1068024.ingest.sentry.io/6062176'; options.tracesSampleRate = 1.0; + options.attachScreenshot = true; + FutureOr beforeSend(SentryEvent event, {dynamic hint}) async { if (launcherConfig.isInit && kReleaseMode) { MediaQueryData data = - MediaQueryData.fromWindow(WidgetsBinding.instance.window); + MediaQueryData.fromView(WidgetsBinding.instance.window); Size size = data.size; - String? userName = AccountStorage().getDefault()?.username ?? + String? userName = AccountStorage.getDefault()?.username ?? Platform.environment['USERNAME']; SentryEvent newEvent; @@ -144,15 +240,18 @@ class _LoadingScreenState extends State { List? exceptions = event.exceptions; if (exceptions != null) { - exceptions.forEach((SentryException exception) { - exception.stackTrace?.frames.forEach((frames) { - if ((frames.inApp ?? false) && - frames.package == 'rpmlauncher') { - githubSourceMap.add( - 'https://github.com/RPMTW/RPMLauncher/blob/${LauncherInfo.isDebugMode ? 'develop' : LauncherInfo.getFullVersion()}/${frames.absPath?.replaceAll('package:rpmlauncher', 'lib/')}#L${frames.lineNo}'); + for (final exception in exceptions) { + final frames = exception.stackTrace?.frames; + if (frames != null) { + for (final frames in frames) { + if ((frames.inApp ?? false) && + frames.package == 'rpmlauncher') { + githubSourceMap.add( + 'https://github.com/RPMTW/RPMLauncher/blob/${LauncherInfo.isDebugMode ? 'develop' : LauncherInfo.getFullVersion()}/${frames.absPath?.replaceAll('package:rpmlauncher', 'lib/')}#L${frames.lineNo}'); + } } - }); - }); + } + } } newEvent = event.copyWith( user: SentryUser( @@ -162,7 +261,7 @@ class _LoadingScreenState extends State { data: { 'userOrigin': LauncherInfo.userOrigin, 'githubSourceMap': githubSourceMap, - 'config': ConfigHelper.getAll(), + 'config': configHelper.getAll(), }), contexts: event.contexts.copyWith( device: SentryDevice( @@ -173,7 +272,6 @@ class _LoadingScreenState extends State { 1024 * 1024) .toInt(), - language: Platform.localeName, name: Platform.localHostname, simulator: false, screenHeightPixels: size.height.toInt(), @@ -181,9 +279,6 @@ class _LoadingScreenState extends State { screenDensity: data.devicePixelRatio, online: true, screenDpi: (data.devicePixelRatio * 160).toInt(), - screenResolution: '${size.width}x${size.height}', - theme: ThemeUtil.getThemeByID(launcherConfig.themeId).name, - timezone: DateTime.now().timeZoneName, )), exceptions: exceptions); @@ -204,9 +299,10 @@ class _LoadingScreenState extends State { } await googleAnalytics?.ping(); + loadingStopwatch.stop(); - setState(() { - isLoading = false; - }); + if (mounted) { + setState(() {}); + } } } diff --git a/lib/screen/main_screen.dart b/lib/ui/screens/main_screen.dart similarity index 83% rename from lib/screen/main_screen.dart rename to lib/ui/screens/main_screen.dart index 0b581a847..cf4fa3492 100644 --- a/lib/screen/main_screen.dart +++ b/lib/ui/screens/main_screen.dart @@ -4,20 +4,18 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:no_context_navigation/no_context_navigation.dart'; -import 'package:provider/provider.dart'; -import 'package:rpmlauncher/function/counter.dart'; -import 'package:rpmlauncher/util/data.dart'; - -import 'package:rpmlauncher/route/generate_route.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; +import 'package:rpmlauncher/route/rpml_navigator_observer.dart'; +import 'package:rpmlauncher/route/generate_route.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/theme/theme_provider.dart'; +import 'package:rpmlauncher/ui/widget/launcher_shortcuts.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/theme.dart'; -import 'package:rpmlauncher/route/RPMNavigatorObserver.dart'; -import 'package:rpmlauncher/widget/launcher_shortcuts.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; class MainScreen extends StatefulWidget { - const MainScreen(); + const MainScreen({super.key}); + @override State createState() => _MainScreenState(); } @@ -25,20 +23,16 @@ class MainScreen extends StatefulWidget { class _MainScreenState extends State { @override Widget build(BuildContext context) { - return Provider( - create: (context) { - logger.info('Provider Created'); - return Counter.create(); - }, - child: DynamicThemeBuilder(builder: (context, theme) { + return ThemeProvider( + builder: (context, theme) { return LauncherShortcuts( child: MaterialApp( debugShowCheckedModeBanner: false, navigatorKey: NavigationService.navigationKey, title: LauncherInfo.getUpperCaseName(), - theme: theme, + theme: LauncherTheme.getMaterialTheme(context), navigatorObservers: [ - RPMNavigatorObserver(), + RPMLNavigatorObserver(), SentryNavigatorObserver() ], localizationsDelegates: const [ @@ -127,15 +121,10 @@ class _MainScreenState extends State { return widget ?? Scaffold(body: Center(child: Text(title, style: style))); }, - onGenerateInitialRoutes: (String initialRouteName) { - return [ - onGenerateRoute(RouteSettings(name: LauncherInfo.route)) - ]; - }, - onGenerateRoute: (RouteSettings settings) => - onGenerateRoute(settings)), + initialRoute: LauncherInfo.route, + onGenerateRoute: onGenerateRoute), ); - }), + }, ); } } diff --git a/lib/ui/theme/launcher_theme.dart b/lib/ui/theme/launcher_theme.dart new file mode 100644 index 000000000..31e682a14 --- /dev/null +++ b/lib/ui/theme/launcher_theme.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:rpmlauncher/config/config.dart'; +import 'package:rpmlauncher/i18n/i18n.dart'; +import 'package:rpmlauncher/ui/theme/rpml_theme_data.dart'; +import 'package:rpmlauncher/ui/theme/rpml_theme_type.dart'; +import 'package:rpmlauncher/ui/theme/theme_provider.dart'; + +class LauncherTheme { + static ThemeChangeNotifier of(BuildContext context) { + return Provider.of(context, listen: false); + } + + static RPMLThemeType getTypeByConfig() { + final id = launcherConfig.themeId; + + return getTypeById(id); + } + + static RPMLThemeType getTypeById(int id) { + try { + return RPMLThemeType.values[id]; + } on StateError { + return RPMLThemeType.dark; + } + } + + static String toI18nString(RPMLThemeType type) { + switch (type) { + case RPMLThemeType.light: + return I18n.format('settings.appearance.theme.light'); + case RPMLThemeType.dark: + return I18n.format('settings.appearance.theme.dark'); + } + } + + static int getSystem() { + final brightness = WidgetsBinding.instance.window.platformBrightness; + + switch (brightness) { + case Brightness.light: + return 0; + + case Brightness.dark: + return 1; + } + } + + static ThemeData getMaterialTheme(BuildContext context) { + return ThemeData( + useMaterial3: true, + fontFamilyFallback: const ['Asap', 'GenJyuuGothic'], + brightness: context.theme.type == RPMLThemeType.light + ? Brightness.light + : Brightness.dark, + tooltipTheme: TooltipThemeData( + textStyle: TextStyle( + color: context.theme.dialogBackgroundColor, + fontWeight: FontWeight.bold), + waitDuration: const Duration(milliseconds: 120), + ), + colorSchemeSeed: const Color(0xFF14AE5C)); + } +} + +extension ThemeExtension on BuildContext { + RPMLThemeData get theme => LauncherTheme.of(this).themeData; +} diff --git a/lib/ui/theme/rpml_theme_data.dart b/lib/ui/theme/rpml_theme_data.dart new file mode 100644 index 000000000..1fd2c2259 --- /dev/null +++ b/lib/ui/theme/rpml_theme_data.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/theme/rpml_theme_type.dart'; + +class RPMLThemeData { + final RPMLThemeType type; + final Color mainColor; + final Color primaryColor; + + final Color backgroundColor; + final Color dialogBackgroundColor; + + final Color textColor; + final Color subTextColor; + final Color borderColor; + + const RPMLThemeData({ + required this.type, + required this.mainColor, + required this.primaryColor, + required this.backgroundColor, + required this.dialogBackgroundColor, + required this.textColor, + required this.subTextColor, + required this.borderColor, + }); + + factory RPMLThemeData.byType(RPMLThemeType type) { + switch (type) { + case RPMLThemeType.light: + return RPMLThemeData.light(); + case RPMLThemeType.dark: + return RPMLThemeData.dark(); + } + } + + factory RPMLThemeData.light() { + return const RPMLThemeData( + type: RPMLThemeType.light, + mainColor: Color.fromARGB(255, 148, 191, 168), + primaryColor: Color(0XFF14AE5C), + backgroundColor: Color.fromARGB(255, 177, 197, 174), + dialogBackgroundColor: Color.fromARGB(255, 185, 232, 207), + textColor: Colors.black, + subTextColor: Colors.black87, + borderColor: Color(0XFF8F8F8F)); + } + + factory RPMLThemeData.dark() { + return const RPMLThemeData( + type: RPMLThemeType.dark, + mainColor: Colors.black, + primaryColor: Color(0XFF14AE5C), + backgroundColor: Color(0xFF1E1E1E), + dialogBackgroundColor: Color(0XFF2F2F2F), + textColor: Color(0xFFFFFFFF), + subTextColor: Colors.white54, + borderColor: Color(0XFF56514D)); + } +} diff --git a/lib/ui/theme/rpml_theme_type.dart b/lib/ui/theme/rpml_theme_type.dart new file mode 100644 index 000000000..e2022d36b --- /dev/null +++ b/lib/ui/theme/rpml_theme_type.dart @@ -0,0 +1,4 @@ +enum RPMLThemeType { + light, + dark, +} diff --git a/lib/ui/theme/theme_provider.dart b/lib/ui/theme/theme_provider.dart new file mode 100644 index 000000000..b551e9e0b --- /dev/null +++ b/lib/ui/theme/theme_provider.dart @@ -0,0 +1,46 @@ +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/ui/theme/rpml_theme_data.dart'; +import 'package:rpmlauncher/ui/theme/rpml_theme_type.dart'; + +class ThemeProvider extends StatefulWidget { + final Widget Function(BuildContext context, RPMLThemeData themeData) builder; + + const ThemeProvider({Key? key, required this.builder}) : super(key: key); + + @override + State createState() => _ThemeProviderState(); +} + +class _ThemeProviderState extends State { + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (_) => ThemeChangeNotifier( + RPMLThemeData.byType(LauncherTheme.getTypeByConfig())), + child: + Consumer(builder: (context, notifier, child) { + return widget.builder(context, notifier._themeData); + })); + } +} + +class ThemeChangeNotifier extends ChangeNotifier implements ReassembleHandler { + RPMLThemeData _themeData; + + RPMLThemeData get themeData => _themeData; + + ThemeChangeNotifier(this._themeData); + + void setTheme(RPMLThemeType type) { + _themeData = RPMLThemeData.byType(type); + notifyListeners(); + } + + /// Handle hot reload + @override + void reassemble() { + setTheme(LauncherTheme.getTypeByConfig()); + } +} diff --git a/lib/view/row_scroll_view.dart b/lib/ui/view/row_scroll_view.dart similarity index 85% rename from lib/view/row_scroll_view.dart rename to lib/ui/view/row_scroll_view.dart index 181e52f2c..5e4dbd2c8 100644 --- a/lib/view/row_scroll_view.dart +++ b/lib/ui/view/row_scroll_view.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; class RowScrollView extends StatelessWidget { late ScrollController _controller; - bool center; + Alignment alignment; Widget child; RowScrollView({ Key? key, ScrollController? controller, - this.center = true, + this.alignment = Alignment.center, required this.child, }) : super(key: key) { _controller = controller ?? ScrollController(); @@ -17,7 +17,7 @@ class RowScrollView extends StatelessWidget { @override Widget build(BuildContext context) { return Align( - alignment: center ? Alignment.center : Alignment.centerLeft, + alignment: alignment, child: Scrollbar( controller: _controller, child: SingleChildScrollView( diff --git a/lib/ui/widget/account_manage_button.dart b/lib/ui/widget/account_manage_button.dart new file mode 100644 index 000000000..56f26f0a6 --- /dev/null +++ b/lib/ui/widget/account_manage_button.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/i18n/i18n.dart'; +import 'package:rpmlauncher/model/account/account.dart'; +import 'package:rpmlauncher/ui/pages/account_page.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; + +class AccountManageButton extends StatelessWidget { + const AccountManageButton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + Account? currentAccount = AccountStorage.getDefault(); + + if (AccountStorage.hasAccount) { + return Tooltip( + message: I18n.format('account.title'), + child: InkResponse( + borderRadius: BorderRadius.circular(30), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: currentAccount!.imageWidget, + ), + onTap: () => AccountScreen.push(context), + ), + ); + } else { + return IconButton( + icon: Icon(Icons.manage_accounts, color: context.theme.textColor), + onPressed: () { + AccountScreen.push(context); + }, + tooltip: I18n.format('account.title'), + ); + } + } +} diff --git a/lib/ui/widget/blur_block.dart b/lib/ui/widget/blur_block.dart new file mode 100644 index 000000000..4abc1daea --- /dev/null +++ b/lib/ui/widget/blur_block.dart @@ -0,0 +1,34 @@ +import 'package:blur/blur.dart'; +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; + +class BlurBlock extends StatelessWidget { + final Widget child; + final double blur; + final double colorOpacity; + final Color? color; + final BoxConstraints? constraints; + final BorderRadius borderRadius; + + const BlurBlock( + {super.key, + required this.child, + this.blur = 15, + this.colorOpacity = 0.3, + this.color, + this.constraints, + this.borderRadius = + const BorderRadius.vertical(top: Radius.circular(10))}); + + @override + Widget build(BuildContext context) { + return Blur( + blur: blur, + blurColor: color ?? context.theme.mainColor, + colorOpacity: colorOpacity, + borderRadius: borderRadius, + overlay: child, + child: Container(constraints: constraints), + ); + } +} diff --git a/lib/widget/keep_alive_wrapper.dart b/lib/ui/widget/keep_alive_wrapper.dart similarity index 100% rename from lib/widget/keep_alive_wrapper.dart rename to lib/ui/widget/keep_alive_wrapper.dart diff --git a/lib/widget/launcher_shortcuts.dart b/lib/ui/widget/launcher_shortcuts.dart similarity index 81% rename from lib/widget/launcher_shortcuts.dart rename to lib/ui/widget/launcher_shortcuts.dart index 4292a9ab7..98f2b836f 100644 --- a/lib/widget/launcher_shortcuts.dart +++ b/lib/ui/widget/launcher_shortcuts.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:rpmlauncher/handler/window_handler.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/Intents.dart'; +import 'package:rpmlauncher/ui/screens/loading_screen.dart'; +import 'package:rpmlauncher/util/intents.dart'; import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; +import 'package:rpmlauncher/ui/widget/rpmtw_design/on_close.dart'; class LauncherShortcuts extends StatelessWidget { final Widget child; @@ -17,19 +17,19 @@ class LauncherShortcuts extends StatelessWidget { return Actions( actions: >{ EscIntent: CallbackAction(onInvoke: (EscIntent intent) { - if (navigator.canPop()) { + if (Navigator.canPop(context)) { try { - navigator.pop(true); + Navigator.pop(context, true); } catch (e) { - navigator.pop(); + Navigator.pop(context); } } return; }), RestartIntent: CallbackAction(onInvoke: (RestartIntent intent) { - logger.info("Reload"); - navigator.pushReplacementNamed(HomePage.route); + logger.info('Reload Launcher'); + navigator.pushReplacementNamed(LoadingScreen.route); Future.delayed(Duration.zero, () { showDialog( context: navigator.context, diff --git a/lib/ui/widget/round_divider.dart b/lib/ui/widget/round_divider.dart new file mode 100644 index 000000000..7e4f56963 --- /dev/null +++ b/lib/ui/widget/round_divider.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; + +class RoundDivider extends StatelessWidget { + final double size; + final Color? color; + + const RoundDivider({super.key, required this.size, this.color}); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: color ?? context.theme.borderColor, width: size), + ))); + } +} diff --git a/lib/ui/widget/rpml_app_bar.dart b/lib/ui/widget/rpml_app_bar.dart new file mode 100644 index 000000000..88645ca12 --- /dev/null +++ b/lib/ui/widget/rpml_app_bar.dart @@ -0,0 +1,247 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/dialog/download_manger_dialog.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/util/data.dart'; +import 'package:rpmlauncher/util/io_util.dart'; +import 'package:rpmlauncher/util/util.dart'; + +class RPMLAppBar extends StatefulWidget { + final Function(int index)? onIndexChanged; + + const RPMLAppBar({super.key, required this.onIndexChanged}); + + @override + State createState() => _RPMLAppBarState(); +} + +class _RPMLAppBarState extends State { + int selectedIndex = 1; + bool menuExpanded = false; + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: const BorderRadius.only( + bottomRight: Radius.circular(10), topRight: Radius.circular(10)), + child: Container( + constraints: BoxConstraints(maxWidth: menuExpanded ? 270 : 70), + child: Column( + children: [ + const SizedBox(height: 20), + Tooltip( + message: '開啟 RPMTW 官方網站', + child: InkResponse( + borderRadius: BorderRadius.circular(5), + onTap: () { + Util.openUri('https://rpmtw.com'); + }, + child: Image.asset('assets/images/logo.png', + height: 50, width: 50), + ), + ), + const SizedBox(height: 15), + Wrap( + spacing: 12, + direction: Axis.vertical, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: IconButton( + onPressed: () { + setState(() { + menuExpanded = !menuExpanded; + }); + }, + icon: const Icon(Icons.menu_outlined), + selectedIcon: const Icon(Icons.menu_open_outlined), + isSelected: menuExpanded, + style: IconButton.styleFrom( + backgroundColor: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + color: context.theme.textColor, + iconSize: 32, + tooltip: '展開選單'), + ), + _buildActionButton( + label: '探索', + icon: const Icon(Icons.explore_outlined), + selectedIcon: const Icon(Icons.explore_rounded), + selected: selectedIndex == 0, + onPressed: () { + setState(() { + selectedIndex = 0; + widget.onIndexChanged?.call(selectedIndex); + }); + }, + ), + _buildActionButton( + label: '收藏庫', + icon: const Icon(Icons.interests_outlined), + selectedIcon: const Icon(Icons.interests), + selected: selectedIndex == 1, + onPressed: () { + setState(() { + selectedIndex = 1; + widget.onIndexChanged?.call(selectedIndex); + }); + }, + ), + _buildActionButton( + label: '釘選與最愛', + icon: const Icon(Icons.push_pin_outlined), + selectedIcon: const Icon(Icons.push_pin_rounded), + selected: selectedIndex == 2, + onPressed: () { + setState(() { + selectedIndex = 2; + widget.onIndexChanged?.call(selectedIndex); + }); + }, + ), + _buildActionButton( + label: '多人遊戲', + icon: const Icon(Icons.groups_3_outlined), + selectedIcon: const Icon(Icons.groups_3_rounded), + selected: selectedIndex == 3, + onPressed: () { + setState(() { + selectedIndex = 3; + widget.onIndexChanged?.call(selectedIndex); + }); + }, + ), + _buildActionButton( + label: '新聞', + icon: const Icon(Icons.newspaper_rounded), + selectedIcon: const Icon(Icons.newspaper), + selected: selectedIndex == 4, + onPressed: () { + setState(() { + selectedIndex = 4; + widget.onIndexChanged?.call(selectedIndex); + }); + }, + ), + ], + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton( + onPressed: () { + DownloadMangerDialog.show(context); + }, + tooltip: '下載管理', + icon: Icon( + Icons.downloading_rounded, + color: context.theme.textColor, + )), + IconButton( + onPressed: () { + IOUtil.openFileManager(dataHome); + }, + tooltip: '開啟儲存位置', + icon: Icon( + Icons.folder_open_rounded, + color: context.theme.textColor, + )), + IconButton( + onPressed: () {}, + tooltip: '檢查更新', + icon: Icon( + Icons.model_training_rounded, + color: context.theme.textColor, + )), + IconButton( + onPressed: () { + Navigator.pushNamed(context, '/settings'); + }, + tooltip: '設定', + icon: Icon( + Icons.tune_rounded, + color: context.theme.textColor, + )), + ], + ), + ), + const SizedBox(height: 22) + ], + )), + ); + } + + Widget _buildActionButton({ + required String label, + required Widget icon, + required Widget selectedIcon, + required bool selected, + required VoidCallback onPressed, + }) { + final indicator = Container( + height: 35, + width: 7, + decoration: selected + ? BoxDecoration( + color: context.theme.primaryColor, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(3), + bottomRight: Radius.circular(3)), + ) + : null, + ); + const double iconSize = 32; + + return Row( + children: [ + indicator, + if (menuExpanded) + Tooltip( + message: label, + child: TextButton.icon( + label: Text( + label, + style: TextStyle( + color: context.theme.textColor, + fontSize: 16, + ), + ), + onPressed: onPressed, + style: TextButton.styleFrom( + alignment: Alignment.centerLeft, + backgroundColor: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + minimumSize: const Size(150, 60)), + icon: IconTheme( + data: IconThemeData( + color: context.theme.textColor, + size: iconSize, + ), + child: selected ? selectedIcon : icon), + ), + ), + if (!menuExpanded) + IconButton( + tooltip: label, + onPressed: onPressed, + icon: icon, + selectedIcon: selectedIcon, + isSelected: selected, + color: context.theme.textColor, + iconSize: iconSize, + style: IconButton.styleFrom( + backgroundColor: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + ), + ], + ); + } +} diff --git a/lib/ui/widget/rpml_button.dart b/lib/ui/widget/rpml_button.dart new file mode 100644 index 000000000..ce29fd46a --- /dev/null +++ b/lib/ui/widget/rpml_button.dart @@ -0,0 +1,102 @@ +import 'package:blur/blur.dart'; +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; + +class RPMLButton extends StatelessWidget { + final String label; + final Widget? icon; + final Function()? onPressed; + + final double width; + final double height; + final Color? color; + final TextStyle? labelStyle; + final RPMLButtonLabelType labelType; + final bool isOutline; + final double? backgroundBlur; + final BorderRadius? borderRadius; + + const RPMLButton( + {Key? key, + required this.label, + this.icon, + this.onPressed, + this.height = 45, + this.width = 45, + this.color, + this.labelStyle, + this.labelType = RPMLButtonLabelType.tooltip, + this.isOutline = false, + this.backgroundBlur, + this.borderRadius}) + : super(key: key); + + @override + Widget build(BuildContext context) { + final iconSize = height / 2; + final buttonColor = color ?? context.theme.primaryColor; + final borderColor = color ?? context.theme.borderColor; + final textColor = context.theme.textColor; + + final content = Row(mainAxisSize: MainAxisSize.min, children: [ + if (icon != null) + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: iconSize, + child: IconTheme.merge( + data: IconThemeData( + size: iconSize, + color: textColor, + ), + child: icon!)), + if (labelType == RPMLButtonLabelType.text) const SizedBox(width: 9), + ], + ), + if (labelType == RPMLButtonLabelType.text) + Text( + label, + style: labelStyle ?? TextStyle(color: textColor, fontSize: 18), + ), + ]); + + final button = OutlinedButton( + onPressed: () { + onPressed?.call(); + }, + style: OutlinedButton.styleFrom( + backgroundColor: isOutline ? null : buttonColor, + side: isOutline + ? BorderSide(color: borderColor, width: 2) + : BorderSide.none, + shape: RoundedRectangleBorder( + borderRadius: borderRadius ?? BorderRadius.circular(8), + ), + padding: const EdgeInsets.all(0), + ), + child: backgroundBlur != null + ? Blur( + blur: backgroundBlur!, + overlay: content, + colorOpacity: 0.2, + blurColor: Colors.black, + child: Container(), + ) + : content); + + return SizedBox( + width: width, + height: height, + child: labelType == RPMLButtonLabelType.tooltip + ? Tooltip( + message: label, + child: button, + ) + : button, + ); + } +} + +enum RPMLButtonLabelType { text, tooltip } diff --git a/lib/ui/widget/rpml_dialog.dart b/lib/ui/widget/rpml_dialog.dart new file mode 100644 index 000000000..5a3bbaabe --- /dev/null +++ b/lib/ui/widget/rpml_dialog.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/i18n/i18n.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; +import 'package:rpmlauncher/util/launcher_info.dart'; + +class RPMLDialog extends StatelessWidget { + final String title; + final Widget icon; + final Widget child; + final List actions; + final EdgeInsets? insetPadding; + + const RPMLDialog( + {super.key, + required this.title, + required this.icon, + required this.child, + this.actions = const [], + this.insetPadding}); + + @override + Widget build(BuildContext context) { + const iconSize = 45.0; + + return Dialog( + insetPadding: insetPadding ?? + EdgeInsets.symmetric( + vertical: MediaQuery.of(context).size.height / 6, + horizontal: MediaQuery.of(context).size.width / 4.3), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + clipBehavior: Clip.hardEdge, + child: Container( + decoration: BoxDecoration( + color: context.theme.dialogBackgroundColor, + border: Border.all(color: context.theme.backgroundColor, width: 2)), + child: Column( + children: [ + ClipRRect( + borderRadius: + const BorderRadius.vertical(bottom: Radius.circular(20)), + child: Container( + color: context.theme.backgroundColor, + constraints: const BoxConstraints(minHeight: 75), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Row( + children: [ + SizedBox( + height: iconSize, + child: IconTheme.merge( + data: const IconThemeData(size: iconSize), + child: icon)), + const SizedBox(width: 20), + Text(title, style: const TextStyle(fontSize: 18)), + ], + ), + ), + Row( + children: [ + ...actions, + IconButton( + onPressed: () { + Navigator.of(context) + .pushNamed(LauncherInfo.route); + }, + tooltip: I18n.format('gui.close'), + icon: const Icon(Icons.close_rounded, size: 30)), + const SizedBox(width: 12), + ], + ) + ], + ), + ), + ), + Expanded(child: child), + ], + ), + ), + ); + } +} diff --git a/lib/widget/RPMNetworkImage.dart b/lib/ui/widget/rpml_network_image.dart similarity index 94% rename from lib/widget/RPMNetworkImage.dart rename to lib/ui/widget/rpml_network_image.dart index 263d606e0..203222dfc 100644 --- a/lib/widget/RPMNetworkImage.dart +++ b/lib/ui/widget/rpml_network_image.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; -class RPMNetworkImage extends StatelessWidget { +class RPMLNetworkImage extends StatelessWidget { final String src; final BoxFit? fit; final double? width; final double? height; final Widget errorWidget; - const RPMNetworkImage( + const RPMLNetworkImage( {Key? key, required this.src, this.fit, @@ -47,4 +47,4 @@ class RPMNetworkImage extends StatelessWidget { }), ); } -} \ No newline at end of file +} diff --git a/lib/ui/widget/rpml_tool_bar.dart b/lib/ui/widget/rpml_tool_bar.dart new file mode 100644 index 000000000..47544f5a4 --- /dev/null +++ b/lib/ui/widget/rpml_tool_bar.dart @@ -0,0 +1,83 @@ +import 'package:blur/blur.dart'; +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; + +class RPMLToolBar extends StatelessWidget { + final String? label; + final VoidCallback? onPressed; + final Widget icon; + final List actions; + const RPMLToolBar( + {super.key, + this.label, + this.onPressed, + this.icon = const Icon(Icons.undo_rounded), + this.actions = const []}); + + @override + Widget build(BuildContext context) { + return Container( + constraints: const BoxConstraints(maxHeight: 65), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: context.theme.mainColor.withOpacity(0.3), + blurRadius: 50, + blurStyle: BlurStyle.outer, + ) + ], + borderRadius: BorderRadius.circular(13), + border: Border.all(width: 2, color: context.theme.primaryColor)), + child: Blur( + blur: 15, + blurColor: context.theme.mainColor, + colorOpacity: 0.3, + borderRadius: BorderRadius.circular(10), + overlay: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + const SizedBox(width: 10), + Wrap( + spacing: 12, + children: actions, + ) + ], + ), + Row( + children: [ + SizedBox( + height: 65, + child: ElevatedButton.icon( + label: Text(label ?? '返回首頁', + style: TextStyle( + color: context.theme.textColor, fontSize: 18)), + icon: IconTheme( + data: IconThemeData( + color: context.theme.textColor, size: 30), + child: icon, + ), + style: ElevatedButton.styleFrom( + backgroundColor: context.theme.primaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + onPressed: () { + if (onPressed != null) { + onPressed!(); + } else { + Navigator.popUntil( + context, (route) => route.isFirst); + } + }), + ), + ], + ) + ], + ), + child: Container()), + ); + } +} diff --git a/lib/ui/widget/rpmtw_design/background.dart b/lib/ui/widget/rpmtw_design/background.dart new file mode 100644 index 000000000..acb94d5d5 --- /dev/null +++ b/lib/ui/widget/rpmtw_design/background.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:rpmlauncher/config/config.dart'; + +class Background extends StatefulWidget { + const Background({super.key}); + + @override + State createState() => _BackgroundState(); +} + +class _BackgroundState extends State { + ImageProvider image = const AssetImage( + 'assets/images/background.png', + ); + + @override + void initState() { + if (launcherConfig.backgroundImageFile != null) { + try { + image = FileImage(launcherConfig.backgroundImageFile!); + } catch (_) {} + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + constraints: const BoxConstraints.expand(), + child: Image(image: image, fit: BoxFit.cover)); + } +} diff --git a/lib/widget/rpmtw_design/DynamicImageFile.dart b/lib/ui/widget/rpmtw_design/dynamic_image_file.dart similarity index 100% rename from lib/widget/rpmtw_design/DynamicImageFile.dart rename to lib/ui/widget/rpmtw_design/dynamic_image_file.dart diff --git a/lib/widget/rpmtw_design/LinkText.dart b/lib/ui/widget/rpmtw_design/link_text.dart similarity index 100% rename from lib/widget/rpmtw_design/LinkText.dart rename to lib/ui/widget/rpmtw_design/link_text.dart diff --git a/lib/widget/rpmtw_design/OkClose.dart b/lib/ui/widget/rpmtw_design/on_close.dart similarity index 100% rename from lib/widget/rpmtw_design/OkClose.dart rename to lib/ui/widget/rpmtw_design/on_close.dart diff --git a/lib/widget/rpmtw_design/rpml_text_field.dart b/lib/ui/widget/rpmtw_design/rpml_text_field.dart similarity index 63% rename from lib/widget/rpmtw_design/rpml_text_field.dart rename to lib/ui/widget/rpmtw_design/rpml_text_field.dart index f82a04f1a..966bf6c9c 100644 --- a/lib/widget/rpmtw_design/rpml_text_field.dart +++ b/lib/ui/widget/rpmtw_design/rpml_text_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:rpmlauncher/ui/theme/launcher_theme.dart'; class RPMLTextField extends StatefulWidget { final TextEditingController? controller; @@ -8,8 +9,10 @@ class RPMLTextField extends StatefulWidget { final TextAlign textAlign; final TextInputType? keyboardType; final VoidCallback? onEditingComplete; + final BorderRadius borderRadius; const RPMLTextField({ + super.key, this.controller, this.onChanged, this.verify, @@ -17,6 +20,7 @@ class RPMLTextField extends StatefulWidget { this.textAlign = TextAlign.center, this.keyboardType, this.onEditingComplete, + this.borderRadius = const BorderRadius.all(Radius.circular(15)), }); @override @@ -28,12 +32,26 @@ class _RPMLTextFieldState extends State { @override Widget build(BuildContext context) { + final border = OutlineInputBorder( + borderRadius: widget.borderRadius, + borderSide: const BorderSide(color: Colors.transparent)); + return TextField( controller: widget.controller, decoration: InputDecoration( hintText: widget.hintText, - border: const OutlineInputBorder(), + border: border, + enabledBorder: border, + focusedBorder: border, + errorBorder: border.copyWith( + borderSide: const BorderSide(color: Colors.red, width: 2)), + focusedErrorBorder: border.copyWith( + borderSide: const BorderSide(color: Colors.red, width: 2)), + disabledBorder: border, errorText: color == null ? null : '', + filled: true, + fillColor: context.theme.backgroundColor, + hoverColor: context.theme.backgroundColor, errorStyle: const TextStyle(fontSize: 0)), textAlign: widget.textAlign, keyboardType: widget.keyboardType, diff --git a/lib/util/Process.dart b/lib/util/Process.dart deleted file mode 100644 index 68a1611df..000000000 --- a/lib/util/Process.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'dart:io'; - -List _getChmodArgs(String file, mode) { - if (mode is num) { - mode = mode.toString(); - } - if (mode is String) { - if (mode.startsWith('0o')) { - mode = mode.substring(2); - } - return [mode, file]; - } - return []; -} - -Future chmod(String file, {String mode = '0o777'}) async { - if (Platform.isLinux || Platform.isMacOS) { - List args = _getChmodArgs(file, mode); - return await Process.run('chmod', args); - } - return null; -} - -Future xdgOpen(String uri) async { - if (Platform.isLinux) { - return await Process.run('xdg-open', [uri]); - } - return null; -} diff --git a/lib/util/RPMHttpClient.dart b/lib/util/RPMHttpClient.dart deleted file mode 100644 index 3b76ac0d7..000000000 --- a/lib/util/RPMHttpClient.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'dart:async'; -import 'dart:convert' as convert; - -import 'package:dio/adapter.dart'; -import 'package:dio/dio.dart'; -import 'package:dio/native_imp.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; - -Future>? Function(RequestOptions requestOptions)? - rpmHttpClientAdapter; - -class RPMHttpClient extends DioForNative { - RPMHttpClient({BaseOptions? baseOptions}) { - options = baseOptions ?? BaseOptions(validateStatus: (state) => true); - httpClientAdapter = DefaultHttpClientAdapter(); - } - - @override - Future> fetch(RequestOptions requestOptions) async { - bool exceptionThrown = false; - Response? response; - final stopwatch = Stopwatch(); - stopwatch.start(); - - try { - response = await rpmHttpClientAdapter?.call(requestOptions) ?? - await super.fetch(requestOptions); - } catch (e) { - exceptionThrown = true; - rethrow; - } finally { - stopwatch.stop(); - - Breadcrumb breadcrumb = Breadcrumb.http( - level: exceptionThrown ? SentryLevel.error : SentryLevel.info, - url: requestOptions.uri, - method: requestOptions.method, - statusCode: response?.statusCode, - requestDuration: stopwatch.elapsed, - reason: response?.statusMessage, - timestamp: DateTime.now(), - ); - - Sentry.addBreadcrumb(breadcrumb); - } - - return response; - } - - static dynamic json(dynamic source) { - if (source is Response) { - return json(source.data); - } else if (source is Map || source is List) { - return source; - } else { - return convert.json.decode(source); - } - } -} diff --git a/lib/util/data.dart b/lib/util/data.dart index b38062017..8d2533310 100644 --- a/lib/util/data.dart +++ b/lib/util/data.dart @@ -3,10 +3,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:flutter/material.dart'; import 'package:no_context_navigation/no_context_navigation.dart'; -// ignore: implementation_imports -import 'package:provider/src/provider.dart'; import 'package:rpmlauncher/function/analytics.dart'; -import 'package:rpmlauncher/function/counter.dart'; import 'package:rpmlauncher/handler/window_handler.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/util/logger.dart'; @@ -17,13 +14,7 @@ Analytics? googleAnalytics; final NavigatorState navigator = NavigationService.navigationKey.currentState!; final Logger logger = Logger.current; List launcherArgs = []; -Directory get dataHome { - try { - return navigator.context.read().dataHome; - } catch (e) { - return LauncherPath.currentDataHome; - } -} +Directory get dataHome => LauncherPath.currentDataHome; class Data { static Future argsInit() async { @@ -36,6 +27,6 @@ class Data { await WindowHandler.parseArguments(launcherArgs); try { parser.parse(launcherArgs); - } catch (e) {} + } catch (_) {} } } diff --git a/lib/util/Intents.dart b/lib/util/intents.dart similarity index 100% rename from lib/util/Intents.dart rename to lib/util/intents.dart diff --git a/lib/util/io_util.dart b/lib/util/io_util.dart new file mode 100644 index 000000000..6c2ad3a68 --- /dev/null +++ b/lib/util/io_util.dart @@ -0,0 +1,57 @@ +import 'dart:io'; + +import 'package:crypto/crypto.dart'; +import 'package:path/path.dart'; +import 'package:rpmlauncher/util/util.dart'; + +class IOUtil { + static Future openFileManager(FileSystemEntity fse) async { + if (fse is Directory) { + createDirectory(fse); + } + + if (Platform.isMacOS) { + await Process.run('open', [fse.absolute.path]); + } else { + await Util.openUri(Uri.decodeFull(fse.uri.toString())); + } + } + + static void createDirectory(Directory dir) { + if (!dir.existsSync()) { + dir.createSync(recursive: true); + } + } + + /// Replace invalid characters in the folder name. + static String replaceFolderName(String name) { + /// Windows: \/:*?"<>| + /// Unix: / + final regexp = RegExp(r'[\\/:*?"<>|]'); + + if (name.isEmpty) { + return '_'; + } + + return name.replaceAll(regexp, '_').trimRight(); + } + + static Future copyDirectory( + Directory source, Directory destination) async { + await source.list(recursive: false).forEach((FileSystemEntity entity) { + if (entity is Directory) { + var newDirectory = + Directory(join(destination.absolute.path, basename(entity.path))); + newDirectory.createSync(recursive: true); + copyDirectory(entity.absolute, newDirectory); + } else if (entity is File) { + entity.copySync(join(destination.path, basename(entity.path))); + } + }); + } + + static bool isCachedFileSha1(File file, String sha1Hash) { + return file.existsSync() && + sha1.convert(file.readAsBytesSync()).toString() == sha1Hash; + } +} diff --git a/lib/util/launcher_info.dart b/lib/util/launcher_info.dart index 747804be4..4d58ef8bd 100644 --- a/lib/util/launcher_info.dart +++ b/lib/util/launcher_info.dart @@ -3,84 +3,85 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; +import 'package:rpmlauncher/ui/pages/home_page.dart'; import 'package:rpmlauncher/util/updater.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; bool kTestMode = false; class LauncherInfo { - static const String homePageUrl = "https://www.rpmtw.com"; - static const String githubRepoUrl = "https://github.com/RPMTW/RPMLauncher"; - static const String discordUrl = "https://discord.gg/5xApZtgV2u"; + static const String homePageUrl = 'https://www.rpmtw.com'; + static const String githubRepoUrl = 'https://github.com/RPMTW/RPMLauncher'; + static const String discordUrl = 'https://discord.gg/5xApZtgV2u'; static const String microsoftClientID = - "b7df55b4-300f-4409-8ea9-a172f844aa15"; + 'b7df55b4-300f-4409-8ea9-a172f844aa15'; static bool get isSnapcraftApp => - const bool.fromEnvironment('sanp', defaultValue: false); + const bool.fromEnvironment('snap', defaultValue: false); static bool isFlatpakApp = false; - static String route = "/"; + static String route = HomePage.route; static String getVersion() { - return const String.fromEnvironment('version', defaultValue: '1.1.0'); + return const String.fromEnvironment('version', defaultValue: '2.0.0'); } static String get userOrigin { if (isSnapcraftApp) { - return "snapcraft"; + return 'snapcraft'; } else if (isFlatpakApp) { - return "flatpak (flathub)"; + return 'flatpak (flathub)'; } else if (Platform.isWindows) { - return "windows installer"; + return 'windows installer'; } else if (Platform.isMacOS) { - return "dmg installer"; + return 'dmg installer'; } else { - return "binary file"; + return 'binary file'; } } static Version get version => Version.parse(getFullVersion()); static String getFullVersion() { - return "${getVersion()}+${getBuildID()}"; + return '${getVersion()}+${getBuildID()}'; } static String getLowercaseName() { - return "rpmlauncher"; + return 'rpmlauncher'; } static String getUpperCaseName() { - return "RPMLauncher"; + return 'RPMLauncher'; } static String getAbbreviationsName() { - return "RWL"; + return 'RWL'; } static VersionTypes getVersionType() { String type = - const String.fromEnvironment('version_type', defaultValue: "debug"); + const String.fromEnvironment('version_type', defaultValue: 'debug'); return VersionTypes.values.byName(type); } static Text getVersionTypeText() { String type = - const String.fromEnvironment('version_type', defaultValue: "debug"); + const String.fromEnvironment('version_type', defaultValue: 'debug'); - if (type == "stable") { - return Text(I18n.format("settings.advanced.channel.stable"), + if (type == 'stable') { + return Text(I18n.format('settings.advanced.channel.stable'), style: const TextStyle( color: Colors.lightGreen, ), textAlign: TextAlign.center); - } else if (type == "dev") { - return Text(I18n.format("settings.advanced.channel.dev"), + } else if (type == 'dev') { + return Text(I18n.format('settings.advanced.channel.dev'), style: const TextStyle(color: Colors.lightBlue, fontSize: 20), textAlign: TextAlign.center); - } else if (type == "debug") { - return Text(I18n.format("settings.advanced.channel.debug"), + } else if (type == 'debug') { + return Text(I18n.format('settings.advanced.channel.debug'), style: const TextStyle(color: Colors.red, fontSize: 20), textAlign: TextAlign.center); } else { @@ -102,14 +103,14 @@ class LauncherInfo { late String exe; if (Platform.isWindows) { - exe = "rpmlauncher.exe"; + exe = 'rpmlauncher.exe'; } else if (Platform.isMacOS) { - exe = "rpmlauncher"; + exe = 'rpmlauncher'; } else if (Platform.isLinux) { if (LauncherInfo.isSnapcraftApp) { - return File(absolute("/snap/rpmlauncher/current/bin/RPMLauncher")); + return File(absolute('/snap/rpmlauncher/current/bin/RPMLauncher')); } - exe = "RPMLauncher"; + exe = 'RPMLauncher'; } return File(join(getRunningDirectory().path, exe)); diff --git a/lib/util/launcher_path.dart b/lib/util/launcher_path.dart index 47eae2d2b..df1ac6586 100644 --- a/lib/util/launcher_path.dart +++ b/lib/util/launcher_path.dart @@ -3,8 +3,8 @@ import 'dart:io'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; import 'package:rpmlauncher/config/config.dart'; +import 'package:rpmlauncher/util/io_util.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/util/util.dart'; import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; @@ -86,9 +86,8 @@ class LauncherPath { _root = Directory(join(base, 'RPMLauncher', 'data')); } - Util.createFolderOptimization(_root); - GameRepository.init(_root); - Util.createFolderOptimization(currentDataHome); + IOUtil.createDirectory(_root); + IOUtil.createDirectory(currentDataHome); } static void setCustomDataHome(Directory home, Directory defaultHome) { diff --git a/lib/util/logger.dart b/lib/util/logger.dart index 207fd92f6..bffd7f905 100644 --- a/lib/util/logger.dart +++ b/lib/util/logger.dart @@ -8,21 +8,7 @@ import 'package:rpmlauncher/util/data.dart'; import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -enum ErrorType { - unknown, - ui, - dart, - flutter, - io, - network, - download, - instance, - data, - parseModInfo, - authorization, - modpack, - config -} +enum ErrorType { unknown, flutter, io, network, task, authorization, config } class Logger { final File _logFile; diff --git a/lib/util/process_util.dart b/lib/util/process_util.dart new file mode 100644 index 000000000..b819433a8 --- /dev/null +++ b/lib/util/process_util.dart @@ -0,0 +1,32 @@ +import 'dart:io'; + +class ProcessUtil { + static List _getChmodArgs(String file, mode) { + if (mode is num) { + mode = mode.toString(); + } + if (mode is String) { + if (mode.startsWith('0o')) { + mode = mode.substring(2); + } + return [mode, file]; + } + return []; + } + + static Future chmod(String file, + {String mode = '0o777'}) async { + if (Platform.isLinux || Platform.isMacOS) { + List args = _getChmodArgs(file, mode); + return await Process.run('chmod', args); + } + return null; + } + + static Future xdgOpen(String uri) async { + if (Platform.isLinux) { + return await Process.run('xdg-open', [uri]); + } + return null; + } +} diff --git a/lib/util/rpml_http_client.dart b/lib/util/rpml_http_client.dart new file mode 100644 index 000000000..2d1f83403 --- /dev/null +++ b/lib/util/rpml_http_client.dart @@ -0,0 +1,19 @@ +import 'package:dio/dio.dart'; +import 'package:dio/io.dart'; +import 'package:dio_smart_retry/dio_smart_retry.dart'; +import 'package:sentry_dio/sentry_dio.dart'; + +HttpClientAdapter? rpmlHttpClientAdapter; +final httpClient = + RPMLHttpClient(BaseOptions(validateStatus: (status) => true)); + +class RPMLHttpClient extends DioForNative { + RPMLHttpClient([super.baseOptions]) { + addSentry(); + httpClientAdapter = rpmlHttpClientAdapter ?? httpClientAdapter; + interceptors.add(RetryInterceptor( + dio: this, + logPrint: print, + )); + } +} diff --git a/lib/util/theme.dart b/lib/util/theme.dart deleted file mode 100644 index fe8768cc2..000000000 --- a/lib/util/theme.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'dart:ui'; - -import 'package:dynamic_themes/dynamic_themes.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; - -enum LauncherTheme { dark, light } - -class ThemeUtil { - static String toI18nString(LauncherTheme theme) { - switch (theme) { - case LauncherTheme.light: - return I18n.format('settings.appearance.theme.light'); - case LauncherTheme.dark: - return I18n.format('settings.appearance.theme.dark'); - } - } - - static int toInt(LauncherTheme theme) { - switch (theme) { - case LauncherTheme.light: - return 0; - case LauncherTheme.dark: - return 1; - } - } - - static LauncherTheme getThemeByID(int id) { - switch (id) { - case 0: - return LauncherTheme.light; - case 1: - return LauncherTheme.dark; - default: - return LauncherTheme.light; - } - } - - static ThemeData getTheme([BuildContext? context]) { - return Theme.of(context ?? navigator.context); - } - - static LauncherTheme getThemeByConfig() { - return ThemeUtil.getThemeByID(launcherConfig.themeId); - } - - static ThemeCollection themeCollection() { - return ThemeCollection(themes: { - ThemeUtil.toInt(LauncherTheme.light): ThemeData( - colorSchemeSeed: Colors.indigo, - fontFamily: 'font', - tooltipTheme: const TooltipThemeData( - textStyle: TextStyle(fontFamily: 'font', color: Colors.white), - waitDuration: Duration(milliseconds: 250), - ), - textTheme: const TextTheme( - bodyLarge: TextStyle( - fontFamily: 'font', - fontFeatures: [FontFeature.tabularFigures()], - ), - ), - useMaterial3: true), - ThemeUtil.toInt(LauncherTheme.dark): ThemeData( - colorSchemeSeed: Colors.indigo, - brightness: Brightness.dark, - fontFamily: 'font', - tooltipTheme: const TooltipThemeData( - textStyle: TextStyle(fontFamily: 'font', color: Colors.black), - waitDuration: Duration(milliseconds: 250), - ), - textTheme: const TextTheme( - bodyLarge: TextStyle( - fontFamily: 'font', - fontFeatures: [FontFeature.tabularFigures()], - )), - useMaterial3: true), - }); - } - - static int getSystem() { - final brightness = WidgetsBinding.instance.window.platformBrightness; - - switch (brightness) { - case Brightness.light: - return toInt(LauncherTheme.light); - - case Brightness.dark: - return toInt(LauncherTheme.dark); - } - } -} - -class DynamicThemeBuilder extends StatelessWidget { - final Widget Function(BuildContext context, ThemeData themeData) builder; - - const DynamicThemeBuilder({Key? key, required this.builder}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return DynamicTheme( - themeCollection: ThemeUtil.themeCollection(), - defaultThemeId: ThemeUtil.toInt(LauncherTheme.dark), - builder: builder); - } -} diff --git a/lib/util/updater.dart b/lib/util/updater.dart index e858bfc1c..0670c3b96 100644 --- a/lib/util/updater.dart +++ b/lib/util/updater.dart @@ -3,16 +3,16 @@ import 'dart:io'; import 'package:archive/archive.dart'; import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:rpmlauncher/config/config.dart'; +import 'package:rpmlauncher/util/io_util.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/i18n/i18n.dart'; import 'package:rpmlauncher/util/util.dart'; import 'package:rpmlauncher/util/data.dart'; -import 'RPMHttpClient.dart'; +import 'rpml_http_client.dart'; enum VersionTypes { stable, dev, debug } @@ -52,15 +52,15 @@ class Updater { } static Future checkForUpdate(VersionTypes channel) async { - http.Response response = await http.get(Uri.parse(_updateUrl)); - Map data = json.decode(response.body); + final response = await httpClient.get(_updateUrl); + final data = json.decode(response.data); Map versionList = data['version_list']; VersionInfo getVersionInfo(Map data) { - String latestVersion = data['latest_version'] ?? "1.1.0"; + String latestVersion = data['latest_version'] ?? "2.0.0"; String latestBuildID = data['latest_build_id'] ?? "0"; return VersionInfo.fromJson( - versionList[data['latest_version_full'] ?? "1.1.0+0"], + versionList[data['latest_version_full'] ?? "2.0.0+0"], latestBuildID, latestVersion, versionList.cast(), @@ -109,7 +109,7 @@ class Updater { } Future downloading() async { - await RPMHttpClient().download( + await RPMLHttpClient().download( downloadUrl, updateFile.absolute.path, onReceiveProgress: (count, total) { @@ -158,7 +158,7 @@ class Updater { case "linux": LauncherInfo.getRunningDirectory().deleteSync(recursive: true); - await Util.copyDirectory( + await IOUtil.copyDirectory( Directory(join( updateDir.absolute.path, "unziped", "RPMLauncher-Linux")), LauncherInfo.getRunningDirectory()); diff --git a/lib/util/util.dart b/lib/util/util.dart index f3abe20ac..12af95bbd 100644 --- a/lib/util/util.dart +++ b/lib/util/util.dart @@ -4,24 +4,17 @@ import 'dart:io' as io show exit; import 'dart:typed_data'; import 'package:archive/archive.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:oauth2/oauth2.dart'; import 'package:path/path.dart'; -import 'package:pub_semver/pub_semver.dart'; import 'package:rpmlauncher/account/microsoft_account_handler.dart'; -import 'package:rpmlauncher/account/mojang_account_handler.dart'; import 'package:rpmlauncher/database/data_box.dart'; -import 'package:rpmlauncher/model/account/Account.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftVersion.dart'; -import 'package:rpmlauncher/model/IO/Properties.dart'; +import 'package:rpmlauncher/model/io/properties.dart'; +import 'package:rpmlauncher/model/account/account.dart'; +import 'package:rpmlauncher/util/process_util.dart'; +import 'package:rpmlauncher/util/data.dart'; import 'package:rpmlauncher/util/launcher_info.dart'; import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/Process.dart'; -import 'package:rpmlauncher/widget/dialog/download_java.dart'; -import 'package:rpmlauncher/util/data.dart'; import 'package:rpmtw_dart_common_library/rpmtw_dart_common_library.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -29,25 +22,7 @@ import '../config/config.dart'; import '../i18n/i18n.dart'; class Util { - static openFileManager(FileSystemEntity fse) async { - if (fse is Directory) { - createFolderOptimization(fse); - } - - if (Platform.isMacOS) { - Process.run('open', [fse.absolute.path]); - } else { - openUri(Uri.decodeFull(fse.uri.toString())); - } - } - - static createFolderOptimization(Directory dir) { - if (!dir.existsSync()) { - dir.createSync(recursive: true); - } - } - - static String? getMinecraftFormatOS() { + static String getMinecraftFormatOS() { if (Platform.isWindows) { return 'windows'; } else if (Platform.isLinux) { @@ -55,7 +30,8 @@ class Util { } else if (Platform.isMacOS) { return 'osx'; } - return null; + + throw UnsupportedError('Unsupported platform'); } static String getLibrarySeparator() { @@ -66,76 +42,6 @@ class Util { } } - static Map parseLibMaven(Map lib, {String? baseUrl}) { - baseUrl ??= lib['url']; - String name = lib['name']; - Map result = {}; - String packageName = name.split(':')[0]; - String split_1 = name.split('$packageName:').join(''); - String fileVersion = split_1.split(':')[split_1.split(':').length - 1]; - String filename = split_1.replaceAll(':', '-'); - String split_2 = filename.split(fileVersion)[0]; - String path = ''; - if (packageName.contains('.')) { - path += '${packageName.replaceAll('.', '/')}/'; - } - - if (split_2.length > 1) { - path += '${split_2.substring(0, split_2.length - 1)}/'; - } - - path += '$fileVersion/$filename'; - - String url = '$baseUrl$path.jar'; - - result['Filename'] = '$filename.jar'; - result['Url'] = url; - - result['Path'] = '$path.jar'; - return result; - } - - static String pathSeparator(src) { - return src.replaceAll('/', Platform.pathSeparator); - } - - static Future openJavaSelectScreen(BuildContext context) async { - final FilePickerResult? result = await FilePicker.platform.pickFiles( - type: FileType.any, - dialogTitle: I18n.format('launcher.java.install.manual.file')); - if (result == null) { - return [false, null]; - } - - PlatformFile file = result.files.single; - List javaFileList = ['java', 'javaw', 'java.exe', 'javaw.exe']; - if (javaFileList.any((element) => element == file.name)) { - return [true, file.path]; - } else { - if (context.mounted) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: - I18nText('launcher.java.install.manual.file.error.title'), - content: - I18nText('auncher.java.install.manual.file.error.message'), - actions: [ - TextButton( - child: Text(I18n.format('gui.confirm')), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }); - } - - return [false, null]; - } - } static int getMurmur2Hash(File file) { /* @@ -265,25 +171,11 @@ class Util { return mainClass; } - static Future copyDirectory( - Directory source, Directory destination) async { - await source.list(recursive: false).forEach((FileSystemEntity entity) { - if (entity is Directory) { - var newDirectory = - Directory(join(destination.absolute.path, basename(entity.path))); - newDirectory.createSync(recursive: true); - copyDirectory(entity.absolute, newDirectory); - } else if (entity is File) { - entity.copySync(join(destination.path, basename(entity.path))); - } - }); - } - static Future openUri(String url) async { if (kTestMode) return; if (Platform.isLinux) { - xdgOpen(url); + await ProcessUtil.xdgOpen(url); } else { await launchUrlString(url).catchError((e) { logger.error(ErrorType.io, 'Can\'t open the url $url'); @@ -316,49 +208,7 @@ class Util { return isValid; } else { - return await MojangHandler.validate(account.accessToken); - } - } - - static Future getVanillaVersionMeta(String versionID) async { - List versionList = - (await MCVersionManifest.getVanilla()).versions; - return versionList.firstWhere((version) => version.id == versionID).meta; - } - - static List javaCheck(List allJavaVersions) { - List needVersions = []; - for (var version in allJavaVersions) { - final javaPath = ConfigHelper.get('java_path_$version'); - - /// 假設Java路徑無效或者不存在 - if (javaPath == null || javaPath == '' || !File(javaPath).existsSync()) { - needVersions.add(version); - } - } - - return needVersions; - } - - static void javaCheckDialog( - {Function? notHasJava, Function? hasJava, List? allJavaVersions}) { - allJavaVersions ??= [8, 16, 17]; - List needVersions = javaCheck(allJavaVersions); - if (needVersions.isNotEmpty) { - if (notHasJava == null) { - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - showDialog( - context: navigator.context, - builder: (context) => DownloadJava( - javaVersions: needVersions, - onDownloaded: hasJava, - )); - }); - } else { - notHasJava.call(); - } - } else { - hasJava?.call(); + return false; } } @@ -373,119 +223,6 @@ class Util { } } - static Version parseMCComparableVersion(String sourceVersion) { - Version comparableVersion; - try { - try { - comparableVersion = Version.parse(sourceVersion); - } catch (e) { - comparableVersion = Version.parse('$sourceVersion.0'); - } - } catch (e) { - String? preVersion() { - int pos = sourceVersion.indexOf('-pre'); - if (pos >= 0) return sourceVersion.substring(0, pos); - - pos = sourceVersion.indexOf(' Pre-release '); - if (pos >= 0) return sourceVersion.substring(0, pos); - - pos = sourceVersion.indexOf(' Pre-Release '); - if (pos >= 0) return sourceVersion.substring(0, pos); - - pos = sourceVersion.indexOf(' Release Candidate '); - if (pos >= 0) return sourceVersion.substring(0, pos); - return null; - } - - String? str = preVersion(); - if (str != null) { - try { - return Version.parse(str); - } catch (e) { - return Version.parse('$str.0'); - } - } - - /// Handling snapshot version (e.g. 21w44a) - RegExp snapshotPattern = RegExp(r'(?:(?\d\d)w(?\d\d)[a-z])'); - if (snapshotPattern.hasMatch(sourceVersion)) { - RegExpMatch match = - snapshotPattern.allMatches(sourceVersion).toList().first; - - String praseRelease(int year, int week) { - if (year == 22 && week == 24) { - return '1.19.1'; - } else if (year == 22 && week >= 11 && week <= 19) { - return '1.19.0'; - } else if (year == 22 && week >= 3 && week <= 7) { - return '1.18.2'; - } else if (year == 21 && week >= 37) { - return '1.18.0'; - } else if (year == 21 && (week >= 3 && week <= 20)) { - return '1.17.0'; - } else if (year == 20 && week >= 6) { - return '1.16.0'; - } else if (year == 19 && week >= 34) { - return '1.15.2'; - } else if (year == 18 && week >= 43 || year == 19 && week <= 14) { - return '1.14.0'; - } else if (year == 18 && week >= 30 && week <= 33) { - return '1.13.1'; - } else if (year == 17 && week >= 43 || year == 18 && week <= 22) { - return '1.13.0'; - } else if (year == 17 && week == 31) { - return '1.12.1'; - } else if (year == 17 && week >= 6 && week <= 18) { - return '1.12.0'; - } else if (year == 16 && week == 50) { - return '1.11.1'; - } else if (year == 16 && week >= 32 && week <= 44) { - return '1.11.0'; - } else if (year == 16 && week >= 20 && week <= 21) { - return '1.10.0'; - } else if (year == 16 && week >= 14 && week <= 15) { - return '1.9.3'; - } else if (year == 15 && week >= 31 || year == 16 && week <= 7) { - return '1.9.0'; - } else if (year == 14 && week >= 2 && week <= 34) { - return '1.8.0'; - } else if (year == 13 && week >= 47 && week <= 49) { - return '1.7.4'; - } else if (year == 13 && week >= 36 && week <= 43) { - return '1.7.2'; - } else if (year == 13 && week >= 16 && week <= 26) { - return '1.6.0'; - } else if (year == 13 && week >= 11 && week <= 12) { - return '1.5.1'; - } else if (year == 13 && week >= 1 && week <= 10) { - return '1.5.0'; - } else if (year == 12 && week >= 49 && week <= 50) { - return '1.4.6'; - } else if (year == 12 && week >= 32 && week <= 42) { - return '1.4.2'; - } else if (year == 12 && week >= 15 && week <= 30) { - return '1.3.1'; - } else if (year == 12 && week >= 3 && week <= 8) { - return '1.2.1'; - } else if (year == 11 && week >= 47 || year == 12 && week <= 1) { - return '1.1.0'; - } else { - return '1.19.0'; - } - } - - int year = int.parse(match.group(1).toString()); //ex: 21 - int week = int.parse(match.group(2).toString()); //ex: 44 - - comparableVersion = Version.parse(praseRelease(year, week)); - } else { - comparableVersion = Version.none; - } - } - - return comparableVersion; - } - static Future hasNetWork() async { try { final result = await InternetAddress.lookup('www.google.com'); @@ -544,14 +281,6 @@ class Util { } } - static Future unZip(File file) async { - try { - return ZipDecoder().decodeBytes(await (file.readAsBytes())); - } catch (e) { - return null; - } - } - static String getCPUArchitecture() { if (Platform.isWindows) { return Platform.environment['PROCESSOR_ARCHITECTURE'] ?? 'AMD64'; diff --git a/lib/view/Edit/WorldView.dart b/lib/view/Edit/WorldView.dart deleted file mode 100644 index 6d3d1cdb6..000000000 --- a/lib/view/Edit/WorldView.dart +++ /dev/null @@ -1,323 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:archive/archive.dart'; -import 'package:dart_minecraft/dart_nbt.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/view/OptionsView.dart'; -import 'package:rpmlauncher/widget/DeleteFileWidget.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; - -class WorldView extends StatefulWidget { - final Directory worldRootDir; - - const WorldView({Key? key, required this.worldRootDir}) : super(key: key); - - @override - State createState() => _WorldViewState(); -} - -class _WorldViewState extends State { - late ScrollController _scrollController; - - Future> getWorldList() async { - List worldList = []; - widget.worldRootDir.listSync().toList().forEach((dir) { - //過濾不是世界的資料夾 - if (dir is Directory && - Directory(dir.path) - .listSync() - .toList() - .any((file) => file.path.contains("level.dat"))) { - worldList.add(dir); - } - }); - return worldList; - } - - late StreamSubscription worldDirEvent; - - @override - void initState() { - _scrollController = ScrollController(); - - super.initState(); - - worldDirEvent = widget.worldRootDir.watch().listen((event) { - if (!widget.worldRootDir.existsSync()) worldDirEvent.cancel(); - setState(() {}); - }); - } - - @override - void dispose() { - worldDirEvent.cancel(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return OptionPage( - mainWidget: FutureBuilder( - future: getWorldList(), - builder: (context, AsyncSnapshot> snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.isEmpty) { - return Center( - child: Text( - I18n.format('edit.instance.world.found'), - style: const TextStyle(fontSize: 30), - )); - } - return ListView.builder( - itemCount: snapshot.data!.length, - controller: _scrollController, - itemBuilder: (context, index) { - late Widget image; - Directory worldDir = snapshot.data![index] as Directory; - try { - if (FileSystemEntity.typeSync( - File(join(worldDir.absolute.path, "icon.png")) - .absolute - .path) != - FileSystemEntityType.notFound) { - image = Image.file( - File(join(worldDir.absolute.path, "icon.png")), - fit: BoxFit.contain); - } else { - image = const Icon(Icons.image, size: 50); - } - } on FileSystemException {} - try { - final nbtReader = NbtReader.fromFile( - join(worldDir.absolute.path, "level.dat")); - NbtCompound nbtData = nbtReader - .read() - .getChildrenByName("Data")[0] as NbtCompound; - - String worldName = - nbtData.getChildrenByName("LevelName")[0].value; - - String? worldVersion; - - try { - worldVersion = (nbtData.getChildrenByName("Version")[0] - as NbtCompound) - .getChildrenByName("Name")[0] - .value; - } catch (e) {} - - int lastPlayed = - nbtData.getChildrenByName("LastPlayed")[0].value; - - return ListTile( - leading: image, - title: Text( - worldName, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 20), - ), - subtitle: Text( - "${I18n.format("game.version")}: $worldVersion", - textAlign: TextAlign.center), - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text( - I18n.format("edit.instance.world.info"), - textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "${I18n.format("edit.instance.world.name")}: $worldName"), - Text( - "${I18n.format("game.version")}: $worldVersion"), - Text( - "${I18n.format("edit.instance.world.time")}: ${DateFormat.yMMMMEEEEd(Platform.localeName).add_jms().format(DateTime.fromMillisecondsSinceEpoch(lastPlayed))}") - ], - )); - }); - setState(() {}); - }, - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Icons.folder), - tooltip: - I18n.format('edit.instance.world.folder'), - onPressed: () { - Util.openFileManager(worldDir); - }, - ), - DeleteFileWidget( - tooltip: I18n.format( - 'edit.instance.world.delete.title'), - message: I18n.format( - "edit.instance.world.delete.message"), - onDeleted: () { - setState(() {}); - }, - fileSystemEntity: worldDir, - ), - ], - )); - } on FileSystemException { - return Container(); - } - }, - ); - } else if (snapshot.hasError) { - return Center(child: Text(snapshot.error.toString())); - } else { - return const Center(child: RWLLoading()); - } - }, - ), - actions: [ - IconButton( - icon: const Icon(Icons.add), - onPressed: () async { - final FilePickerResult? result = await FilePicker.platform - .pickFiles( - dialogTitle: I18n.format("edit.instance.world.zip"), - allowedExtensions: ['zip']); - - if (result == null) { - return; - } - - PlatformFile file = result.files.single; - - Future unWorldZip() async { - final File worldZipFile = File(file.path!); - final bytes = worldZipFile.readAsBytesSync(); - final archive = ZipDecoder().decodeBytes(bytes); - bool isParentFolder = archive.files - .any((file) => file.toString().startsWith("level.dat")); - bool isnotParentFolder = archive.files - .any((file) => file.toString().contains("level.dat")); - if (isParentFolder) { - //只有一層資料夾 - final worldDirName = - file.name.split(extension(file.path!)).join(""); - for (final archiveFile in archive) { - final zipFileName = archiveFile.name; - if (archiveFile.isFile) { - await Future.delayed(const Duration(microseconds: 50)); - final data = archiveFile.content as List; - File(join(widget.worldRootDir.absolute.path, worldDirName, - zipFileName)) - ..createSync(recursive: true) - ..writeAsBytesSync(data); - } else { - await Future.delayed(const Duration(microseconds: 50)); - Directory(join(widget.worldRootDir.absolute.path, - worldDirName, zipFileName)) - .create(recursive: true); - } - } - return true; - } else if (isnotParentFolder) { - //有兩層資料夾 - for (final archiveFile in archive) { - final zipFileName = archiveFile.name; - if (archiveFile.isFile) { - await Future.delayed(const Duration(microseconds: 50)); - final data = archiveFile.content as List; - File(join(widget.worldRootDir.absolute.path, zipFileName)) - ..createSync(recursive: true) - ..writeAsBytesSync(data); - } else { - await Future.delayed(const Duration(microseconds: 50)); - Directory(join( - widget.worldRootDir.absolute.path, zipFileName)) - .create(recursive: true); - } - } - return true; - } else { - //錯誤格式 - Navigator.of(context).pop(); - showDialog( - context: context, - builder: (context) { - return AlertDialog( - contentPadding: const EdgeInsets.all(16.0), - title: Text(I18n.format("gui.error.info"), - textAlign: TextAlign.center), - content: Text( - I18n.format('edit.instance.world.add.error'), - textAlign: TextAlign.center), - actions: [ - TextButton( - child: Text(I18n.format("gui.ok")), - onPressed: () { - Navigator.pop(context); - }, - ) - ]); - }); - return false; - } - } - - if (context.mounted) { - showDialog( - barrierDismissible: false, - context: context, - builder: (context) { - return FutureBuilder( - future: unWorldZip(), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData && snapshot.data) { - return AlertDialog( - title: Text(I18n.format("gui.tips.info")), - content: Text(I18n.format('gui.handler.done'), - textAlign: TextAlign.center), - actions: [ - TextButton( - child: Text(I18n.format("gui.ok")), - onPressed: () { - Navigator.pop(context); - }, - ) - ]); - } else { - return AlertDialog( - title: Text(I18n.format("gui.tips.info")), - content: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const RWLLoading(), - const SizedBox(width: 12), - I18nText("edit.instance.world.parseing"), - ], - ), - ); - } - }); - }); - } - }, - tooltip: I18n.format("edit.instance.world.add"), - ), - IconButton( - icon: const Icon(Icons.folder), - onPressed: () { - Util.openFileManager(widget.worldRootDir); - }, - tooltip: I18n.format("edit.instance.world.folder"), - ) - ]); - } -} diff --git a/lib/view/Edit/mods_view.dart b/lib/view/Edit/mods_view.dart deleted file mode 100644 index c748627b8..000000000 --- a/lib/view/Edit/mods_view.dart +++ /dev/null @@ -1,1081 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:isolate'; - -import 'package:archive/archive.dart'; -import 'package:archive/archive_io.dart'; -import 'package:contextmenu/contextmenu.dart'; -import 'package:crypto/crypto.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/database/data_box.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/mod/curseforge/curseforge_handler.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/mod_info.dart'; -import 'package:rpmlauncher/model/IO/isolate_option.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/database/database.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/view/OptionsView.dart'; -import 'package:rpmlauncher/widget/ModSourceSelection.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:rpmtw_api_client/rpmtw_api_client.dart' hide ModLoader; -import 'package:toml/toml.dart'; - -import '../../widget/FileSwitchBox.dart'; -import '../../widget/rwl_loading.dart'; - -class ModsView extends StatefulWidget { - final Instance instance; - - InstanceConfig get instanceConfig => instance.config; - - const ModsView(this.instance); - - @override - State createState() => _ModsViewState(); -} - -class _ModsViewState extends State { - final TextEditingController modSearchController = TextEditingController(); - final DataBox modInfoBox = Database.instance.modInfoBox; - - StateSetter? setModState; - late StreamSubscription modDirEvent; - late List files; - - Directory get modDir => - InstanceRepository.getModRootDir(widget.instance.uuid); - - late Map modInfos; - late Map allModInfos; - List deletedModFiles = []; - - @override - void initState() { - files = widget.instance.getModFiles(); - - super.initState(); - - modDirEvent = modDir.watch().listen((event) { - if (!modDir.existsSync()) modDirEvent.cancel(); - if (event is FileSystemMoveEvent) { - return; - } - files = widget.instance.getModFiles(); - if (deletedModFiles.contains(event.path) && mounted) { - deletedModFiles.remove(event.path); - return; - } else if (mounted) { - try { - setState(() {}); - } catch (e) {} - } - }); - } - - @override - void dispose() { - modDirEvent.cancel(); - modSearchController.dispose(); - super.dispose(); - } - - static ModInfo _getModInfo( - File modFile, int murmur2Hash, String md5Hash, IsolateOption option) { - ModLoader modType = ModLoader.unknown; - try { - final unzipped = ZipDecoder() - .decodeBytes(File(modFile.absolute.path).readAsBytesSync()); - List conflicts = []; - Map modInfoMap = {}; - - ArchiveFile? fabric = unzipped.findFile('fabric.mod.json'); - //Forge Mod Info File (1.13 -> 1.17.1+) - ArchiveFile? forge113 = unzipped.findFile('META-INF/mods.toml'); - //Forge Mod Info File (1.7.10 -> 1.12.2) - ArchiveFile? forge112 = unzipped.findFile('mcmod.info'); - - if (fabric != null) { - modType = ModLoader.fabric; - //Fabric Mod Info File - modInfoMap = json.decode(utf8.decode(fabric.content as List)); - - try { - if (modInfoMap.containsKey("icon")) { - for (var i in unzipped) { - if (i.name == modInfoMap["icon"]) { - GameRepository.getModIconFile(md5Hash) - ..createSync(recursive: true) - ..writeAsBytesSync(i.content as List); - } - } - } - } catch (err) { - logger.error(ErrorType.parseModInfo, "Mod Icon Parsing Error $err"); - } - - void handle(Map map) { - try { - Map conflictsMap = map.cast(); - conflictsMap.forEach((key, value) { - conflicts.add(ConflictMod(namespace: key, versionID: value)); - }); - } catch (e) { - logger.error( - ErrorType.parseModInfo, 'field to handle conflict mods'); - } - } - - if (modInfoMap.containsKey("conflicts")) { - handle(modInfoMap["conflicts"]); - } - if (modInfoMap.containsKey("breaks")) { - handle(modInfoMap["breaks"]); - } - - return ModInfo( - loader: modType, - name: modInfoMap["name"], - description: modInfoMap["description"], - version: modInfoMap["version"], - curseID: null, - md5Hash: md5Hash, - murmur2Hash: murmur2Hash, - conflicts: conflicts, - namespace: modInfoMap["id"]); - } else if (forge113 != null) { - modType = ModLoader.forge; - TomlDocument modToml; - - modToml = TomlDocument.parse(const Utf8Decoder(allowMalformed: true) - .convert(forge113.content as List)); - - modInfoMap = modToml.toMap(); - - final Map info = modInfoMap["mods"][0]; - - if (modInfoMap["logoFile"].toString().isNotEmpty) { - for (var i in unzipped) { - if (i.name == modInfoMap["logoFile"]) { - GameRepository.getModIconFile(md5Hash) - ..createSync(recursive: true) - ..writeAsBytesSync(i.content as List); - } - } - } - - return ModInfo( - loader: modType, - name: info["displayName"], - description: info["description"], - version: info["version"], - curseID: null, - md5Hash: md5Hash, - murmur2Hash: murmur2Hash, - conflicts: [], - namespace: info["modId"]); - } else if (forge112 != null) { - modType = ModLoader.forge; - modInfoMap = json.decode(const Utf8Decoder(allowMalformed: true) - .convert(forge112.content as List))[0]; - - if (modInfoMap["logoFile"].toString().isNotEmpty) { - for (ArchiveFile f in unzipped) { - if (f.name == modInfoMap["logoFile"]) { - GameRepository.getModIconFile(md5Hash) - ..createSync(recursive: true) - ..writeAsBytesSync(f.content as List); - } - } - } - - return ModInfo( - loader: modType, - name: modInfoMap["name"], - description: modInfoMap["description"], - version: modInfoMap["version"], - curseID: null, - md5Hash: md5Hash, - murmur2Hash: murmur2Hash, - conflicts: [], - namespace: modInfoMap["modid"]); - } else { - throw Exception("Unknown ModLoader"); - } - } catch (e) { - return ModInfo( - loader: modType, - name: modFile.absolute.path - .split(Platform.pathSeparator) - .last - .replaceFirst(".jar", "") - .replaceFirst(".disable", ""), - description: 'unknown', - version: 'unknown', - curseID: null, - conflicts: [], - md5Hash: md5Hash, - murmur2Hash: murmur2Hash, - namespace: "unknown"); - } - } - - /// Returns a map of mod file to mod hash - static Future> getModInfos( - IsolateOption option) async { - option.init(); - final DateTime start = DateTime.now(); - final Map infos = {}; - final List files = option.argument[0]; - final Iterable infoKeys = option.argument[1]; - - try { - for (FileSystemEntity modFile in files) { - if (modFile is File) { - if (!modFile.existsSync()) continue; - - final String md5Hash = - md5.convert(await modFile.readAsBytes()).toString(); - - try { - if (!infoKeys.contains(md5Hash)) { - final int murmur2Hash = Util.getMurmur2Hash(modFile); - final ModInfo info = - _getModInfo(modFile, murmur2Hash, md5Hash, option); - final List matchesFiles = await RPMTWApiClient - .instance.curseforgeResource - .getFilesByFingerprint([murmur2Hash]); - final int? curseID; - if (matchesFiles.isNotEmpty) { - curseID = matchesFiles.first.modId; - } else { - curseID = null; - } - - info.curseID = curseID; - option.sendData(info, index: 1); - } - infos[modFile] = md5Hash; - } on FormatException catch (e, stackTrace) { - if (e is! ArchiveException) { - logger.error(ErrorType.io, e, stackTrace: stackTrace); - } - } - } - - /// send progress - option.sendData((files.indexOf(modFile) + 1) / files.length); - } - } catch (e, stackTrace) { - logger.error(ErrorType.io, e, stackTrace: stackTrace); - } - - DateTime end = DateTime.now(); - logger.info("ModInfos loaded in ${end.difference(start).inMilliseconds}ms"); - return infos; - } - - void filterSearchResults(String query) { - modInfos = Map.fromEntries( - allModInfos.entries.where((MapEntry entry) { - String name = entry.value.name; - final nameLower = name.toLowerCase(); - final searchLower = query.toLowerCase(); - return nameLower.contains(searchLower); - })); - - setModState?.call(() {}); - } - - @override - Widget build(BuildContext context) { - final ReceivePort progressPort = ReceivePort(); - - Future> get() async { - final ReceivePort hivePort = ReceivePort(); - - final List needPuts = []; - hivePort.listen((value) { - if (value is ModInfo) { - needPuts.add(value); - } - }); - - final Map hashes = await compute( - getModInfos, - IsolateOption.create([files, modInfoBox.keys.cast().toList()], - ports: [progressPort, hivePort])); - - for (ModInfo info in needPuts) { - await modInfoBox.put(info.md5Hash, info); - } - - final List> infos = hashes.entries - .map((entry) => MapEntry(entry.key, (modInfoBox.get(entry.value))!)) - .toList(); - - return Map.fromEntries(infos - ..sort((a, b) => - a.value.name.toLowerCase().compareTo(b.value.name.toLowerCase()))); - } - - return FutureBuilder>( - future: get(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData) { - allModInfos = snapshot.data!; - modInfos = allModInfos; - - return OptionPage( - mainWidget: Builder(builder: (context) { - if (files.isEmpty) { - return Center( - child: Text( - I18n.format("edit.instance.mods.list.found"), - style: const TextStyle(fontSize: 30), - )); - } else { - return Column( - children: [ - const SizedBox( - height: 12, - ), - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - width: 12, - ), - Expanded( - child: RPMLTextField( - textAlign: TextAlign.center, - controller: modSearchController, - hintText: I18n.format('edit.instance.mods.enter'), - onEditingComplete: () { - filterSearchResults(modSearchController.text); - }, - )), - const SizedBox( - width: 12, - ), - const SizedBox( - width: 12, - ), - ], - ), - const SizedBox( - height: 10, - ), - StatefulBuilder(builder: (context, setModState_) { - DateTime start = DateTime.now(); - setModState = setModState_; - - return Expanded( - child: ListView.builder( - itemCount: modInfos.length, - itemBuilder: (context, index) { - MapEntry entry = - modInfos.entries.toList()[index]; - final info = entry.value; - - try { - return Dismissible( - key: Key(info.md5Hash), - onDismissed: (direction) async { - bool deleted = await info - .deleteMod(entry.key, onDeleting: () { - deletedModFiles.add(entry.key.path); - modInfos.remove(entry.key); - setModState?.call(() {}); - }); - - if (deleted && mounted) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar( - content: I18nText( - 'edit.instance.mods.deleted', - args: {"mod_name": info.name}, - ))); - } - }, - background: Container(color: Colors.red), - child: modListTile(info, entry.key, context), - ); - } catch (error, stackTrace) { - logger.error(ErrorType.unknown, error, - stackTrace: stackTrace); - return Container(); - } finally { - if (entry.key == modInfos.keys.last) { - DateTime end = DateTime.now(); - logger.info( - "ModList built in ${end.difference(start).inMilliseconds}ms"); - } - } - }, - ), - ); - }) - ], - ); - } - }), - actions: [ - IconButton( - icon: const Icon(Icons.add), - onPressed: () { - if (widget.instanceConfig.loaderEnum == ModLoader.vanilla) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: I18nText.errorInfoText(), - content: I18nText( - "edit.instance.mods.error.vanilla"), - actions: [ - TextButton( - child: Text(I18n.format("gui.ok")), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - )); - } else { - showDialog( - context: context, - builder: (context) => ModSourceSelection( - widget.instance.uuid, allModInfos)); - } - }, - tooltip: I18n.format("gui.mod.add"), - ), - IconButton( - icon: const Icon(Icons.folder), - onPressed: () { - Util.openFileManager(modDir); - }, - tooltip: I18n.format("edit.instance.mods.folder.open"), - ), - IconButton( - icon: const Icon(Icons.restore), - onPressed: () { - showDialog( - context: context, - builder: (context) => _CheckModUpdates( - modInfos: allModInfos, - instance: widget.instance, - setModState: setModState)); - }, - tooltip: I18n.format("edit.instance.mods.updater.check"), - ), - IconButton( - icon: const Icon(Icons.file_download), - onPressed: () { - showDialog( - context: context, - builder: (context) => _UpdateAllMods( - modInfos: allModInfos, - modDir: modDir, - )); - }, - tooltip: I18n.format("edit.instance.mods.updater.update_all"), - ) - ], - ); - } else if (snapshot.hasError) { - return Text(snapshot.error.toString()); - } else { - return _ModInfoLoading(progressPort: progressPort); - } - }); - } - - Widget modListTile(ModInfo modInfo, File file, BuildContext context) { - if (!file.existsSync()) { - if (extension(file.path) == '.jar' && - File("${file.path}.disable").existsSync() || - (extension(file.path) == '.disable' && - File(file.path.split(".disable")[0]).existsSync())) { - } else { - return const SizedBox(); - } - } - - String modName = modInfo.name; - - return ContextMenuArea( - builder: (context) => [ - ListTile( - title: I18nText("edit.instance.mods.list.delete"), - subtitle: I18nText("edit.instance.mods.list.delete.description"), - onTap: () { - Navigator.pop(context); - modInfo.deleteMod(file, onDeleting: () { - deletedModFiles.add(file.path); - modInfos.remove(file); - setModState?.call(() {}); - }); - }, - ), - Builder(builder: (context) { - bool modSwitch = !file.path.endsWith(".disable"); - - String tooltip = modSwitch - ? I18n.format('gui.disable') - : I18n.format('gui.enable'); - return ListTile( - title: Text(tooltip), - subtitle: I18nText( - "edit.instance.mods.list.disable_or_enable", - args: {"disable_or_enable": tooltip}, - ), - onTap: () async { - try { - String newPath; - if (modSwitch) { - modSwitch = false; - newPath = "${file.absolute.path}.disable"; - } else { - modSwitch = true; - newPath = file.absolute.path.split(".disable")[0]; - } - await file.rename(newPath); - - File newFile = File(newPath); - ModInfo info = allModInfos[file]!; - allModInfos.remove(file); - modInfos.remove(file); - allModInfos[newFile] = info; - modInfos[newFile] = info; - - setModState?.call(() {}); - } on FileSystemException {} - if (mounted) { - Navigator.of(context).pop(); - } - }, - ); - }), - ], - child: Row( - children: [ - Expanded( - child: ListTile( - leading: FutureBuilder( - future: modInfo.getImageWidget(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return SizedBox( - width: 50, height: 50, child: snapshot.data!); - } else { - return const SizedBox( - width: 50, height: 50, child: RWLLoading()); - } - }, - ), - title: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(modName), - ], - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Builder( - builder: (context) { - if (modInfo.needsUpdate) { - return Tooltip( - message: I18n.format( - "edit.instance.mods.updater.update", - ), - child: IconButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => _UpdateMod( - modInfo: modInfo, - modDir: modDir, - file: file)); - }, - icon: const Icon(Icons.file_download)), - ); - } else { - return const SizedBox(); - } - }, - ), - Builder(builder: (context) { - Map conflictMods = Map.fromEntries( - allModInfos.entries.where((entry) => entry - .value.conflicts - .any((mod) => mod.isConflict(modInfo)))); - - if (conflictMods.isNotEmpty) { - List conflictModNames = []; - conflictMods.forEach((file, info) { - conflictModNames.add(info.name); - }); - - return Tooltip( - message: I18n.format('edit.instance.mods.list.conflict', - args: { - "mods": conflictModNames - .join(I18n.format('gui.separate')) - }), - child: const Icon(Icons.warning), - ); - } else { - return const SizedBox(); - } - }), - Builder( - builder: (context) { - if (modInfo.loader == widget.instanceConfig.loaderEnum || - modInfo.loader == ModLoader.unknown) { - return const SizedBox(); - } else { - return Tooltip( - message: I18n.format( - "edit.instance.mods.list.conflict.loader", - args: { - "modloader": modInfo.loader.name, - "instance_modloader": - widget.instanceConfig.loader - }), - child: const Icon(Icons.warning)); - } - }, - ), - FileSwitchBox(file: file), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - modInfo.deleteMod(file, onDeleting: () { - deletedModFiles.add(file.path); - modInfos.remove(file); - setModState?.call(() {}); - }); - }, - ), - ], - ), - onTap: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: SelectableText( - I18n.format("edit.instance.mods.list.name", - args: {"name": modName}), - textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text(I18n.format( - "edit.instance.mods.list.description", - args: {"description": modInfo.description})), - Text( - I18n.format("edit.instance.mods.list.version") + - modInfo.version.toString()), - curseForgeInfo(modInfo.curseID) - ], - )); - }, - ); - }, - ), - ), - const SizedBox( - width: 15, - ), - ], - ), - ); - } -} - -class _UpdateAllMods extends StatefulWidget { - const _UpdateAllMods({ - Key? key, - required this.modInfos, - required this.modDir, - }) : super(key: key); - - final Map modInfos; - final Directory modDir; - - @override - State<_UpdateAllMods> createState() => _UpdateAllModsState(); -} - -class _UpdateAllModsState extends State<_UpdateAllMods> { - int total = 0; - int done = 0; - double _progress = 0.0; - late bool needUpdate; - - Future updateAllIng() async { - Map needUpdates = Map.fromEntries( - widget.modInfos.entries.where((entry) => entry.value.needsUpdate)); - total = needUpdates.length; - for (MapEntry entry in needUpdates.entries) { - await entry.value.updating(widget.modDir, entry.key); - done++; - _progress = done / total; - if (mounted) { - setState(() {}); - } - } - } - - @override - void initState() { - needUpdate = - widget.modInfos.entries.any((entry) => entry.value.needsUpdate); - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - if (needUpdate) { - updateAllIng(); - } - }); - } - - @override - Widget build(BuildContext context) { - if (needUpdate) { - if (_progress == 1.0) { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: I18nText("edit.instance.mods.updater.update_all.done"), - actions: const [OkClose()], - ); - } else { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText("edit.instance.mods.updater.updating"), - I18nText( - "edit.instance.mods.updater.progress", - args: { - "done": done.toString(), - "total": total.toString(), - }, - ), - const SizedBox( - height: 12, - ), - LinearProgressIndicator(value: _progress) - ], - ), - ); - } - } else { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: I18nText("edit.instance.mods.updater.update_all.none"), - actions: const [OkClose()], - ); - } - } -} - -class _UpdateMod extends StatelessWidget { - const _UpdateMod( - {Key? key, - required this.modInfo, - required this.file, - required this.modDir}) - : super(key: key); - final ModInfo modInfo; - final File file; - final Directory modDir; - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: modInfo.updating(modDir, file), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: I18nText("edit.instance.mods.updater.done"), - actions: const [OkClose()], - ); - } else { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText("edit.instance.mods.updater.updating"), - const SizedBox(height: 12), - const RWLLoading() - ], - ), - ); - } - }, - ); - } -} - -class _CheckModUpdates extends StatefulWidget { - const _CheckModUpdates( - {Key? key, - required this.modInfos, - required this.instance, - required this.setModState}) - : super(key: key); - - final Map modInfos; - final Instance instance; - final StateSetter? setModState; - - @override - State<_CheckModUpdates> createState() => _CheckModUpdatesState(); -} - -class _CheckModUpdatesState extends State<_CheckModUpdates> { - late int total; - int done = 0; - double _progress = 0.0; - - @override - void initState() { - total = widget.modInfos.length; - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) => checking()); - } - - Future checking() async { - for (MapEntry entry in widget.modInfos.entries) { - // 更新延遲至少需要5分鐘 - ModInfo info = entry.value; - File file = entry.key; - - if (info.curseID != null && - (info.lastUpdate?.isBefore( - DateTime.now().subtract(const Duration(minutes: 5))) ?? - true)) { - CurseForgeModFile? updateData = await CurseForgeHandler.needUpdates( - info.curseID!, - widget.instance.config.version, - widget.instance.config.loaderEnum, - info.murmur2Hash); - - info.lastUpdate = DateTime.now(); - if (updateData != null) { - info.needsUpdate = true; - info.lastUpdateData = updateData.toMap(); - } - try { - await info.save(); - } catch (e) {} - } - done++; - _progress = (widget.modInfos.keys.toList().indexOf(file) + 1) / - widget.modInfos.length; - - if (mounted) { - setState(() {}); - } - } - if (mounted) { - widget.setModState?.call(() {}); - } - } - - @override - Widget build(BuildContext context) { - if (_progress == 1.0) { - bool press = false; - Map needUpdates = Map.fromEntries( - widget.modInfos.entries.where((entry) => entry.value.needsUpdate)); - - return AlertDialog( - title: I18nText.tipsInfoText(), - content: StatefulBuilder(builder: (context, setState) { - return SizedBox( - width: 280, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - I18nText("edit.instance.mods.updater.check.done"), - Builder( - builder: (context) { - if (needUpdates.isEmpty) return Container(); - - if (press) { - return IconButton( - onPressed: () { - press = false; - setState(() {}); - }, - icon: const Icon(Icons.unfold_less), - ); - } else { - return IconButton( - icon: const Icon(Icons.unfold_more), - onPressed: () { - press = true; - setState(() {}); - }, - ); - } - }, - ), - Builder( - builder: (context) { - if (press) { - return I18nText( - "edit.instance.mods.updater.check.can_update"); - } else { - return Container(); - } - }, - ), - Builder( - builder: (context) { - if (press) { - return ListView.builder( - itemBuilder: (context, index) { - return Text( - needUpdates.entries.elementAt(index).value.name); - }, - shrinkWrap: true, - itemCount: needUpdates.length, - ); - } else { - return Container(); - } - }, - ) - ], - ), - ); - }), - actions: const [OkClose()], - ); - } else { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText("edit.instance.mods.updater.checking"), - I18nText( - "edit.instance.mods.updater.progress", - args: { - "done": done.toString(), - "total": total.toString(), - }, - ), - const SizedBox( - height: 12, - ), - LinearProgressIndicator(value: _progress) - ], - ), - ); - } - } -} - -class _ModInfoLoading extends StatefulWidget { - const _ModInfoLoading({ - Key? key, - required this.progressPort, - }) : super(key: key); - - final ReceivePort progressPort; - - @override - State<_ModInfoLoading> createState() => _ModInfoLoadingState(); -} - -class _ModInfoLoadingState extends State<_ModInfoLoading> { - double progress = 0.0; - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - widget.progressPort.listen((message) { - if (message is double && mounted) { - progress = message; - setState(() {}); - } - }); - }); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - const SizedBox(height: 30), - Row( - children: [ - const SizedBox( - width: 50, - ), - Expanded(child: LinearProgressIndicator(value: progress)), - const SizedBox( - width: 50, - ), - ], - ), - const SizedBox(height: 15), - I18nText("edit.instance.mods.loading", - style: const TextStyle(fontSize: 30)), - ], - ); - } -} - -Widget curseForgeInfo(int? curseID) { - return Builder(builder: (content) { - if (curseID != null) { - return IconButton( - onPressed: () async { - CurseForgeMod? mod; - try { - mod = await RPMTWApiClient.instance.curseforgeResource - .getMod(curseID); - } catch (e) { - mod = null; - } - - if (mod != null) { - String pageUrl = mod.links.websiteUrl; - Util.openUri(pageUrl); - } - }, - icon: const Icon(Icons.open_in_new), - tooltip: I18n.format('edit.instance.mods.open_in_curseforge'), - ); - } else { - return const SizedBox.shrink(); - } - }); -} diff --git a/lib/view/MinecraftNewsView.dart b/lib/view/MinecraftNewsView.dart deleted file mode 100644 index 461ecd3a7..000000000 --- a/lib/view/MinecraftNewsView.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/model/Game/MinecraftNews.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/RPMNetworkImage.dart'; -import 'package:smooth_page_indicator/smooth_page_indicator.dart'; - -class MinecraftNewsView extends StatefulWidget { - final MinecraftNews news; - - const MinecraftNewsView({ - required this.news, - Key? key, - }) : super(key: key); - - @override - State createState() => _MinecraftNewsViewState(); -} - -class _MinecraftNewsViewState extends State { - late PageController newsPageController; - int index = 0; - - @override - void initState() { - newsPageController = PageController(keepPage: true); - Timer.periodic(const Duration(seconds: 5), (timer) { - if (mounted) { - setState(() { - index = index == (widget.news.length - 1) ? 0 : index + 1; - }); - } - }); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: 15, - ), - Builder(builder: (context) { - MinecraftNew news = widget.news[index]; - return InkWell( - onTap: () => Util.openUri(news.link), - child: Column( - children: [ - SizedBox( - width: 250, - height: 250, - child: RPMNetworkImage(src: news.imageUri)), - Text(news.title, textAlign: TextAlign.center), - ], - ), - ); - }), - const SizedBox( - height: 10, - ), - Center( - child: AnimatedSmoothIndicator( - activeIndex: index, - count: widget.news.length, - onDotClicked: (index_) { - index = index_; - setState(() {}); - }, - ), - ), - ], - ), - Expanded( - child: ListView.builder( - controller: ScrollController(), - itemCount: widget.news.length, - itemBuilder: (context, index) { - MinecraftNew news = widget.news[index]; - return ListTile( - onTap: () => Util.openUri(news.link), - leading: SizedBox( - width: 50, - height: 50, - child: RPMNetworkImage( - src: news.imageUri, - fit: BoxFit.contain, - ), - ), - title: Text(news.title), - subtitle: Text(news.description), - trailing: IconButton( - onPressed: () => Util.openUri(news.link), - icon: const Icon(Icons.open_in_browser), - ), - ); - }), - ), - ], - ); - } -} diff --git a/lib/view/OptionsView.dart b/lib/view/OptionsView.dart deleted file mode 100644 index d9392fe81..000000000 --- a/lib/view/OptionsView.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:split_view/split_view.dart'; - -import 'package:rpmlauncher/model/UI/ViewOptions.dart'; - -class OptionsView extends StatefulWidget { - final List Function(StateSetter) optionWidgets; - final ViewOptions Function() options; - final List? weights; - final List? limits; - final double gripSize; - - const OptionsView({ - Key? key, - required this.optionWidgets, - required this.options, - this.weights, - this.limits, - required this.gripSize, - }) : super(key: key); - - @override - State createState() => _OptionsViewState(); -} - -class _OptionsViewState extends State { - final PageController _pageController = PageController(initialPage: 0); - int selectedIndex = 0; - bool pageIsScrolling = false; - - Future _animateToPage(int index) async { - try { - int? page = _pageController.page?.toInt(); - if (page != null && ((page - index == 1) || page - index == -1)) { - await _pageController.animateToPage( - index, - duration: const Duration(milliseconds: 350), - curve: Curves.easeInOut, - ); - } else { - _pageController.jumpToPage(index); - } - } catch (e) {} - } - - @override - void dispose() { - _pageController.dispose(); - super.dispose(); - } - - void onScroll(double offset) { - if (pageIsScrolling == false) { - pageIsScrolling = true; - if (offset > 0) { - _pageController - .nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut) - .then((value) => pageIsScrolling = false); - } else { - _pageController - .previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut) - .then((value) => pageIsScrolling = false); - } - } - } - - @override - Widget build(BuildContext context) { - return SplitView( - gripSize: widget.gripSize, - controller: SplitViewController( - weights: widget.weights ?? [0.2, 0.8], - limits: widget.limits ?? - [WeightLimit(max: 0.2, min: 0.1), WeightLimit()]), - viewMode: SplitViewMode.Horizontal, - children: [ - StatefulBuilder(builder: (context, setOptionState) { - return ListView.builder( - itemCount: widget.options.call().length, - itemBuilder: (context, index) { - ViewOptionTile option = widget.options.call().options[index]; - late Widget optionWidget; - - if (!option.show) { - optionWidget = const SizedBox.shrink(); - } else { - optionWidget = ListTile( - title: Text(option.title!), - leading: option.icon, - onTap: () async { - selectedIndex = index; - setOptionState(() {}); - // _pageController.jumpToPage(index); - await _animateToPage(index); - }, - tileColor: selectedIndex == index - ? Colors.white12 - : Theme.of(context).scaffoldBackgroundColor, - trailing: Builder(builder: (context) { - if (option.description != null) { - return Tooltip( - message: option.description!, - child: const Icon(Icons.help), - ); - } else { - return const SizedBox(); - } - }), - ); - } - - return optionWidget; - }); - }), - StatefulBuilder(builder: (context, setPageState) { - // return Listener( - // onPointerSignal: (pointerSignal) { - // if (pointerSignal is PointerScrollEvent) { - // onScroll(pointerSignal.scrollDelta.dy); - // } - // },); - return PageView( - physics: const NeverScrollableScrollPhysics(), - scrollDirection: Axis.vertical, - controller: _pageController, - children: widget.optionWidgets.call(setPageState), - ); - }) - ]); - } -} - -class OptionPage extends StatefulWidget { - final Widget mainWidget; - final List actions; - - const OptionPage({ - Key? key, - required this.mainWidget, - required this.actions, - }) : super(key: key); - - @override - State createState() => _OptionPageState(); -} - -class _OptionPageState extends State { - @override - Widget build(BuildContext context) { - return SplitView( - viewMode: SplitViewMode.Vertical, - gripSize: 0, - controller: SplitViewController( - weights: [0.92], limits: [WeightLimit(max: 0.92, min: 0.92)]), - children: [ - widget.mainWidget, - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: widget.actions, - ) - ], - ); - } -} diff --git a/lib/view/instance_view.dart b/lib/view/instance_view.dart deleted file mode 100644 index f3436582b..000000000 --- a/lib/view/instance_view.dart +++ /dev/null @@ -1,316 +0,0 @@ -import 'dart:io'; - -import 'package:contextmenu/contextmenu.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/launcher/GameRepository.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/Background.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:split_view/split_view.dart'; - -class InstanceView extends StatefulWidget { - final MinecraftSide side; - - const InstanceView({Key? key, required this.side}) : super(key: key); - - @override - State createState() => _InstanceViewState(); -} - -class _InstanceViewState extends State { - Directory instanceRootDir = GameRepository.getInstanceRootDir(); - int chooseIndex = -1; - - @override - void initState() { - super.initState(); - instanceRootDir.watch().listen((event) async { - try { - await Future.delayed(const Duration(milliseconds: 250)); - Directory dir = Directory(event.path); - bool check2 = event.isDirectory && - (await dir.list().toList()) - .any((e) => basename(e.path) == 'instance.json'); - - if (mounted && - (event.path.contains('instance.json') || - check2 || - event is FileSystemDeleteEvent)) { - setState(() {}); - } - } catch (e) {} - }); - } - - Future> getInstanceList() async { - List instances = []; - List dirs = await instanceRootDir.list().toList(); - - for (FileSystemEntity dir in dirs) { - if (dir is Directory) { - List files = await dir.list().toList(); - if (files.any((file) => basename(file.path) == 'instance.json')) { - Instance? instance = - Instance.fromUUID(InstanceRepository.getUUIDByDir(dir)); - if (instance != null && instance.config.sideEnum == widget.side) { - instances.add(instance); - } - } - } - } - - instances.sort((a, b) => a.name.compareTo(b.name)); - return instances; - } - - @override - Widget build(BuildContext context) { - return Background( - child: FutureBuilder( - builder: (context, AsyncSnapshot> snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.isNotEmpty) { - return SplitView( - gripSize: 0, - controller: SplitViewController(weights: [0.7]), - viewMode: SplitViewMode.Horizontal, - children: [ - Builder( - builder: (context) { - return GridView.builder( - itemCount: snapshot.data!.length, - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 8), - physics: const ScrollPhysics(), - itemBuilder: (context, index) { - try { - Instance instance = snapshot.data![index]; - - return ContextMenuArea( - builder: (context) => [ - ListTile( - title: I18nText('gui.instance.launch'), - subtitle: I18nText( - 'gui.instance.launch.subtitle'), - onTap: () { - Navigator.pop(context); - instance.launch(context); - }, - ), - ListTile( - title: I18nText('gui.edit'), - subtitle: I18nText('gui.edit.subtitle'), - onTap: () { - Navigator.pop(context); - instance.edit(context); - }, - ), - ListTile( - title: I18nText('gui.folder'), - subtitle: I18nText( - 'homepage.instance.contextmenu.folder.subtitle'), - onTap: () { - Navigator.pop(context); - instance.openFolder(); - }, - ), - ListTile( - title: I18nText('gui.copy'), - subtitle: I18nText( - 'homepage.instance.contextmenu.copy.subtitle'), - onTap: () { - Navigator.pop(context); - instance.copy(); - }, - ), - ListTile( - title: I18nText('gui.delete', - style: - const TextStyle(color: Colors.red)), - subtitle: I18nText( - 'homepage.instance.contextmenu.delete.subtitle'), - onTap: () { - Navigator.pop(context); - instance.delete(); - }, - ) - ], - child: ClipRRect( - borderRadius: BorderRadius.circular(20), - child: Card( - child: InkWell( - onTap: () { - chooseIndex = index; - setState(() {}); - }, - child: Padding( - padding: const EdgeInsets.all(5.0), - child: Column( - children: [ - Expanded( - child: instance.imageWidget( - expand: true)), - Text(instance.name, - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis) - ], - ), - ), - ), - ), - ), - ); - } on FileSystemException { - return const SizedBox.shrink(); - } catch (e, stackTrace) { - logger.error(ErrorType.unknown, e, - stackTrace: stackTrace); - return const SizedBox.shrink(); - } - }, - ); - }, - ), - Builder(builder: (context) { - if (chooseIndex == -1 || - (snapshot.data!.length - 1) < chooseIndex || - !InstanceRepository.instanceConfigFile( - snapshot.data![chooseIndex].path) - .existsSync()) { - return Container(); - } else { - Instance instance = snapshot.data![chooseIndex]; - return SingleChildScrollView( - controller: ScrollController(), - child: Column( - children: [ - const SizedBox( - height: 10, - ), - instance.imageWidget(width: 100, height: 100), - const SizedBox( - height: 5, - ), - Text(instance.name, - style: const TextStyle(color: Colors.white), - textAlign: TextAlign.center), - const SizedBox(height: 12), - _InstanceActionButton( - icon: const Icon( - Icons.play_arrow, - ), - label: - Text(I18n.format('gui.instance.launch')), - onPressed: () => instance.launch(context)), - const SizedBox(height: 12), - _InstanceActionButton( - onPressed: () { - instance.edit(context); - }, - icon: const Icon( - Icons.edit, - ), - label: Text(I18n.format('gui.edit')), - ), - const SizedBox(height: 12), - _InstanceActionButton( - icon: const Icon( - Icons.folder, - ), - onPressed: () { - instance.openFolder(); - }, - label: Text(I18n.format('gui.folder')), - ), - const SizedBox(height: 12), - _InstanceActionButton( - icon: const Icon( - Icons.content_copy, - ), - onPressed: () { - instance.copy(); - }, - label: Text(I18n.format('gui.copy')), - ), - const SizedBox(height: 12), - _InstanceActionButton( - icon: const Icon( - Icons.delete, - ), - label: Text(I18n.format('gui.delete')), - onPressed: () { - instance.delete(); - }, - ), - ], - ), - ); - } - }), - ]); - } else { - return Transform.scale( - scale: 2, - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Icon(Icons.sports_esports, color: Colors.white), - Text(I18n.format('homepage.instance.found'), - style: const TextStyle(color: Colors.white)), - Text(I18n.format('homepage.instance.found.tips'), - style: const TextStyle(color: Colors.white)) - ]))); - } - } else { - return const RWLLoading(); - } - }, - future: getInstanceList(), - ), - ); - } -} - -class _InstanceActionButton extends StatelessWidget { - const _InstanceActionButton({ - Key? key, - required this.onPressed, - required this.icon, - required this.label, - }) : super(key: key); - - final VoidCallback? onPressed; - final Icon icon; - final Text label; - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: onPressed, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - icon, - const SizedBox(width: 8), - Text( - label.data!, - overflow: TextOverflow.ellipsis, - ) - ], - ), - ), - ); - } -} diff --git a/lib/widget/AccountManageAction.dart b/lib/widget/AccountManageAction.dart deleted file mode 100644 index d4fa682b1..000000000 --- a/lib/widget/AccountManageAction.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/model/account/Account.dart'; -import 'package:rpmlauncher/screen/account.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; - -class AccountManageButton extends StatelessWidget { - const AccountManageButton({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - Account? currentAccount = AccountStorage().getDefault(); - - if (AccountStorage().hasAccount) { - return Tooltip( - message: I18n.format("account.title"), - child: InkResponse( - radius: 40, - highlightShape: BoxShape.rectangle, - borderRadius: const BorderRadius.all(Radius.circular(30)), - child: Row( - children: [ - SizedBox( - width: 30, height: 30, child: currentAccount!.imageWidget), - const SizedBox( - width: 5, - ), - Text(currentAccount.username), - const SizedBox( - width: 10, - ), - ], - ), - onTap: () => AccountScreen.push(context), - ), - ); - } else { - return IconButton( - icon: const Icon(Icons.manage_accounts), - onPressed: () { - AccountScreen.push(context); - }, - tooltip: I18n.format("account.title"), - ); - } - } -} diff --git a/lib/widget/AddInstance.dart b/lib/widget/AddInstance.dart deleted file mode 100644 index 22172e677..000000000 --- a/lib/widget/AddInstance.dart +++ /dev/null @@ -1,267 +0,0 @@ -import 'package:rpmlauncher/launcher/Fabric/FabricClient.dart'; -import 'package:rpmlauncher/launcher/Fabric/FabricServer.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeClient.dart'; -import 'package:rpmlauncher/launcher/InstallingState.dart'; -import 'package:rpmlauncher/launcher/Vanilla/VanillaClient.dart'; -import 'package:rpmlauncher/launcher/Vanilla/VanillaServer.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftMeta.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/Game/MinecraftVersion.dart'; -import 'package:rpmlauncher/route/PushTransitions.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/rpml_text_field.dart'; -import 'package:uuid/uuid.dart'; - -import 'rwl_loading.dart'; - -class AddInstanceDialog extends StatefulWidget { - final String instanceName; - final MCVersion version; - final ModLoader modLoaderID; - final String? loaderVersion; - final MinecraftSide side; - final Future Function(Instance)? onInstalled; - - const AddInstanceDialog(this.instanceName, this.version, this.modLoaderID, - this.loaderVersion, this.side, - {this.onInstalled}); - - @override - State createState() => _AddInstanceDialogState(); -} - -class _AddInstanceDialogState extends State { - late TextEditingController _nameController; - - @override - void initState() { - _nameController = TextEditingController(text: widget.instanceName); - super.initState(); - } - - @override - void dispose() { - _nameController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - contentPadding: const EdgeInsets.all(16.0), - title: Text(I18n.format('version.list.instance.add')), - content: Row( - children: [ - Text(I18n.format('edit.instance.homepage.instance.name')), - Expanded( - child: RPMLTextField( - controller: _nameController, - onChanged: (value) { - setState(() {}); - }, - )), - ], - ), - actions: [ - TextButton( - child: Text(I18n.format('gui.cancel')), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text(I18n.format('gui.confirm')), - onPressed: () { - installingState.finish = false; - navigator.pop(); - navigator.push( - PushTransitions( - builder: (context) => HomePage( - /// 依據玩家選擇的安裝檔類型到不同頁面 (客戶端或伺服器) - initialPage: widget.side.isClient ? 0 : 1, - )), - ); - - WidgetsBinding.instance.addPostFrameCallback((_) async { - showDialog( - context: context, - builder: (BuildContext context) { - return FutureBuilder( - future: widget.version.meta, - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return Task( - meta: snapshot.data, - version: widget.version, - loader: widget.modLoaderID, - loaderVersion: widget.loaderVersion ?? '', - instanceName: _nameController.text, - side: widget.side, - onInstalled: widget.onInstalled, - ); - } else if (snapshot.hasError) { - return Text(snapshot.error.toString()); - } else { - return const Center(child: RWLLoading()); - } - }); - }); - }); - }, - ), - ], - ); - } -} - -class Task extends StatefulWidget { - final MinecraftMeta meta; - final MCVersion version; - final ModLoader loader; - final String loaderVersion; - final String instanceName; - final MinecraftSide side; - final Future Function(Instance)? onInstalled; - - const Task( - {Key? key, - required this.meta, - required this.version, - required this.loader, - required this.loaderVersion, - required this.instanceName, - required this.side, - this.onInstalled}) - : super(key: key); - - @override - State createState() => _TaskState(); -} - -class _TaskState extends State { - @override - void initState() { - installingState.nowEvent = I18n.format('version.list.downloading.ready'); - - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - String uuid = const Uuid().v4(); - InstanceConfig config = InstanceConfig( - uuid: uuid, - name: widget.instanceName, - side: widget.side, - version: widget.version.id, - loader: widget.loader.name, - javaVersion: widget.meta.javaVersion, - loaderVersion: widget.loaderVersion, - assetsID: widget.meta['assets']); - config.createConfigFile(); - Instance instance = Instance.fromUUID(uuid)!; - - Future whenComplete() async { - if (widget.onInstalled != null) { - installingState.nowEvent = - I18n.format('version.list.downloading.handling'); - setState(() {}); - await widget.onInstalled!(instance); - } - installingState.finish = true; - setState(() {}); - } - - Util.javaCheckDialog( - hasJava: () { - if (widget.loader == ModLoader.vanilla) { - if (widget.side == MinecraftSide.client) { - VanillaClient.createClient( - setState: setState, - meta: widget.meta, - versionID: widget.version.id, - instance: instance) - .whenComplete(() => whenComplete()); - } else if (widget.side == MinecraftSide.server) { - VanillaServer.createServer( - setState: setState, - meta: widget.meta, - versionID: widget.version.id, - instance: instance) - .whenComplete(() => whenComplete()); - } - } else if (widget.loader == ModLoader.fabric) { - if (widget.side == MinecraftSide.client) { - FabricClient.createClient( - setState: setState, - meta: widget.meta, - versionID: widget.version.id, - loaderVersion: widget.loaderVersion, - instance: instance) - .whenComplete(() => whenComplete()); - } else if (widget.side == MinecraftSide.server) { - FabricServer.createServer( - setState: setState, - meta: widget.meta, - versionID: widget.version.id, - loaderVersion: widget.loaderVersion, - instance: instance) - .whenComplete(() => whenComplete()); - } - } else if (widget.loader == ModLoader.forge) { - ForgeClient.createClient( - setState: setState, - meta: widget.meta, - gameVersionID: widget.version.id, - forgeVersionID: widget.loaderVersion, - instance: instance) - .then((ForgeClientState state) => state.handlerState( - context, setState, instance, - onSuccessful: widget.onInstalled)); - } - }, - allJavaVersions: instance.config.needJavaVersion); - }); - } - - @override - Widget build(BuildContext context) { - if (installingState.downloadInfos.progress == 1.0 && - installingState.finish) { - return AlertDialog( - contentPadding: const EdgeInsets.all(16.0), - title: Text(I18n.format('gui.download.done')), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text(I18n.format('gui.close'))) - ], - ); - } else { - return WillPopScope( - onWillPop: () => Future.value(false), - child: AlertDialog( - title: Text(installingState.nowEvent, textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - installingState.downloadInfos.progress == 0.0 - ? const LinearProgressIndicator() - : LinearProgressIndicator( - value: installingState.downloadInfos.progress, - ), - Text( - '${(installingState.downloadInfos.progress * 100).toStringAsFixed(2)}%') - ], - ), - ), - ); - } - } -} diff --git a/lib/widget/DeleteFileWidget.dart b/lib/widget/DeleteFileWidget.dart deleted file mode 100644 index 9e91a9b5c..000000000 --- a/lib/widget/DeleteFileWidget.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/widget/FileDeleteError.dart'; - -class DeleteFileWidget extends StatelessWidget { - final FileSystemEntity fileSystemEntity; - final String message; - final String tooltip; - final Function? onDeleted; - - const DeleteFileWidget({ - required this.message, - required this.tooltip, - required this.fileSystemEntity, - this.onDeleted, - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return IconButton( - icon: const Icon(Icons.delete), - tooltip: tooltip, - onPressed: () { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text(I18n.format("gui.tips.info")), - content: Text(message), - actions: [ - TextButton( - child: Text(I18n.format("gui.cancel")), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text(I18n.format("gui.confirm")), - onPressed: () { - Navigator.of(context).pop(); - if (fileSystemEntity.existsSync()) { - try { - fileSystemEntity.deleteSync(recursive: true); - } on FileSystemException { - showDialog( - context: context, - builder: (context) => - const FileDeleteError()); - } - } - - if (onDeleted != null) { - onDeleted!(); - } - }), - ]); - }); - }, - ); - } -} diff --git a/lib/widget/FabricVersion.dart b/lib/widget/FabricVersion.dart deleted file mode 100644 index 6d8ca6870..000000000 --- a/lib/widget/FabricVersion.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/launcher/Fabric/FabricAPI.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/Game/MinecraftVersion.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; - -import 'AddInstance.dart'; -import 'rwl_loading.dart'; - -class FabricVersion extends StatelessWidget { - final String instanceName; - final MCVersion version; - final MinecraftSide side; - - const FabricVersion(this.instanceName, this.version, this.side); - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(I18n.format("version.list.mod.loader.fabric.version"), - textAlign: TextAlign.center), - content: SizedBox( - height: MediaQuery.of(context).size.height / 3, - width: MediaQuery.of(context).size.width / 3, - child: FutureBuilder( - future: FabricAPI.getLoaderVersions(version.id), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return ListView.builder( - shrinkWrap: true, - itemCount: snapshot.data.length, - itemBuilder: (BuildContext context, int index) { - Map fabricMeta = snapshot.data[index]; - late Text subtitleText; - bool isStable = fabricMeta["loader"]["stable"]; - if (isStable) { - subtitleText = Text( - I18n.format("edit.instance.mods.release"), - textAlign: TextAlign.center, - style: const TextStyle(color: Colors.lightBlue)); - } else { - subtitleText = Text( - I18n.format("edit.instance.mods.beta"), - textAlign: TextAlign.center, - style: const TextStyle(color: Colors.red)); - } - - return Material( - child: ListTile( - title: Text(fabricMeta["loader"]["version"], - textAlign: TextAlign.center), - subtitle: subtitleText, - onTap: () { - showDialog( - context: context, - builder: (context) => AddInstanceDialog( - instanceName, - version, - ModLoader.fabric, - fabricMeta["loader"]["version"], - side, - ), - ); - }, - ), - ); - }); - } else { - return const Center(child: RWLLoading()); - } - }, - ), - )); - } -} diff --git a/lib/widget/FileDeleteError.dart b/lib/widget/FileDeleteError.dart deleted file mode 100644 index 3ae2b418d..000000000 --- a/lib/widget/FileDeleteError.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; - -class FileDeleteError extends StatelessWidget { - const FileDeleteError({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: I18nText.errorInfoText(), - content: I18nText("rpmlauncher.file.delete.error"), - actions: const [OkClose()], - ); - } -} diff --git a/lib/widget/FileSwitchBox.dart b/lib/widget/FileSwitchBox.dart deleted file mode 100644 index d7729e558..000000000 --- a/lib/widget/FileSwitchBox.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; - -class FileSwitchBox extends StatefulWidget { - final File file; - const FileSwitchBox({ - Key? key, - required this.file, - }) : super(key: key); - - @override - State createState() => _FileSwitchBoxState(); -} - -class _FileSwitchBoxState extends State { - late File file; - bool get modSwitch => !file.path.endsWith(".disable"); - - @override - void initState() { - file = widget.file; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Tooltip( - message: - modSwitch ? I18n.format('gui.disable') : I18n.format('gui.enable'), - child: Checkbox( - value: modSwitch, - activeColor: Colors.blueAccent, - onChanged: (value) async { - try { - if (modSwitch) { - String name = "${file.absolute.path}.disable"; - await file.rename(name); - file = File(name); - setState(() {}); - } else { - String name = file.absolute.path.split(".disable")[0]; - await file.rename(name); - file = File(name); - setState(() {}); - } - } on FileSystemException {} - }), - ); - } -} diff --git a/lib/widget/ForgeVersion.dart b/lib/widget/ForgeVersion.dart deleted file mode 100644 index 538e535eb..000000000 --- a/lib/widget/ForgeVersion.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/launcher/Forge/ForgeAPI.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/model/Game/MinecraftVersion.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; - -import 'AddInstance.dart'; -import 'rwl_loading.dart'; - -class ForgeVersion extends StatefulWidget { - final String instanceName; - final MCVersion version; - - const ForgeVersion(this.instanceName, this.version); - - @override - State createState() => _ForgeVersionState(); -} - -class _ForgeVersionState extends State { - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(I18n.format('version.list.mod.loader.forge.version'), - textAlign: TextAlign.center), - content: SizedBox( - height: MediaQuery.of(context).size.height / 3, - width: MediaQuery.of(context).size.width / 3, - child: FutureBuilder( - future: ForgeAPI.getAllLoaderVersion(widget.version.id), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return ListView.builder( - shrinkWrap: true, - itemCount: snapshot.data.length, - itemBuilder: (BuildContext context, int index) { - String forgeVersionID = snapshot.data[index] - .toString() - .split("${widget.version.id}-") - .join(""); - - return Material( - child: ListTile( - title: - Text(forgeVersionID, textAlign: TextAlign.center), - subtitle: Builder(builder: (context) { - if (index == 0) { - return Text( - I18n.format( - 'version.list.mod.loader.forge.version.latest'), - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.lightGreenAccent)); - } else { - return Container(); - } - }), - onTap: () { - showDialog( - context: context, - builder: (context) => AddInstanceDialog( - widget.instanceName, - widget.version, - ModLoader.forge, - forgeVersionID, - MinecraftSide.client), - ); - }, - ), - ); - }); - } else { - return const Center(child: RWLLoading()); - } - }, - ), - )); - } -} diff --git a/lib/widget/ModSourceSelection.dart b/lib/widget/ModSourceSelection.dart deleted file mode 100644 index 053365625..000000000 --- a/lib/widget/ModSourceSelection.dart +++ /dev/null @@ -1,130 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/model/Game/mod_info.dart'; -import 'package:rpmlauncher/pages/curseforge_mods_page.dart'; -import 'package:rpmlauncher/pages/modrinth_mod.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; - -class _ModSourceSelectionState extends State { - Directory get modDir => InstanceRepository.getModRootDir(widget.instanceUUID); - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Center( - child: AlertDialog( - scrollable: true, - title: Text(I18n.format("source.mod.title"), textAlign: TextAlign.center), - content: Row( - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - FloatingActionButton( - backgroundColor: Colors.deepPurpleAccent, - onPressed: () async { - final FilePickerResult? result = - await FilePicker.platform.pickFiles(allowedExtensions: [ - 'application/zip', - 'application/java-archive', - 'jar', - ], allowMultiple: true); - - if (result == null || result.files.isEmpty) return; - - if (modDir.existsSync()) { - for (PlatformFile file in result.files) { - File(file.path!) - .copySync(join(modDir.absolute.path, file.name)); - } - } - - if (!mounted) return; - Navigator.pop(context); - }, - child: const Icon(Icons.computer), - ), - const SizedBox( - height: 12, - ), - Text(I18n.format("source.local")) - ], - ), - const SizedBox( - width: 12, - ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - FloatingActionButton( - backgroundColor: Colors.transparent, - onPressed: () { - Navigator.pop(context); - showDialog( - context: context, - builder: (context) => CurseForgeModsPage( - instanceUUID: widget.instanceUUID, - modInfos: widget.modInfos)); - }, - child: Image.asset("assets/images/CurseForge.png")), - const SizedBox( - height: 12, - ), - Text(I18n.format("source.curseforge")), - ], - ), - const SizedBox( - width: 12, - ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - FloatingActionButton( - backgroundColor: Colors.transparent, - onPressed: () { - Navigator.pop(context); - showDialog( - context: context, - builder: (context) => - ModrinthMod(instanceUUID: widget.instanceUUID)); - }, - child: Image.asset("assets/images/Modrinth.png"), - ), - const SizedBox( - height: 12, - ), - Text(I18n.format("source.modrinth")) - ], - ) - ], - ), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - onPressed: () { - Navigator.pop(context); - }, - tooltip: I18n.format("gui.close"), - ) - ], - )); - } -} - -class ModSourceSelection extends StatefulWidget { - final String instanceUUID; - final Map modInfos; - - const ModSourceSelection(this.instanceUUID, this.modInfos); - - @override - State createState() => _ModSourceSelectionState(); -} diff --git a/lib/widget/ShaderpackSourceSelection.dart b/lib/widget/ShaderpackSourceSelection.dart deleted file mode 100644 index 6c09ddb7b..000000000 --- a/lib/widget/ShaderpackSourceSelection.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:rpmlauncher/launcher/InstanceRepository.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; - -class _ShaderpackSourceSelectionState extends State { - late Directory shaderpackDir = - InstanceRepository.getShaderpackRootDir(widget.instanceUUID); - - _ShaderpackSourceSelectionState(); - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Center( - child: AlertDialog( - scrollable: true, - title: I18nText("edit.instance.shaderpack.add.source", - textAlign: TextAlign.center), - content: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - children: [ - FloatingActionButton( - backgroundColor: Colors.deepPurpleAccent, - onPressed: () async { - final FilePickerResult? result = await FilePicker.platform - .pickFiles( - dialogTitle: - I18n.format('edit.instance.shaderpack.file'), - allowMultiple: true, - allowedExtensions: ['zip']); - - if (result == null || result.files.isEmpty) { - return; - } - - for (PlatformFile file in result.files) { - File(file.path!) - .copySync(join(shaderpackDir.absolute.path, file.name)); - } - - if (!mounted) return; - Navigator.pop(context); - }, - child: const Icon(Icons.computer), - ), - const SizedBox( - height: 12, - ), - Text(I18n.format("source.local")) - ], - ), - ], - ), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - onPressed: () { - Navigator.pop(context); - }, - tooltip: I18n.format("gui.close"), - ) - ], - )); - } -} - -class ShaderpackSourceSelection extends StatefulWidget { - final String instanceUUID; - - const ShaderpackSourceSelection(this.instanceUUID); - - @override - State createState() => - _ShaderpackSourceSelectionState(); -} diff --git a/lib/widget/WIPWidget.dart b/lib/widget/WIPWidget.dart deleted file mode 100644 index 8baa8f927..000000000 --- a/lib/widget/WIPWidget.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; - -class WiPWidget extends StatelessWidget { - @override - build(BuildContext context) { - return AlertDialog( - title: Text(I18n.format('gui.tips.info')), - content: Text(I18n.format('gui.wip')), - actions: const [OkClose()], - ); - } -} diff --git a/lib/widget/dialog/GameCrash.dart b/lib/widget/dialog/GameCrash.dart deleted file mode 100644 index ef1b2b748..000000000 --- a/lib/widget/dialog/GameCrash.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:rpmlauncher/handler/window_handler.dart'; -import 'package:rpmlauncher/route/PushTransitions.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -class _GameCrashState extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Center( - child: AlertDialog( - title: Text(I18n.format("log.game.crash.title"), - textAlign: TextAlign.center), - content: SizedBox( - height: 400.0, - width: 1000.0, - child: ListView( - children: [ - Text("${I18n.format("log.game.crash.code")}: ${widget.errorCode}", - textAlign: TextAlign.center, - style: const TextStyle(color: Colors.redAccent, fontSize: 20)), - const SizedBox(height: 10), - Text("${I18n.format("log.game.crash.report")}:", - textAlign: TextAlign.center, - style: const TextStyle(color: Colors.cyanAccent, fontSize: 20)), - const SizedBox(height: 10), - Text(widget.errorLog, textAlign: TextAlign.center) - ], - ), - ), - actions: [ - IconButton( - icon: const Icon(Icons.copy_outlined), - onPressed: () { - Clipboard.setData(ClipboardData(text: widget.errorLog)); - }, - tooltip: I18n.format("gui.copy.clipboard"), - ), - IconButton( - icon: const Icon(Icons.close_sharp), - onPressed: () { - if (WindowHandler.isMultiWindow) { - WindowHandler.close(); - } else { - navigator.push( - PushTransitions(builder: (context) => const HomePage())); - } - }, - tooltip: I18n.format("gui.close"), - ) - ], - )); - } -} - -class GameCrash extends StatefulWidget { - final int errorCode; - final String errorLog; - - const GameCrash({ - required this.errorCode, - required this.errorLog, - }); - - @override - State createState() => _GameCrashState(); -} diff --git a/lib/widget/dialog/UnSupportedForgeVersion.dart b/lib/widget/dialog/UnSupportedForgeVersion.dart deleted file mode 100644 index 319336b61..000000000 --- a/lib/widget/dialog/UnSupportedForgeVersion.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; - -class UnSupportedForgeVersion extends StatelessWidget { - String gameVersion; - UnSupportedForgeVersion({ - required this.gameVersion, - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: I18nText.errorInfoText(), - content: I18nText( - "version.list.mod.loader.forge.error", - args: {"version": gameVersion}, - ), - actions: const [OkClose()], - ); - } -} diff --git a/lib/widget/dialog/agree_eula_dialog.dart b/lib/widget/dialog/agree_eula_dialog.dart deleted file mode 100644 index ee23a6a8b..000000000 --- a/lib/widget/dialog/agree_eula_dialog.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/model/IO/Properties.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/LinkText.dart'; - -class AgreeEulaDialog extends StatelessWidget { - const AgreeEulaDialog({ - Key? key, - required this.properties, - required this.eulaFile, - }) : super(key: key); - - final Properties properties; - final File eulaFile; - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: I18nText.tipsInfoText(), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - I18nText("launcher.server.eula.title"), - const SizedBox( - height: 12, - ), - LinkText( - link: "https://www.minecraft.net/en-us/eula", - text: I18n.format("launcher.server.eula")) - ], - ), - actions: [ - ElevatedButton( - onPressed: () { - properties['eula'] = true.toString(); - eulaFile.writeAsStringSync(Properties.encode(properties)); - Navigator.pop(context); - }, - child: Text(I18n.format('gui.agree')), - ), - ElevatedButton( - onPressed: () { - Navigator.pushNamed(context, HomePage.route); - }, - child: Text(I18n.format('gui.disagree')), - ) - ], - ); - } -} diff --git a/lib/widget/dialog/download_java.dart b/lib/widget/dialog/download_java.dart deleted file mode 100644 index 68c74b7d6..000000000 --- a/lib/widget/dialog/download_java.dart +++ /dev/null @@ -1,332 +0,0 @@ -import 'dart:io'; -import 'dart:isolate'; - -import 'package:archive/archive_io.dart'; -import 'package:flutter/foundation.dart'; -import 'package:rpmlauncher/model/IO/isolate_option.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/Process.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; -import 'package:rpmlauncher/widget/settings/java_path.dart'; -import 'package:rpmlauncher/util/data.dart'; - -class DownloadJava extends StatefulWidget { - final List javaVersions; - final Function? onDownloaded; - - const DownloadJava({required this.javaVersions, this.onDownloaded}); - - @override - State createState() => _DownloadJavaState(); -} - -class _DownloadJavaState extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: I18nText( - 'gui.tips.info', - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 25), - ), - content: I18nText( - 'launcher.java.install.not', - args: { - 'java_version': widget.javaVersions.join(I18n.format('gui.separate')) - }, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 20, - ), - ), - actions: [ - Center( - child: TextButton( - child: I18nText('launcher.java.install.auto', - style: const TextStyle(fontSize: 20, color: Colors.red)), - onPressed: () { - Navigator.pop(context); - showDialog( - barrierDismissible: false, - context: context, - builder: (context) => Task( - javaVersions: widget.javaVersions, - onDownloaded: widget.onDownloaded, - )); - })), - const SizedBox( - height: 10, - ), - Center( - child: TextButton( - child: I18nText('launcher.java.install.manual', - style: const TextStyle(fontSize: 20, color: Colors.lightBlue)), - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: I18nText('launcher.java.install.manual'), - content: const JavaPathSettings(), - actions: [ - OkClose( - onOk: () { - List needVersions = - Util.javaCheck(widget.javaVersions); - - if (needVersions.isNotEmpty) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: I18nText.errorInfoText(), - content: I18nText( - 'launcher.java.install.manual.error'), - actions: const [OkClose()], - )); - } else { - Navigator.pop(context); - widget.onDownloaded?.call(); - } - }, - ) - ], - )); - }, - )), - ], - ); - } -} - -class Task extends StatefulWidget { - final List javaVersions; - final Function? onDownloaded; - const Task({required this.javaVersions, this.onDownloaded}); - - @override - State createState() => _TaskState(); -} - -class _TaskState extends State { - late List downloadJavaProgress; - late List finishList; - bool isExtractingArchive = false; - - double get downloadProgress { - if (finishList.every((b) => b)) return 1; - - double p = 0.0; - downloadJavaProgress.forEach((progress) { - p += progress; - }); - - return p / downloadJavaProgress.length; - } - - bool get finish { - return finishList.every((b) => b); - } - - @override - void initState() { - super.initState(); - _start(); - } - - @override - Widget build(BuildContext context) { - if (finish) { - return AlertDialog( - title: I18nText('launcher.java.install.auto.download.done', - textAlign: TextAlign.center), - actions: [ - OkClose( - onOk: () => widget.onDownloaded?.call(), - ) - ], - ); - } else if (isExtractingArchive) { - return AlertDialog( - title: I18nText('launcher.java.install.auto.download.extracting', - textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: const [CircularProgressIndicator()], - ), - ); - } else { - return AlertDialog( - title: I18nText('launcher.java.install.auto.downloading', - textAlign: TextAlign.center), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('${(downloadProgress * 100).toStringAsFixed(2)}%'), - LinearProgressIndicator( - value: downloadProgress, - ), - ], - ), - ); - } - } - - Future _start() async { - downloadJavaProgress = - List.generate(widget.javaVersions.length, (index) => 0.0); - finishList = List.generate(widget.javaVersions.length, (index) => false); - - await Future.wait( - widget.javaVersions.map((version) => _downloadThread(version))); - - isExtractingArchive = true; - if (mounted) setState(() {}); - - await Future.wait( - widget.javaVersions.map((version) => _extractArchiveThread(version))); - } - - Future _downloadThread(int version) async { - final startTime = DateTime.now(); - final port = ReceivePort(); - - port.listen((message) { - if (mounted) { - setState(() { - downloadJavaProgress[widget.javaVersions.indexOf(version)] = - double.parse(message.toString()); - }); - - if (kTestMode) { - finishList[widget.javaVersions.indexOf(version)] = true; - } - } - }); - - await compute( - _downloadJREArchive, - IsolateOption.create(version, ports: [port]), - ); - - downloadJavaProgress[widget.javaVersions.indexOf(version)] = 1.0; - if (mounted) setState(() {}); - final endTime = DateTime.now(); - final duration = endTime.difference(startTime); - logger.info( - 'It took ${duration.inSeconds} Seconds to download Java $version'); - } - - static _downloadJREArchive(IsolateOption option) async { - option.init(); - - // java 16+ files from https://adoptium.net/temurin/archive/ - const Map javaRuntimeUrl = { - 'windows': { - '8': - 'https://github.com/AdoptOpenJDK/semeru8-binaries/releases/download/jdk8u302-b08_openj9-0.27.0/ibm-semeru-open-jdk_x64_windows_8u302b08_openj9-0.27.0.zip', - '16': - 'https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_windows_hotspot_16.0.2_7.zip', - '17': - 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4%2B8/OpenJDK17U-jre_x64_windows_hotspot_17.0.4_8.zip', - }, - 'linux': { - '8': - 'https://github.com/AdoptOpenJDK/semeru8-binaries/releases/download/jdk8u302-b08_openj9-0.27.0/ibm-semeru-open-jdk_x64_linux_8u302b08_openj9-0.27.0.tar.gz', - '16': - 'https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_linux_hotspot_16.0.2_7.tar.gz', - '17': - 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4%2B8/OpenJDK17U-jre_x64_linux_hotspot_17.0.4_8.tar.gz', - }, - 'macos': { - '8': - 'https://github.com/AdoptOpenJDK/semeru8-binaries/releases/download/jdk8u302-b08_openj9-0.27.0/ibm-semeru-open-jdk_x64_mac_8u302b08_openj9-0.27.0.tar.gz', - '16': - 'https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_mac_hotspot_16.0.2_7.tar.gz', - '17': - 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4%2B8/OpenJDK17U-jre_x64_mac_hotspot_17.0.4_8.tar.gz', - }, - 'macos-arm64': { - '17': - 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4%2B8/OpenJDK17U-jre_aarch64_mac_hotspot_17.0.4_8.tar.gz', - } - }; - - final int javaVersion = option.argument; - - Future download(String url) async { - await RPMHttpClient().download( - url, - join(dataHome.absolute.path, 'jre', javaVersion.toString(), - 'jre.${Platform.isWindows ? 'zip' : 'tar.gz'}'), - onReceiveProgress: (count, total) => option.sendData(count / total), - ); - } - - switch (Platform.operatingSystem) { - case 'windows': - await download(javaRuntimeUrl['windows'][javaVersion.toString()]); - break; - case 'linux': - await download(javaRuntimeUrl['linux'][javaVersion.toString()]); - break; - case 'macos': - // Apple Silicon is only supported on Java 17 and above - if (Util.getCPUArchitecture().contains('arm64') && javaVersion >= 17) { - await download(javaRuntimeUrl['macos-arm64'][javaVersion.toString()]); - } else { - await download(javaRuntimeUrl['macos'][javaVersion.toString()]); - } - break; - } - - Isolate.current.kill(); - } - - static Future _extractArchive(IsolateOption option) async { - option.init(); - - final int version = option.argument; - - final Directory root = - Directory(join(dataHome.absolute.path, 'jre', version.toString())); - final String inputPath = - join(root.path, 'jre.${Platform.isWindows ? 'zip' : 'tar.gz'}'); - - await extractFileToDisk(inputPath, root.path, asyncWrite: true); - await File(inputPath).delete(); - - final jreRoot = root.listSync().firstWhere((e) => e is Directory); - - late String execPath; - - if (Platform.isWindows) { - execPath = join(jreRoot.path, 'bin', 'javaw.exe'); - } else if (Platform.isLinux) { - execPath = join(jreRoot.path, 'bin', 'java'); - } else if (Platform.isMacOS) { - execPath = - join(jreRoot.path, 'jre.bundle', 'Contents', 'Home', 'bin', 'java'); - } - ConfigHelper.set('java_path_$version', execPath); - if (!kTestMode) { - await chmod(execPath); - } - Isolate.current.kill(); - } - - Future _extractArchiveThread(int version) async { - await compute(_extractArchive, IsolateOption.create(version)); - finishList[widget.javaVersions.indexOf(version)] = true; - if (mounted) setState(() {}); - } -} diff --git a/lib/widget/memory_slider.dart b/lib/widget/memory_slider.dart deleted file mode 100644 index 4192d7075..000000000 --- a/lib/widget/memory_slider.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/theme.dart'; -import 'package:rpmlauncher/widget/rwl_loading.dart'; -import 'package:rpmlauncher_plugin/rpmlauncher_plugin.dart'; - -class MemorySlider extends StatefulWidget { - final void Function(double) onChanged; - final double value; - - const MemorySlider({Key? key, required this.onChanged, required this.value}) - : super(key: key); - - @override - State createState() => _MemorySliderState(); -} - -class _MemorySliderState extends State { - late double memory; - - @override - void initState() { - memory = widget.value; - super.initState(); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: RPMLauncherPlugin.getTotalPhysicalMemory(), - builder: (context, snapshot) { - if (snapshot.hasData) { - final MemoryInfo info = snapshot.data!; - final double physical = info.physical; - final double formattedPhysical = info.formattedPhysical.toDouble(); - - /// If the computer memory size changes, reset it - if (memory > formattedPhysical) { - memory = 1024; - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - I18nText( - 'settings.java.ram.max', - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 5), - Text( - '${I18n.format('settings.java.ram.physical')} ${physical.toInt()} MB (${(physical / 1024).toStringAsFixed(2)} GB)', - style: Theme.of(context).textTheme.bodyMedium, - ), - Slider( - value: memory, - onChanged: (double value) { - memory = value; - setState(() {}); - widget.onChanged(value); - }, - activeColor: ThemeUtil.getTheme().colorScheme.primary, - min: 1024, - max: formattedPhysical, - divisions: (formattedPhysical ~/ 1024) - 1, - label: '${memory.toInt()} MB (${memory ~/ 1024}GB)', - ), - ], - ); - } else { - return const RWLLoading(); - } - }); - } -} diff --git a/lib/widget/modrinth_mod_version.dart b/lib/widget/modrinth_mod_version.dart deleted file mode 100644 index 3ac8e015c..000000000 --- a/lib/widget/modrinth_mod_version.dart +++ /dev/null @@ -1,218 +0,0 @@ -import 'dart:io'; -import 'dart:isolate'; - -import 'package:rpmlauncher/launcher/CheckData.dart'; -import 'package:rpmlauncher/mod/modrinth_handler.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/IO/isolate_option.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; - -import 'rwl_loading.dart'; - -class ModrinthModVersion extends StatefulWidget { - final String modrinthID; - final InstanceConfig instanceConfig; - final Directory modDir; - final String modName; - - const ModrinthModVersion( - this.modrinthID, this.instanceConfig, this.modDir, this.modName); - - @override - State createState() => _ModrinthModVersionState(); -} - -class _ModrinthModVersionState extends State { - List installedFiles = []; - - late List modFileList; - - @override - void initState() { - modFileList = widget.modDir.listSync().whereType().toList(); - super.initState(); - } - - Future getInstalledWidget(versionInfo) async { - late FileSystemEntity fse; - try { - fse = modFileList.firstWhere((fse) => CheckData.checkSha1Sync( - fse, versionInfo['files'][0]['hashes']['sha1'])); - installedFiles.add(fse); - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.check), - Text(I18n.format('edit.instance.mods.installed'), - textAlign: TextAlign.center) - ], - ); - } catch (e) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.close), - Text(I18n.format('edit.instance.mods.uninstalled'), - textAlign: TextAlign.center) - ], - ); - } - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(I18n.format('edit.instance.mods.download.select.version')), - content: SizedBox( - height: MediaQuery.of(context).size.height / 3, - width: MediaQuery.of(context).size.width / 3, - child: FutureBuilder( - future: ModrinthHandler.getModFilesInfo(widget.modrinthID, - widget.instanceConfig.version, widget.instanceConfig.loader), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return ListView.builder( - itemCount: snapshot.data!.length, - itemBuilder: - (BuildContext fileBuildContext, int versionIndex) { - Map versionInfo = snapshot.data[versionIndex]; - - return ListTile( - leading: SizedBox( - width: 50, - height: 50, - child: FutureBuilder( - future: getInstalledWidget(versionInfo), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return snapshot.data; - } else { - return const CircularProgressIndicator(); - } - }), - ), - title: Text(versionInfo['name']), - subtitle: ModrinthHandler.parseReleaseType( - versionInfo['version_type']), - onTap: () { - File modFile = File(join( - widget.modDir.absolute.path, - versionInfo['files'][0]['filename'])); - final url = versionInfo['files'][0]['url']; - installedFiles.forEach((file) { - try { - file.deleteSync(recursive: true); - } on FileSystemException {} - }); - showDialog( - barrierDismissible: false, - context: context, - builder: (context) => - Task(url, modFile, widget.modName), - ); - }, - ); - }); - } else { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const [RWLLoading()], - ); - } - })), - actions: [ - IconButton( - icon: const Icon(Icons.close_sharp), - tooltip: I18n.format('gui.close'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - } -} - -class Task extends StatefulWidget { - final String url; - final File modFile; - final String modName; - - const Task(this.url, this.modFile, this.modName); - - @override - State createState() => _TaskState(); -} - -class _TaskState extends State { - @override - void initState() { - super.initState(); - thread(widget.url, widget.modFile); - } - - static double _progress = 0.0; - - thread(url, modFile) async { - ReceivePort port = ReceivePort(); - ReceivePort exit = ReceivePort(); - - await Isolate.spawn( - downloading, IsolateOption.create([url, modFile], ports: [port]), - onExit: exit.sendPort); - - exit.listen((message) { - if (message == null) { - // A null message means the isolate exited - } - }); - port.listen((message) { - setState(() { - _progress = message; - }); - }); - } - - static downloading(IsolateOption option) async { - option.init(); - - String url = option.argument[0]; - File modFile = option.argument[1]; - await RPMHttpClient().download(url, modFile.path, - onReceiveProgress: (rec, total) { - option.sendData(rec / total); - }); - } - - @override - Widget build(BuildContext context) { - if (_progress == 1.0) { - return AlertDialog( - title: Text(I18n.format('gui.download.done')), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }, - child: Text(I18n.format('gui.close'))) - ], - ); - } else { - return AlertDialog( - title: Text('${I18n.format('gui.download.ing')} ${widget.modName}'), - content: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text('${(_progress * 100).toStringAsFixed(3)}%'), - LinearProgressIndicator(value: _progress) - ], - ), - ); - } - } -} diff --git a/lib/widget/rpmtw_design/Background.dart b/lib/widget/rpmtw_design/Background.dart deleted file mode 100644 index d0cc77c67..000000000 --- a/lib/widget/rpmtw_design/Background.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/config/config.dart'; - -class Background extends StatelessWidget { - const Background({ - Key? key, - required this.child, - }) : super(key: key); - final Widget child; - @override - Widget build(BuildContext context) { - return Stack(children: [ - ConstrainedBox( - constraints: const BoxConstraints.expand(), - child: Builder(builder: (context) { - Image defaultImage = Image.asset( - "assets/images/background.png", - fit: BoxFit.fill, - ); - - if (launcherConfig.backgroundImageFile == null) { - return defaultImage; - } else { - try { - return Image.file( - launcherConfig.backgroundImageFile!, - fit: BoxFit.fill, - ); - } catch (e) { - return defaultImage; - } - } - }), - ), - Opacity( - opacity: 0.18, - child: ColoredBox( - color: Colors.black, - child: SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - ), - ), - ), - child - ]); - } -} diff --git a/lib/widget/rpmtw_design/NewFeaturesWidget.dart b/lib/widget/rpmtw_design/NewFeaturesWidget.dart deleted file mode 100644 index 75047037f..000000000 --- a/lib/widget/rpmtw_design/NewFeaturesWidget.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; - -class NewFeaturesWidget extends StatelessWidget { - const NewFeaturesWidget({Key? key, required this.child}) : super(key: key); - - final Widget child; - - @override - Widget build(BuildContext context) { - return Stack( - clipBehavior: Clip.none, - children: [ - child, - const Positioned( - bottom: 11, - left: 20, - child: Icon(Icons.star_rate, color: Colors.yellow, size: 21), - ), - ], - ); - } -} diff --git a/lib/widget/rwl_loading.dart b/lib/widget/rwl_loading.dart deleted file mode 100644 index a2529f583..000000000 --- a/lib/widget/rwl_loading.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; - -class RWLLoading extends StatefulWidget { - final bool animations; - final bool logo; - - const RWLLoading({ - Key? key, - this.animations = false, - this.logo = false, - }) : super(key: key); - - @override - State createState() => _RWLLoadingState(); -} - -class _RWLLoadingState extends State { - bool get animations => widget.animations; - bool get logo => widget.logo; - - double _widgetOpacity = 0; - - List tips = [ - 'rpmlauncher.tips.1', - 'rpmlauncher.tips.2', - 'rpmlauncher.tips.3', - ]; - - @override - void initState() { - if (animations) { - Future.delayed(const Duration(milliseconds: 400)).whenComplete(() => { - if (mounted) - { - setState(() { - _widgetOpacity = 1; - }) - } - }); - } - - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - Widget wdiget = Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Builder(builder: (context) { - if (logo) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset('assets/images/Logo.png', scale: 0.9), - const SizedBox( - height: 10, - ), - ], - ); - } else { - return const SizedBox(); - } - }), - logo - ? SizedBox( - width: MediaQuery.of(context).size.width / 5, - height: MediaQuery.of(context).size.height / 45, - child: const LinearProgressIndicator()) - : const CircularProgressIndicator(), - Builder(builder: (context) { - if (logo) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - height: 10, - ), - Text(I18n.format('homepage.loading'), - style: const TextStyle( - fontSize: 35, color: Colors.lightBlue)), - const SizedBox( - height: 10, - ), - I18nText('rpmlauncher.tips.title', - style: const TextStyle( - fontSize: 15, fontStyle: FontStyle.italic)), - I18nText(tips.elementAt(Random().nextInt(tips.length)), - style: const TextStyle(fontSize: 20)), - ], - ); - } else { - return const SizedBox(); - } - }), - ], - ), - ); - - if (animations) { - wdiget = AnimatedOpacity( - opacity: _widgetOpacity, - duration: const Duration(milliseconds: 700), - child: wdiget); - } - return wdiget; - } -} diff --git a/lib/widget/settings/java_path.dart b/lib/widget/settings/java_path.dart deleted file mode 100644 index 4e5c7abbc..000000000 --- a/lib/widget/settings/java_path.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; - -class JavaPathSettings extends StatefulWidget { - const JavaPathSettings({Key? key}) : super(key: key); - - @override - State createState() => _JavaPathSettingsState(); -} - -class _JavaPathSettingsState extends State { - final List javaVersions = [8, 16, 17]; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - for (final int version in javaVersions) _JavaVersion(version: version) - ], - ); - } -} - -class _JavaVersion extends StatefulWidget { - final int version; - - const _JavaVersion({Key? key, required this.version}) : super(key: key); - - @override - State<_JavaVersion> createState() => _JavaVersionState(); -} - -class _JavaVersionState extends State<_JavaVersion> { - late String? javaPath; - - @override - void initState() { - javaPath = ConfigHelper.get('java_path_${widget.version}'); - super.initState(); - } - - @override - Widget build(BuildContext context) { - final pathStyle = Theme.of(context).textTheme.bodyMedium; - - return RowScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: IntrinsicHeight( - child: Row( - children: [ - Text('Java ${widget.version}', - style: Theme.of(context).textTheme.titleMedium), - const SizedBox(width: 10), - javaPath != null - ? Text(javaPath!, - style: pathStyle?.copyWith( - color: Theme.of(context).hintColor, - )) - : I18nText('settings.java.path.unset', - style: pathStyle?.copyWith(color: Colors.orangeAccent)), - const SizedBox(width: 10), - OutlinedButton.icon( - onPressed: () { - Util.openJavaSelectScreen(context).then((value) { - if (value[0]) { - ConfigHelper.set( - 'java_path_${widget.version}', value[1]); - javaPath = ConfigHelper.get( - 'java_path_${widget.version}'); - setState(() {}); - } - }); - }, - icon: const Icon(Icons.file_open), - label: Text(I18n.format('settings.java.path.select'))), - ], - ), - ), - ), - ); - } -} diff --git a/lib/widget/settings/jvm_args_settings.dart b/lib/widget/settings/jvm_args_settings.dart deleted file mode 100644 index b1314f5c1..000000000 --- a/lib/widget/settings/jvm_args_settings.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/model/Game/jvm_args.dart'; - -class JVMArgsSettings extends StatefulWidget { - final List value; - final Function(List) onChanged; - const JVMArgsSettings( - {super.key, required this.value, required this.onChanged}); - - @override - State createState() => _JVMArgsSettingsState(); -} - -class _JVMArgsSettingsState extends State { - late TextEditingController _controller; - - @override - void initState() { - _controller = - TextEditingController(text: JvmArgs.fromList(widget.value).args); - super.initState(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Column(children: [ - I18nText( - 'settings.java.jvm.args', - style: Theme.of(context).textTheme.titleLarge, - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), - FractionallySizedBox( - widthFactor: 0.6, - child: TextField( - controller: _controller, - decoration: InputDecoration( - hintText: I18n.format('settings.java.jvm.args.hint'), - border: const OutlineInputBorder()), - textAlign: TextAlign.center, - onChanged: (value) { - setState(() {}); - widget.onChanged(JvmArgs(args: value).toList()); - }, - ), - ) - ]); - } -} diff --git a/lib/widget/settings/theme_selector.dart b/lib/widget/settings/theme_selector.dart deleted file mode 100644 index 77ac46d39..000000000 --- a/lib/widget/settings/theme_selector.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:dynamic_themes/dynamic_themes.dart'; -import 'package:flutter/material.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/util/theme.dart'; - -class ThemeSelector extends StatefulWidget { - const ThemeSelector(); - - @override - State createState() => _ThemeSelectorState(); -} - -class _ThemeSelectorState extends State { - @override - Widget build(BuildContext context) { - return SegmentedButton( - segments: [ - ButtonSegment( - value: ThemeUtil.toInt(LauncherTheme.light), - label: Text(ThemeUtil.toI18nString(LauncherTheme.light)), - icon: const Icon(Icons.wb_sunny), - ), - ButtonSegment( - value: ThemeUtil.toInt(LauncherTheme.dark), - label: Text(ThemeUtil.toI18nString(LauncherTheme.dark)), - icon: const Icon(Icons.nightlight_round), - ), - ], - selected: {launcherConfig.themeId}, - onSelectionChanged: (newSelection) async { - final themeId = newSelection.first; - launcherConfig.themeId = themeId; - await DynamicTheme.of(context)!.setTheme(themeId); - setState(() {}); - }, - ); - } -} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 766f87759..fd3b2bbb9 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,7 +11,7 @@ import path_provider_macos import rpmlauncher_plugin import screen_retriever import sentry_flutter -import shared_preferences_macos +import shared_preferences_foundation import url_launcher_macos import window_manager import window_size diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 000000000..0b62a1ea0 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/128.png new file mode 100644 index 000000000..1fb07de56 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/16.png new file mode 100644 index 000000000..e026cddab Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/256.png new file mode 100644 index 000000000..cd4acc9c4 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/32.png new file mode 100644 index 000000000..0e3f6be7b Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/512.png new file mode 100644 index 000000000..63fae72f2 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/64.png new file mode 100644 index 000000000..1180c5f72 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/64.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index a2ec33f19..2003d5bb1 100644 --- a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,68 +1 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} +{"images":[{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} \ No newline at end of file diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index f4ddfc3da..000000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 2d9723a4f..000000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 642ebb531..000000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index 484ba9263..000000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index 65306cdce..000000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index b2731cea6..000000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 3c42ca42d..000000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index ffe110ab3..cbb5f4f06 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -11,4 +11,4 @@ PRODUCT_NAME = rpmlauncher PRODUCT_BUNDLE_IDENTIFIER = rpmtw.rpmlauncher // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2021-2022 The RPMTW Team. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2021-2023 The RPMTW Team. All rights reserved. diff --git a/packageing/AppImageBuilder.yml b/packageing/AppImageBuilder.yml index 988d075ce..7fcf92b98 100644 --- a/packageing/AppImageBuilder.yml +++ b/packageing/AppImageBuilder.yml @@ -11,7 +11,7 @@ AppDir: id: com.rpmtw.rpmlauncher name: RPMLauncher icon: rpmlauncher - version: 1.1.0 + version: 2.0.0 exec: RPMLauncher exec_args: $@ runtime: diff --git a/packageing/exe/app_icon.ico b/packageing/exe/app_icon.ico index d5f311b3f..01a900b16 100644 Binary files a/packageing/exe/app_icon.ico and b/packageing/exe/app_icon.ico differ diff --git a/packageing/exe/rpmlauncher.iss b/packageing/exe/rpmlauncher.iss index 73dd3569c..314a7c856 100644 --- a/packageing/exe/rpmlauncher.iss +++ b/packageing/exe/rpmlauncher.iss @@ -32,7 +32,7 @@ SolidCompression=yes WizardStyle=modern SetupIconFile="app_icon.ico" VersionInfoCompany=The RPMTW Team -VersionInfoCopyright="Copyright © The RPMTW Team 2021-2022 All Right Reserved." +VersionInfoCopyright="Copyright © The RPMTW Team 2021-2023 All Right Reserved." VersionInfoDescription="A better Minecraft Launcher that supports cross-platform and many functionalities for you to explore!" diff --git a/plugin/analysis_options.yaml b/packages/rpmlauncher_plugin/analysis_options.yaml similarity index 100% rename from plugin/analysis_options.yaml rename to packages/rpmlauncher_plugin/analysis_options.yaml diff --git a/plugin/lib/rpmlauncher_plugin.dart b/packages/rpmlauncher_plugin/lib/rpmlauncher_plugin.dart similarity index 100% rename from plugin/lib/rpmlauncher_plugin.dart rename to packages/rpmlauncher_plugin/lib/rpmlauncher_plugin.dart diff --git a/plugin/lib/src/model/memory_info.dart b/packages/rpmlauncher_plugin/lib/src/model/memory_info.dart similarity index 100% rename from plugin/lib/src/model/memory_info.dart rename to packages/rpmlauncher_plugin/lib/src/model/memory_info.dart diff --git a/plugin/lib/src/plugin.dart b/packages/rpmlauncher_plugin/lib/src/plugin.dart similarity index 100% rename from plugin/lib/src/plugin.dart rename to packages/rpmlauncher_plugin/lib/src/plugin.dart diff --git a/plugin/linux/CMakeLists.txt b/packages/rpmlauncher_plugin/linux/CMakeLists.txt similarity index 100% rename from plugin/linux/CMakeLists.txt rename to packages/rpmlauncher_plugin/linux/CMakeLists.txt diff --git a/plugin/linux/include/rpmlauncher_plugin/rpmlauncher_plugin.h b/packages/rpmlauncher_plugin/linux/include/rpmlauncher_plugin/rpmlauncher_plugin.h similarity index 100% rename from plugin/linux/include/rpmlauncher_plugin/rpmlauncher_plugin.h rename to packages/rpmlauncher_plugin/linux/include/rpmlauncher_plugin/rpmlauncher_plugin.h diff --git a/plugin/linux/rpmlauncher_plugin.cc b/packages/rpmlauncher_plugin/linux/rpmlauncher_plugin.cc similarity index 100% rename from plugin/linux/rpmlauncher_plugin.cc rename to packages/rpmlauncher_plugin/linux/rpmlauncher_plugin.cc diff --git a/plugin/macos/Classes/RpmlauncherPlugin.swift b/packages/rpmlauncher_plugin/macos/Classes/RpmlauncherPlugin.swift similarity index 100% rename from plugin/macos/Classes/RpmlauncherPlugin.swift rename to packages/rpmlauncher_plugin/macos/Classes/RpmlauncherPlugin.swift diff --git a/plugin/macos/rpmlauncher_plugin.podspec b/packages/rpmlauncher_plugin/macos/rpmlauncher_plugin.podspec similarity index 100% rename from plugin/macos/rpmlauncher_plugin.podspec rename to packages/rpmlauncher_plugin/macos/rpmlauncher_plugin.podspec diff --git a/plugin/pubspec.yaml b/packages/rpmlauncher_plugin/pubspec.yaml similarity index 100% rename from plugin/pubspec.yaml rename to packages/rpmlauncher_plugin/pubspec.yaml diff --git a/plugin/windows/.gitignore b/packages/rpmlauncher_plugin/windows/.gitignore similarity index 100% rename from plugin/windows/.gitignore rename to packages/rpmlauncher_plugin/windows/.gitignore diff --git a/plugin/windows/CMakeLists.txt b/packages/rpmlauncher_plugin/windows/CMakeLists.txt similarity index 100% rename from plugin/windows/CMakeLists.txt rename to packages/rpmlauncher_plugin/windows/CMakeLists.txt diff --git a/plugin/windows/include/rpmlauncher_plugin/rpmlauncher_plugin.h b/packages/rpmlauncher_plugin/windows/include/rpmlauncher_plugin/rpmlauncher_plugin.h similarity index 100% rename from plugin/windows/include/rpmlauncher_plugin/rpmlauncher_plugin.h rename to packages/rpmlauncher_plugin/windows/include/rpmlauncher_plugin/rpmlauncher_plugin.h diff --git a/plugin/windows/rpmlauncher_plugin.cpp b/packages/rpmlauncher_plugin/windows/rpmlauncher_plugin.cpp similarity index 100% rename from plugin/windows/rpmlauncher_plugin.cpp rename to packages/rpmlauncher_plugin/windows/rpmlauncher_plugin.cpp diff --git a/pubspec.lock b/pubspec.lock index 562f62956..1858c9c33 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,34 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "3444216bfd127af50bbe4862d8843ed44db946dd933554f0d7285e89f10e28ac" + sha256: "0c80aeab9bc807ab10022cd3b2f4cf2ecdf231949dc1ddd9442406a003f19201" url: "https://pub.dev" source: hosted - version: "50.0.0" - after_layout: - dependency: transitive - description: - name: after_layout - sha256: "95a1cb2ca1464f44f14769329fbf15987d20ab6c88f8fc5d359bd362be625f29" - url: "https://pub.dev" - source: hosted - version: "1.2.0" + version: "52.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "68796c31f510c8455a06fed75fc97d8e5ad04d324a830322ab3efc9feb6201c1" - url: "https://pub.dev" - source: hosted - version: "5.2.0" - animations: - dependency: transitive - description: - name: animations - sha256: fe8a6bdca435f718bb1dc8a11661b2c22504c6da40ef934cee8327ed77934164 + sha256: cd8ee83568a77f3ae6b913a36093a1c9b1264e7cb7f834d9ddd2311dade9c1f4 url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "5.4.0" archive: dependency: "direct main" description: @@ -57,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + blur: + dependency: "direct main" + description: + name: blur + sha256: fd23f1247faee4a7d1a3efb6b7c3cea134f3b939d72e5f8d45233deb0776259f + url: "https://pub.dev" + source: hosted + version: "3.1.0" boolean_selector: dependency: transitive description: @@ -101,10 +93,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "6f48c61a9dcd2c3a9e62d3dcdab1ba382790e2f31026288cbabe55d6003c9c23" + sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.3" build_runner_core: dependency: transitive description: @@ -125,10 +117,10 @@ packages: dependency: transitive description: name: built_value - sha256: "59e08b0079bb75f7e27392498e26339387c1089c6bd58525a14eb8508637277b" + sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" url: "https://pub.dev" source: hosted - version: "8.4.2" + version: "8.4.3" characters: dependency: transitive description: @@ -141,10 +133,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: dd007e4fb8270916820a0d66e24f619266b60773cddd082c6439341645af2659 + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" clock: dependency: transitive description: @@ -157,26 +149,18 @@ packages: dependency: transitive description: name: code_builder - sha256: "02ce3596b459c666530f045ad6f96209474e8fee6e4855940a3cee65fb872ec5" + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "4.4.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" - contextmenu: - dependency: "direct main" - description: - name: contextmenu - sha256: e0c7d60e2fc9f316f5b03f5fe2c0f977d65125345d1a1f77eea02be612e32d0c - url: "https://pub.dev" - source: hosted - version: "3.0.0" + version: "1.17.1" convert: dependency: transitive description: @@ -239,18 +223,26 @@ packages: dependency: "direct main" description: name: dio - sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" + sha256: "3709d74615bba5e443eb141f6a7f4bcc4788f8fae6f743edadfb79c2a8e6287e" url: "https://pub.dev" source: hosted - version: "4.0.6" - dynamic_themes: + version: "5.0.1" + dio_smart_retry: dependency: "direct main" description: - name: dynamic_themes - sha256: "35e285d3b61cd70a25bf1035e6192fc578d7b6c7b09b0cc99da0f5e2780651a9" + name: dio_smart_retry + sha256: "1a2d0cf73ab56bf5998b375cda2d51f45c77268e712e4073f232cdc7753a94b2" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "5.0.0" + dyn_mouse_scroll: + dependency: "direct main" + description: + name: dyn_mouse_scroll + sha256: "57890aed6fb19069bfbc5300f736a540ef23f8d91c956ead9c8f8e01060eb3e3" + url: "https://pub.dev" + source: hosted + version: "1.0.7" enum_to_string: dependency: transitive description: @@ -259,6 +251,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -299,6 +299,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: "155556c4aba56c8474308d51b4e5446b80a07db9ab66a3cb74aff05c110df982" + url: "https://pub.dev" + source: hosted + version: "0.60.0" flag: dependency: "direct main" description: @@ -325,14 +333,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_markdown: - dependency: "direct main" - description: - name: flutter_markdown - sha256: "981442432b632237ffc1cf8092b4173b9e9f2278b5740637287c3069b51c8f09" - url: "https://pub.dev" - source: hosted - version: "0.6.13" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -342,7 +342,7 @@ packages: source: hosted version: "2.0.7" flutter_svg: - dependency: transitive + dependency: "direct main" description: name: flutter_svg sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" @@ -408,7 +408,7 @@ packages: source: hosted version: "2.0.0" http: - dependency: "direct main" + dependency: transitive description: name: http sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" @@ -424,7 +424,7 @@ packages: source: hosted version: "3.2.1" http_parser: - dependency: transitive + dependency: "direct main" description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" @@ -435,10 +435,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.0" io: dependency: "direct main" description: @@ -451,18 +451,26 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: "3520fa844009431b5d4491a5a778603520cdc399ab3406332dcc50f93547258c" url: "https://pub.dev" source: hosted version: "4.7.0" + json_serializable: + dependency: "direct main" + description: + name: json_serializable + sha256: f3c2c18a7889580f71926f30c1937727c8c7d4f3a435f8f5e8b0ddd25253ef5d + url: "https://pub.dev" + source: hosted + version: "6.5.4" jwt_decoder: dependency: transitive description: @@ -503,22 +511,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - markdown: - dependency: transitive + lottie: + dependency: "direct main" description: - name: markdown - sha256: c2b81e184067b41d0264d514f7cdaa2c02d38511e39d6521a1ccc238f6d7b3f2 + name: lottie + sha256: "49bbc544e44bf0c734ccda29b182e3516a12f5021ea98b206cf31a168b0f97da" url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "2.2.0" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: c94db23593b89766cda57aab9ac311e3616cf87c6fa4e9749df032f66f30dcb8 url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.14" material_color_utilities: dependency: transitive description: @@ -528,21 +536,21 @@ packages: source: hosted version: "0.2.0" meta: - dependency: transitive + dependency: "direct main" description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.0" mime: dependency: transitive description: name: mime - sha256: "52e38f7e1143ef39daf532117d6b8f8f617bf4bcd6044ed8c29040d20d269630" + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" nested: dependency: transitive description: @@ -627,10 +635,10 @@ packages: dependency: "direct main" description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_drawing: dependency: transitive description: @@ -683,10 +691,10 @@ packages: dependency: transitive description: name: path_provider_macos - sha256: "2a97e7fbb7ae9dcd0dfc1220a78e9ec3e71da691912e617e8715ff2a13086ae8" + sha256: cd57cb98a30ce9d12fdd1896d9d3b0517ce689f942de6ccd2708cd39b3d18a7c url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.0.7" path_provider_platform_interface: dependency: transitive description: @@ -755,10 +763,10 @@ packages: dependency: "direct main" description: name: provider - sha256: e1e7413d70444ea3096815a60fe5da1b11bda8a9dc4769252cc82c53536f8bcc + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f url: "https://pub.dev" source: hosted - version: "6.0.4" + version: "6.0.5" pub_semver: dependency: "direct main" description: @@ -779,14 +787,14 @@ packages: dependency: "direct main" description: name: quiver - sha256: "93982981971e812c94d4a6fa3a57b89f9ec12b38b6380cd3c1370c3b01e4580e" + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.2.1" rpmlauncher_plugin: dependency: "direct main" description: - path: plugin + path: "packages/rpmlauncher_plugin" relative: true source: path version: "0.0.1" @@ -794,10 +802,10 @@ packages: dependency: "direct main" description: name: rpmtw_api_client - sha256: d72f830fec9fd8f676f61118962a746b56a89277fbb5443e7b0834e70bf446d8 + sha256: "4139c36c0f9cd2b3eebb587fb5e24d7195c6d1c32372cfb342a7ed247359e298" url: "https://pub.dev" source: hosted - version: "1.0.9+3" + version: "1.0.10" rpmtw_dart_common_library: dependency: "direct main" description: @@ -810,34 +818,42 @@ packages: dependency: transitive description: name: screen_retriever - sha256: b2d23e3cf48ca735fe9edb2dabe9e7e7c3d7acb5d645e382b9b8f5248dd0d165 + sha256: "9c3839c4eb80807cd8210afa3c84a177ba00aef9f9b7b74ad92d3a0ab1d7e7ed" url: "https://pub.dev" source: hosted - version: "0.1.4" + version: "0.1.5" sentry: dependency: transitive description: name: sentry - sha256: "939adf568c12e3bbe231250af12f0023ac9590d374137191ebb84c429cf1e816" + sha256: "34d95f1954160509a8db1ed6695348e52ccd5ec7d64fec426fbbe471d53f1eb8" + url: "https://pub.dev" + source: hosted + version: "7.0.0-rc.1" + sentry_dio: + dependency: "direct main" + description: + name: sentry_dio + sha256: "44b8169c372c0a45225c239b75ba4464512e60f41ea7f08f327bbf2e30be80bc" url: "https://pub.dev" source: hosted - version: "6.17.0" + version: "7.0.0-rc.1" sentry_flutter: dependency: "direct main" description: name: sentry_flutter - sha256: ac33a76c08ebd5cec0542a0e3614ff0b09a0e8744fb8a7ce65f98bcc510a56e8 + sha256: "51066bc86c9a5df444fc1bda40199fd4936e6bf3f5ed32608fe35bbb87585d78" url: "https://pub.dev" source: hosted - version: "6.17.0" + version: "7.0.0-rc.1" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "76917b7d4b9526b2ba416808a7eb9fb2863c1a09cf63ec85f1453da240fa818a" + sha256: "95688ad7fc320f8566f28e2ee91b6743c10b433ccc262f6469f3007f2aa62e78" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.16" shared_preferences_android: dependency: transitive description: @@ -846,11 +862,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.14" - shared_preferences_ios: + shared_preferences_foundation: dependency: transitive description: - name: shared_preferences_ios - sha256: "585a14cefec7da8c9c2fb8cd283a3bb726b4155c0952afe6a0caaa7b2272de34" + name: shared_preferences_foundation + sha256: f451880807c86a6d4591642c8250ee17197cabef8536f072d3c44013dca44e04 url: "https://pub.dev" source: hosted version: "2.1.1" @@ -858,18 +874,10 @@ packages: dependency: transitive description: name: shared_preferences_linux - sha256: "28aefc1261746e7bad3d09799496054beb84e8c4ffcdfed7734e17b4ada459a5" + sha256: fbc3cd6826896b66a5f576b025e4f344f780c84ea7f8203097a353370607a2c8 url: "https://pub.dev" source: hosted - version: "2.1.1" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - sha256: fbb94bf296576f49be37a1496d5951796211a8db0aa22cc0d68c46440dad808c - url: "https://pub.dev" - source: hosted - version: "2.0.4" + version: "2.1.2" shared_preferences_platform_interface: dependency: transitive description: @@ -890,10 +898,10 @@ packages: dependency: transitive description: name: shared_preferences_windows - sha256: "97f7ab9a7da96d9cf19581f5de520ceb529548498bd6b5e0ccd02d68a0d15eba" + sha256: "07c274c2115d4d5e4280622abb09f0980e2c5b1fcdc98ae9f59a3bad5bfc1f26" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" shelf: dependency: transitive description: @@ -915,14 +923,6 @@ packages: description: flutter source: sdk version: "0.0.99" - smooth_page_indicator: - dependency: "direct main" - description: - name: smooth_page_indicator - sha256: "49e9b6a265790454c39bd4a447a02f398c02b44b2602e7c5e3a381dc2e3b4102" - url: "https://pub.dev" - source: hosted - version: "1.0.0+2" socket_io_client: dependency: transitive description: @@ -963,14 +963,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" - split_view: - dependency: "direct main" - description: - name: split_view - sha256: "7ad0e1c40703901aa1175fd465dec5e965b55324f9cc8e51526479a4a96d01a4" - url: "https://pub.dev" - source: hosted - version: "3.2.1" stack_trace: dependency: transitive description: @@ -1003,6 +995,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: "direct main" + description: + name: synchronized + sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" + url: "https://pub.dev" + source: hosted + version: "3.0.1" term_glyph: dependency: transitive description: @@ -1015,10 +1015,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "6182294da5abf431177fccc1ee02401f6df30f766bc6130a0852c6b6d7ee6b2d" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.4.18" timing: dependency: transitive description: @@ -1027,14 +1027,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - toml: - dependency: "direct main" - description: - name: toml - sha256: "69756bc12eccf279b72217a87310d217efc4b3752f722e890f672801f19ac485" - url: "https://pub.dev" - source: hosted - version: "0.13.1" typed_data: dependency: transitive description: @@ -1143,10 +1135,10 @@ packages: dependency: transitive description: name: web_socket_channel - sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" + sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" win32: dependency: transitive description: @@ -1159,10 +1151,10 @@ packages: dependency: "direct main" description: name: window_manager - sha256: "5a7bda81b8b8e5feb733d07f4bd174e64dfb412031aff5314a375118e42b498e" + sha256: "5bdd29dc5f1f3185fc90696373a571d77968e03e5e820fb1ecdbdade3f5d8fff" url: "https://pub.dev" source: hosted - version: "0.2.8" + version: "0.3.0" window_size: dependency: "direct main" description: @@ -1176,10 +1168,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: "11541eedefbcaec9de35aa82650b695297ce668662bbd6e3911a7fabdbde589f" + sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 url: "https://pub.dev" source: hosted - version: "0.2.0+2" + version: "0.2.0+3" xml: dependency: transitive description: @@ -1197,5 +1189,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.18.0 <4.0.0" - flutter: ">=3.7.0-1.1.pre" + dart: ">=3.0.0-218.1.beta <4.0.0" + flutter: ">=3.8.0-10.1.pre" diff --git a/pubspec.yaml b/pubspec.yaml index a19499d5b..1ed4e1af7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,36 +3,33 @@ description: A better Minecraft Launcher that supports cross-platform and many f publish_to: "none" -version: 1.1.0 +version: 2.0.0 environment: - sdk: ">=2.17.0 <3.0.0" - flutter: ">=3.7.0-1.1.pre <4.0.0" + sdk: ">=3.0.0-218.1.beta <4.0.0" + flutter: ">=3.8.0-10.1.pre <4.0.0" dependencies: flutter: sdk: flutter + equatable: ^2.0.5 + json_annotation: ^4.7.0 + json_serializable: ^6.5.4 flutter_localizations: sdk: flutter - http: ^0.13.5 url_launcher: ^6.1.2 path_provider: ^2.0.10 - split_view: ^3.1.0 path: ^1.8.1 archive: ^3.3.1 crypto: ^3.0.2 oauth2: ^2.0.0 dart_minecraft: ^0.5.1 - dynamic_themes: ^1.1.0 - intl: ^0.17.0 - toml: ^0.13.1 + intl: ^0.18.0 line_icons: ^2.0.1 - flutter_markdown: ^0.6.10 - dio: ^4.0.6 + dio: ^5.0.1 no_context_navigation: ^2.1.2 flag: ^6.0.0 args: ^2.3.1 - contextmenu: ^3.0.0 provider: ^6.0.2 pub_semver: ^2.1.1 dart_big5: ^0.0.5 @@ -42,54 +39,103 @@ dependencies: path: plugins/window_size ref: a738913c8ce2c9f47515382d40827e794a334274 rpmlauncher_plugin: - path: plugin + path: packages/rpmlauncher_plugin io: ^1.0.3 watcher: ^1.0.1 - smooth_page_indicator: ^1.0.0+2 quiver: ^3.0.1 dart_discord_rpc: git: https://github.com/RPMTW/dart_discord_rpc.git uuid: ^3.0.6 - sentry_flutter: ^6.5.1 + sentry_flutter: ^7.0.0-rc.1 file_picker: ^4.5.1 rpmtw_dart_common_library: ^0.0.4 flutter_window_close: ^0.2.2 hive: ^2.1.0 - rpmtw_api_client: ^1.0.8-rc.9 + rpmtw_api_client: ^1.0.10 shared_preferences: ^2.0.15 - window_manager: ^0.2.8 + window_manager: ^0.3.0 + synchronized: ^3.0.0+3 + http_parser: ^4.0.2 + flutter_svg: ^1.1.6 + lottie: ^2.2.0 + blur: ^3.0.0 + sentry_dio: ^7.0.0-rc.1 + dyn_mouse_scroll: ^1.0.7 + meta: ^1.8.0 + dio_smart_retry: ^5.0.0 + fl_chart: ^0.60.0 dev_dependencies: - build_runner: ^2.1.10 + build_runner: ^2.3.3 flutter_lints: ^2.0.0 flutter_test: sdk: flutter hive_generator: ^2.0.0 - # msix: 2.3.0 flutter: uses-material-design: true assets: - assets/images/ + - assets/images/loader/ + - assets/images/versions/ - assets/lang/ fonts: - - family: font + # Source: https://fonts.google.com/specimen/Asap + - family: Asap fonts: - - asset: assets/fonts/NotoSansCJKtc-Regular.otf #Source: https://fonts.google.com/noto + - asset: assets/fonts/Asap/Asap-Thin.ttf + weight: 100 + - asset: assets/fonts/Asap/Asap-ThinItalic.ttf + weight: 100 + style: italic + - asset: assets/fonts/Asap/Asap-ExtraLight.ttf + weight: 200 + - asset: assets/fonts/Asap/Asap-ExtraLightItalic.ttf + weight: 200 + style: italic + - asset: assets/fonts/Asap/Asap-Light.ttf + weight: 300 + - asset: assets/fonts/Asap/Asap-LightItalic.ttf + weight: 300 + style: italic + - asset: assets/fonts/Asap/Asap-Regular.ttf + weight: 400 + - asset: assets/fonts/Asap/Asap-Italic.ttf + weight: 400 + style: italic + - asset: assets/fonts/Asap/Asap-Medium.ttf + weight: 500 + - asset: assets/fonts/Asap/Asap-MediumItalic.ttf + weight: 500 + style: italic + - asset: assets/fonts/Asap/Asap-SemiBold.ttf + weight: 600 + - asset: assets/fonts/Asap/Asap-SemiBoldItalic.ttf + weight: 600 + style: italic + - asset: assets/fonts/Asap/Asap-Bold.ttf + weight: 700 + - asset: assets/fonts/Asap/Asap-BoldItalic.ttf + weight: 700 + style: italic + - asset: assets/fonts/Asap/Asap-ExtraBold.ttf + weight: 800 + - asset: assets/fonts/Asap/Asap-ExtraBoldItalic.ttf + weight: 800 + style: italic + - asset: assets/fonts/Asap/Asap-Black.ttf + weight: 900 + - asset: assets/fonts/Asap/Asap-BlackItalic.ttf + weight: 900 + style: italic + + - family: GenJyuuGothic + fonts: + - asset: assets/fonts/GenJyuuGothic/GenJyuuGothic-Medium.ttf + weight: 500 + - asset: assets/fonts/GenJyuuGothic/GenJyuuGothic-Bold.ttf + weight: 700 + # - family: mono # fonts: # - asset: assets/fonts/NotoSansMono-Medium.ttf #Source: https://fonts.google.com/noto -# msix_config: -# display_name: RPMLauncher -# publisher_display_name: The RPMTW Team -# publisher: E=rpmtw666@gmail.com, CN=ga.rpmtw.rpmlauncher, OU=The RPMTW Team, O=The RPMTW Team, L=TW, S=TW, C=TW -# identity_name: ga.rpmtw.rpmlauncher -# logo_path: ./assets/images/Logo.png -# start_menu_icon_path: ./assets/images/Logo.png -# tile_icon_path: ./assets/images/Logo.png -# icons_background_color: "#000000" -# architecture: x64 -# capabilities: "internetClient,allowElevation,internetClientServer,sharedUserCertificates" -# certificate_path: ./windows/MsixInstaller/CERTIFICATE.pfx -# certificate_password: "rpmtw" -# languages: "zh-tw, zh-cn, zh-hk, en-us, ja-jp" diff --git a/scripts/UpdateJson/analysis_options.yml b/scripts/UpdateJson/analysis_options.yml index a8b2537e9..f9b303465 100644 --- a/scripts/UpdateJson/analysis_options.yml +++ b/scripts/UpdateJson/analysis_options.yml @@ -1,5 +1 @@ include: package:flutter_lints/flutter.yaml - -linter: - rules: - non_constant_identifier_names: false diff --git a/scripts/UpdateJson/bin/main.dart b/scripts/UpdateJson/bin/main.dart index f8d866d6b..1dc518d69 100644 --- a/scripts/UpdateJson/bin/main.dart +++ b/scripts/UpdateJson/bin/main.dart @@ -1,8 +1,8 @@ import 'dart:io'; -import 'package:http/http.dart'; import 'dart:convert'; import 'dart:core'; import 'package:args/args.dart'; +import 'package:http/http.dart'; void main(List args) async { File updateJsonFile = File('update.json'); @@ -54,11 +54,11 @@ void main(List args) async { updateJson['dev']['latest_build_id'] = buildID; updateJson['dev']['latest_version_full'] = '$version+$buildID'; } - + // 由於目前啟動器還不穩定,暫時兩個更新通道都一併更新 // if (type == 'stable') { - updateStable(); - updateDev(); + updateStable(); + updateDev(); // } else if (type == 'dev') { // updateDev(); // } diff --git a/scripts/UpdateJson/pubspec.lock b/scripts/UpdateJson/pubspec.lock index 573555eb2..bfbd98dc2 100644 --- a/scripts/UpdateJson/pubspec.lock +++ b/scripts/UpdateJson/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: "direct main" description: name: args - sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515 + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.0" async: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" flutter_lints: dependency: "direct dev" description: @@ -61,18 +61,18 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.0" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" source_span: dependency: transitive description: diff --git a/scripts/UpdateJson/pubspec.yaml b/scripts/UpdateJson/pubspec.yaml index 715c83dbe..03171a354 100644 --- a/scripts/UpdateJson/pubspec.yaml +++ b/scripts/UpdateJson/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: args: ^2.2.0 - http: ^0.13.3 + http: ^0.13.5 dev_dependencies: flutter_lints: ^1.0.4 diff --git a/scripts/UpdateJson/update.json b/scripts/UpdateJson/update.json deleted file mode 100644 index e05f2d619..000000000 --- a/scripts/UpdateJson/update.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "dev": { - "latest_version": "1.0.0", - "latest_version_code": "100" - }, - "stable": { - "latest_version": "1.0.0", - "latest_version_code": "0" - }, - "version_list": { - "1.0.0": { - "443": { - "download_url": { - "windows": "test", - "linux": "test", - "macos": "test" - }, - "changelog": "# 新增 \n# 修改 \n# 修正 \n# 移除", - "type": "dev" - }, - "0": { - "download_url": { - "windows": "test", - "linux": "test", - "macos": "test" - }, - "changelog": "", - "type": "stable" - }, - "100": { - "download_url": { - "windows_7": "https://github.com/RPMTW/RPMLauncher/releases/download/1.0.0.100/RPMLauncher-100-windows_7.zip", - "windows_10_11": "https://github.com/RPMTW/RPMLauncher/releases/download/1.0.0.100/RPMLauncher-100-windows_10_11.zip", - "linux": "https://github.com/RPMTW/RPMLauncher/releases/download/1.0.0.100/RPMLauncher-100-linux.zip", - "macos": "https://github.com/RPMTW/RPMLauncher/releases/download/1.0.0.100/RPMLauncher-100-macos.zip" - }, - "changelog": "測試", - "type": "dev" - } - } - } -} \ No newline at end of file diff --git a/snap/gui/rpmlauncher.png b/snap/gui/rpmlauncher.png index 2d9723a4f..f57fe0e0f 100644 Binary files a/snap/gui/rpmlauncher.png and b/snap/gui/rpmlauncher.png differ diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 12d4352ac..dc7747a18 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -7,12 +7,10 @@ description: A better Minecraft Launcher that supports cross-platform and many f # contact: https://discord.gg/5xApZtgV2u license: GPL-3.0 confinement: strict -base: core20 +base: core22 grade: stable apps: rpmlauncher: - command-chain: - - snap/command-chain/desktop-launch command: bin/RPMLauncher plugs: - network @@ -33,42 +31,19 @@ parts: plugin: dump build-packages: - git + stage-packages: + - libgtk-3-dev override-build: | - snapcraftctl build - chmod +x $SNAPCRAFT_PART_SRC/RPMLauncher-Linux/RPMLauncher + craftctl default + chmod +x $CRAFT_PART_SRC/RPMLauncher-Linux/RPMLauncher override-stage: | - snapcraftctl stage + craftctl default chmod +x bin/RPMLauncher organize: RPMLauncher-Linux: bin/ - gnome-3-38-extension: - source: "$SNAPCRAFT_EXTENSIONS_DIR/desktop" - source-subdir: "gnome" - plugin: "make" - make-parameters: ["PLATFORM_PLUG=gnome-3-38-2004"] - build-packages: ["gcc", "libgtk-3-dev"] assumes: - snapd2.43 -plugs: - gtk-3-themes: - interface: content - target: $SNAP/data-dir/themes - default-provider: gtk-common-themes - icon-themes: - interface: content - target: $SNAP/data-dir/icons - default-provider: gtk-common-themes - sound-themes: - interface: content - target: $SNAP/data-dir/sounds - default-provider: gtk-common-themes - gnome-3-38-2004: - interface: content - target: $SNAP/gnome-platform - default-provider: gnome-3-38-2004 -environment: - SNAP_DESKTOP_RUNTIME: "$SNAP/gnome-platform" layout: "/usr/share/xml/iso-codes": bind: "$SNAP/gnome-platform/usr/share/xml/iso-codes" diff --git a/test/data/1.19.3_version_meta.json b/test/data/1.19.3_version_meta.json new file mode 100644 index 000000000..7b6f4d4cb --- /dev/null +++ b/test/data/1.19.3_version_meta.json @@ -0,0 +1 @@ +{"arguments": {"game": ["--username", "${auth_player_name}", "--version", "${version_name}", "--gameDir", "${game_directory}", "--assetsDir", "${assets_root}", "--assetIndex", "${assets_index_name}", "--uuid", "${auth_uuid}", "--accessToken", "${auth_access_token}", "--clientId", "${clientid}", "--xuid", "${auth_xuid}", "--userType", "${user_type}", "--versionType", "${version_type}", {"rules": [{"action": "allow", "features": {"is_demo_user": true}}], "value": "--demo"}, {"rules": [{"action": "allow", "features": {"has_custom_resolution": true}}], "value": ["--width", "${resolution_width}", "--height", "${resolution_height}"]}], "jvm": [{"rules": [{"action": "allow", "os": {"name": "osx"}}], "value": ["-XstartOnFirstThread"]}, {"rules": [{"action": "allow", "os": {"name": "windows"}}], "value": "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump"}, {"rules": [{"action": "allow", "os": {"name": "windows", "version": "^10\\."}}], "value": ["-Dos.name=Windows 10", "-Dos.version=10.0"]}, {"rules": [{"action": "allow", "os": {"arch": "x86"}}], "value": "-Xss1M"}, "-Djava.library.path=${natives_directory}", "-Dminecraft.launcher.brand=${launcher_name}", "-Dminecraft.launcher.version=${launcher_version}", "-cp", "${classpath}"]}, "assetIndex": {"id": "2", "sha1": "c492375ded5da34b646b8c5c0842a0028bc69cec", "size": 390746, "totalSize": 548701912, "url": "https://piston-meta.mojang.com/v1/packages/c492375ded5da34b646b8c5c0842a0028bc69cec/2.json"}, "assets": "2", "complianceLevel": 1, "downloads": {"client": {"sha1": "977727ec9ab8b4631e5c12839f064092f17663f8", "size": 22704188, "url": "https://piston-data.mojang.com/v1/objects/977727ec9ab8b4631e5c12839f064092f17663f8/client.jar"}, "client_mappings": {"sha1": "42366909cc612e76208d34bf1356f05a88e08a1d", "size": 7574170, "url": "https://piston-data.mojang.com/v1/objects/42366909cc612e76208d34bf1356f05a88e08a1d/client.txt"}, "server": {"sha1": "c9df48efed58511cdd0213c56b9013a7b5c9ac1f", "size": 47162712, "url": "https://piston-data.mojang.com/v1/objects/c9df48efed58511cdd0213c56b9013a7b5c9ac1f/server.jar"}, "server_mappings": {"sha1": "bc44f6dd84cd2f3ad8c0caad850eaca9e82067e3", "size": 5852819, "url": "https://piston-data.mojang.com/v1/objects/bc44f6dd84cd2f3ad8c0caad850eaca9e82067e3/server.txt"}}, "id": "1.19.3", "javaVersion": {"component": "java-runtime-gamma", "majorVersion": 17}, "libraries": [{"downloads": {"artifact": {"path": "ca/weblite/java-objc-bridge/1.1/java-objc-bridge-1.1.jar", "sha1": "1227f9e0666314f9de41477e3ec277e542ed7f7b", "size": 1330045, "url": "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.1/java-objc-bridge-1.1.jar"}}, "name": "ca.weblite:java-objc-bridge:1.1", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "com/github/oshi/oshi-core/6.2.2/oshi-core-6.2.2.jar", "sha1": "54f5efc19bca95d709d9a37d19ffcbba3d21c1a6", "size": 947865, "url": "https://libraries.minecraft.net/com/github/oshi/oshi-core/6.2.2/oshi-core-6.2.2.jar"}}, "name": "com.github.oshi:oshi-core:6.2.2"}, {"downloads": {"artifact": {"path": "com/google/code/gson/gson/2.10/gson-2.10.jar", "sha1": "dd9b193aef96e973d5a11ab13cd17430c2e4306b", "size": 286235, "url": "https://libraries.minecraft.net/com/google/code/gson/gson/2.10/gson-2.10.jar"}}, "name": "com.google.code.gson:gson:2.10"}, {"downloads": {"artifact": {"path": "com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar", "sha1": "1dcf1de382a0bf95a3d8b0849546c88bac1292c9", "size": 4617, "url": "https://libraries.minecraft.net/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar"}}, "name": "com.google.guava:failureaccess:1.0.1"}, {"downloads": {"artifact": {"path": "com/google/guava/guava/31.1-jre/guava-31.1-jre.jar", "sha1": "60458f877d055d0c9114d9e1a2efb737b4bc282c", "size": 2959479, "url": "https://libraries.minecraft.net/com/google/guava/guava/31.1-jre/guava-31.1-jre.jar"}}, "name": "com.google.guava:guava:31.1-jre"}, {"downloads": {"artifact": {"path": "com/ibm/icu/icu4j/71.1/icu4j-71.1.jar", "sha1": "9e7d3304c23f9ba5cb71915f7cce23231a57a445", "size": 13963762, "url": "https://libraries.minecraft.net/com/ibm/icu/icu4j/71.1/icu4j-71.1.jar"}}, "name": "com.ibm.icu:icu4j:71.1"}, {"downloads": {"artifact": {"path": "com/mojang/authlib/3.16.29/authlib-3.16.29.jar", "sha1": "efcca7a2fd33a399287834787f3a899086752b14", "size": 117060, "url": "https://libraries.minecraft.net/com/mojang/authlib/3.16.29/authlib-3.16.29.jar"}}, "name": "com.mojang:authlib:3.16.29"}, {"downloads": {"artifact": {"path": "com/mojang/blocklist/1.0.10/blocklist-1.0.10.jar", "sha1": "5c685c5ffa94c4cd39496c7184c1d122e515ecef", "size": 964, "url": "https://libraries.minecraft.net/com/mojang/blocklist/1.0.10/blocklist-1.0.10.jar"}}, "name": "com.mojang:blocklist:1.0.10"}, {"downloads": {"artifact": {"path": "com/mojang/brigadier/1.0.18/brigadier-1.0.18.jar", "sha1": "c1ef1234282716483c92183f49bef47b1a89bfa9", "size": 77116, "url": "https://libraries.minecraft.net/com/mojang/brigadier/1.0.18/brigadier-1.0.18.jar"}}, "name": "com.mojang:brigadier:1.0.18"}, {"downloads": {"artifact": {"path": "com/mojang/datafixerupper/5.0.28/datafixerupper-5.0.28.jar", "sha1": "e2157e236e529aff80a5fc3ccb506e56d46b130b", "size": 684966, "url": "https://libraries.minecraft.net/com/mojang/datafixerupper/5.0.28/datafixerupper-5.0.28.jar"}}, "name": "com.mojang:datafixerupper:5.0.28"}, {"downloads": {"artifact": {"path": "com/mojang/javabridge/2.0.25/javabridge-2.0.25.jar", "sha1": "bfa589b0bd71588fa7e594640114b0cda0cff5f6", "size": 5884, "url": "https://libraries.minecraft.net/com/mojang/javabridge/2.0.25/javabridge-2.0.25.jar"}}, "name": "com.mojang:javabridge:2.0.25"}, {"downloads": {"artifact": {"path": "com/mojang/logging/1.1.1/logging-1.1.1.jar", "sha1": "832b8e6674a9b325a5175a3a6267dfaf34c85139", "size": 15343, "url": "https://libraries.minecraft.net/com/mojang/logging/1.1.1/logging-1.1.1.jar"}}, "name": "com.mojang:logging:1.1.1"}, {"downloads": {"artifact": {"path": "com/mojang/patchy/2.2.10/patchy-2.2.10.jar", "sha1": "da05971b07cbb379d002cf7eaec6a2048211fefc", "size": 4439, "url": "https://libraries.minecraft.net/com/mojang/patchy/2.2.10/patchy-2.2.10.jar"}}, "name": "com.mojang:patchy:2.2.10"}, {"downloads": {"artifact": {"path": "com/mojang/text2speech/1.13.9/text2speech-1.13.9.jar", "sha1": "5f4e3a6ef86cb021f7ca87ca192cddb50c26eb59", "size": 12123, "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.13.9/text2speech-1.13.9.jar"}}, "name": "com.mojang:text2speech:1.13.9"}, {"downloads": {"artifact": {"path": "com/mojang/text2speech/1.13.9/text2speech-1.13.9-natives-linux.jar", "sha1": "6c63ecb3b6408dcfdde6440c9ee62c060542af33", "size": 7833, "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.13.9/text2speech-1.13.9-natives-linux.jar"}}, "name": "com.mojang:text2speech:1.13.9:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "com/mojang/text2speech/1.13.9/text2speech-1.13.9-natives-windows.jar", "sha1": "7a90898b29e5c72f90ba6ebe86fa78a6afd7d3eb", "size": 81379, "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.13.9/text2speech-1.13.9-natives-windows.jar"}}, "name": "com.mojang:text2speech:1.13.9:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "commons-codec/commons-codec/1.15/commons-codec-1.15.jar", "sha1": "49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d", "size": 353793, "url": "https://libraries.minecraft.net/commons-codec/commons-codec/1.15/commons-codec-1.15.jar"}}, "name": "commons-codec:commons-codec:1.15"}, {"downloads": {"artifact": {"path": "commons-io/commons-io/2.11.0/commons-io-2.11.0.jar", "sha1": "a2503f302b11ebde7ebc3df41daebe0e4eea3689", "size": 327135, "url": "https://libraries.minecraft.net/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar"}}, "name": "commons-io:commons-io:2.11.0"}, {"downloads": {"artifact": {"path": "commons-logging/commons-logging/1.2/commons-logging-1.2.jar", "sha1": "4bfc12adfe4842bf07b657f0369c4cb522955686", "size": 61829, "url": "https://libraries.minecraft.net/commons-logging/commons-logging/1.2/commons-logging-1.2.jar"}}, "name": "commons-logging:commons-logging:1.2"}, {"downloads": {"artifact": {"path": "io/netty/netty-buffer/4.1.82.Final/netty-buffer-4.1.82.Final.jar", "sha1": "a544270cf1ae8b8077082f5036436a9a9971ea71", "size": 304664, "url": "https://libraries.minecraft.net/io/netty/netty-buffer/4.1.82.Final/netty-buffer-4.1.82.Final.jar"}}, "name": "io.netty:netty-buffer:4.1.82.Final"}, {"downloads": {"artifact": {"path": "io/netty/netty-codec/4.1.82.Final/netty-codec-4.1.82.Final.jar", "sha1": "b77200379acb345a9ffdece1c605e591ac3e4e0a", "size": 339155, "url": "https://libraries.minecraft.net/io/netty/netty-codec/4.1.82.Final/netty-codec-4.1.82.Final.jar"}}, "name": "io.netty:netty-codec:4.1.82.Final"}, {"downloads": {"artifact": {"path": "io/netty/netty-common/4.1.82.Final/netty-common-4.1.82.Final.jar", "sha1": "022d148e85c3f5ebdacc0ce1f5aabb1d420f73f3", "size": 653880, "url": "https://libraries.minecraft.net/io/netty/netty-common/4.1.82.Final/netty-common-4.1.82.Final.jar"}}, "name": "io.netty:netty-common:4.1.82.Final"}, {"downloads": {"artifact": {"path": "io/netty/netty-handler/4.1.82.Final/netty-handler-4.1.82.Final.jar", "sha1": "644041d1fa96a5d3130a29e8978630d716d76e38", "size": 538569, "url": "https://libraries.minecraft.net/io/netty/netty-handler/4.1.82.Final/netty-handler-4.1.82.Final.jar"}}, "name": "io.netty:netty-handler:4.1.82.Final"}, {"downloads": {"artifact": {"path": "io/netty/netty-resolver/4.1.82.Final/netty-resolver-4.1.82.Final.jar", "sha1": "38f665ae8dcd29032eea31245ba7806bed2e0fa8", "size": 37776, "url": "https://libraries.minecraft.net/io/netty/netty-resolver/4.1.82.Final/netty-resolver-4.1.82.Final.jar"}}, "name": "io.netty:netty-resolver:4.1.82.Final"}, {"downloads": {"artifact": {"path": "io/netty/netty-transport-classes-epoll/4.1.82.Final/netty-transport-classes-epoll-4.1.82.Final.jar", "sha1": "e7c7dd18deac93105797f30057c912651ea76521", "size": 142066, "url": "https://libraries.minecraft.net/io/netty/netty-transport-classes-epoll/4.1.82.Final/netty-transport-classes-epoll-4.1.82.Final.jar"}}, "name": "io.netty:netty-transport-classes-epoll:4.1.82.Final"}, {"downloads": {"artifact": {"path": "io/netty/netty-transport-native-epoll/4.1.82.Final/netty-transport-native-epoll-4.1.82.Final-linux-aarch_64.jar", "sha1": "476409d6255001ca53a55f65b01c13822f8dc93a", "size": 39489, "url": "https://libraries.minecraft.net/io/netty/netty-transport-native-epoll/4.1.82.Final/netty-transport-native-epoll-4.1.82.Final-linux-aarch_64.jar"}}, "name": "io.netty:netty-transport-native-epoll:4.1.82.Final:linux-aarch_64", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "io/netty/netty-transport-native-epoll/4.1.82.Final/netty-transport-native-epoll-4.1.82.Final-linux-x86_64.jar", "sha1": "c7350a71920f3ae9142945e25fed4846cce53374", "size": 37922, "url": "https://libraries.minecraft.net/io/netty/netty-transport-native-epoll/4.1.82.Final/netty-transport-native-epoll-4.1.82.Final-linux-x86_64.jar"}}, "name": "io.netty:netty-transport-native-epoll:4.1.82.Final:linux-x86_64", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "io/netty/netty-transport-native-unix-common/4.1.82.Final/netty-transport-native-unix-common-4.1.82.Final.jar", "sha1": "3e895b35ca1b8a0eca56cacff4c2dde5d2c6abce", "size": 43684, "url": "https://libraries.minecraft.net/io/netty/netty-transport-native-unix-common/4.1.82.Final/netty-transport-native-unix-common-4.1.82.Final.jar"}}, "name": "io.netty:netty-transport-native-unix-common:4.1.82.Final"}, {"downloads": {"artifact": {"path": "io/netty/netty-transport/4.1.82.Final/netty-transport-4.1.82.Final.jar", "sha1": "e431a218d91acb6476ccad5f5aafde50aa3945ca", "size": 485752, "url": "https://libraries.minecraft.net/io/netty/netty-transport/4.1.82.Final/netty-transport-4.1.82.Final.jar"}}, "name": "io.netty:netty-transport:4.1.82.Final"}, {"downloads": {"artifact": {"path": "it/unimi/dsi/fastutil/8.5.9/fastutil-8.5.9.jar", "sha1": "bb7ea75ecdb216654237830b3a96d87ad91f8cc5", "size": 23376043, "url": "https://libraries.minecraft.net/it/unimi/dsi/fastutil/8.5.9/fastutil-8.5.9.jar"}}, "name": "it.unimi.dsi:fastutil:8.5.9"}, {"downloads": {"artifact": {"path": "net/java/dev/jna/jna-platform/5.12.1/jna-platform-5.12.1.jar", "sha1": "097406a297c852f4a41e688a176ec675f72e8329", "size": 1356627, "url": "https://libraries.minecraft.net/net/java/dev/jna/jna-platform/5.12.1/jna-platform-5.12.1.jar"}}, "name": "net.java.dev.jna:jna-platform:5.12.1"}, {"downloads": {"artifact": {"path": "net/java/dev/jna/jna/5.12.1/jna-5.12.1.jar", "sha1": "b1e93a735caea94f503e95e6fe79bf9cdc1e985d", "size": 1866196, "url": "https://libraries.minecraft.net/net/java/dev/jna/jna/5.12.1/jna-5.12.1.jar"}}, "name": "net.java.dev.jna:jna:5.12.1"}, {"downloads": {"artifact": {"path": "net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar", "sha1": "4fdac2fbe92dfad86aa6e9301736f6b4342a3f5c", "size": 78146, "url": "https://libraries.minecraft.net/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar"}}, "name": "net.sf.jopt-simple:jopt-simple:5.0.4"}, {"downloads": {"artifact": {"path": "org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar", "sha1": "4ec95b60d4e86b5c95a0e919cb172a0af98011ef", "size": 1018316, "url": "https://libraries.minecraft.net/org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar"}}, "name": "org.apache.commons:commons-compress:1.21"}, {"downloads": {"artifact": {"path": "org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar", "sha1": "c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e", "size": 587402, "url": "https://libraries.minecraft.net/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar"}}, "name": "org.apache.commons:commons-lang3:3.12.0"}, {"downloads": {"artifact": {"path": "org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar", "sha1": "e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada", "size": 780321, "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar"}}, "name": "org.apache.httpcomponents:httpclient:4.5.13"}, {"downloads": {"artifact": {"path": "org/apache/httpcomponents/httpcore/4.4.15/httpcore-4.4.15.jar", "sha1": "7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d", "size": 328324, "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpcore/4.4.15/httpcore-4.4.15.jar"}}, "name": "org.apache.httpcomponents:httpcore:4.4.15"}, {"downloads": {"artifact": {"path": "org/apache/logging/log4j/log4j-api/2.19.0/log4j-api-2.19.0.jar", "sha1": "ea1b37f38c327596b216542bc636cfdc0b8036fa", "size": 317566, "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-api/2.19.0/log4j-api-2.19.0.jar"}}, "name": "org.apache.logging.log4j:log4j-api:2.19.0"}, {"downloads": {"artifact": {"path": "org/apache/logging/log4j/log4j-core/2.19.0/log4j-core-2.19.0.jar", "sha1": "3b6eeb4de4c49c0fe38a4ee27188ff5fee44d0bb", "size": 1864386, "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-core/2.19.0/log4j-core-2.19.0.jar"}}, "name": "org.apache.logging.log4j:log4j-core:2.19.0"}, {"downloads": {"artifact": {"path": "org/apache/logging/log4j/log4j-slf4j2-impl/2.19.0/log4j-slf4j2-impl-2.19.0.jar", "sha1": "5c04bfdd63ce9dceb2e284b81e96b6a70010ee10", "size": 27721, "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-slf4j2-impl/2.19.0/log4j-slf4j2-impl-2.19.0.jar"}}, "name": "org.apache.logging.log4j:log4j-slf4j2-impl:2.19.0"}, {"downloads": {"artifact": {"path": "org/joml/joml/1.10.5/joml-1.10.5.jar", "sha1": "22566d58af70ad3d72308bab63b8339906deb649", "size": 712082, "url": "https://libraries.minecraft.net/org/joml/joml/1.10.5/joml-1.10.5.jar"}}, "name": "org.joml:joml:1.10.5"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar", "sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0", "size": 128801, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar"}}, "name": "org.lwjgl:lwjgl-glfw:3.3.1"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux.jar", "sha1": "81716978214ecbda15050ca394b06ef61501a49e", "size": 119817, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux.jar"}}, "name": "org.lwjgl:lwjgl-glfw:3.3.1:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos.jar", "sha1": "9ec4ce1fc8c85fdef03ef4ff2aace6f5775fb280", "size": 131655, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos.jar"}}, "name": "org.lwjgl:lwjgl-glfw:3.3.1:natives-macos", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar", "sha1": "cac0d3f712a3da7641fa174735a5f315de7ffe0a", "size": 129077, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-macos-arm64.jar"}}, "name": "org.lwjgl:lwjgl-glfw:3.3.1:natives-macos-arm64", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-windows.jar", "sha1": "ed892f945cf7e79c8756796f32d00fa4ceaf573b", "size": 145512, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-windows.jar"}}, "name": "org.lwjgl:lwjgl-glfw:3.3.1:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-windows-x86.jar", "sha1": "b997e3391d6ce8f05487e7335d95c606043884a1", "size": 139251, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-windows-x86.jar"}}, "name": "org.lwjgl:lwjgl-glfw:3.3.1:natives-windows-x86", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", "sha1": "a817bcf213db49f710603677457567c37d53e103", "size": 36601, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar"}}, "name": "org.lwjgl:lwjgl-jemalloc:3.3.1"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-linux.jar", "sha1": "33dbb017b6ed6b25f993ad9d56741a49e7937718", "size": 166524, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-linux.jar"}}, "name": "org.lwjgl:lwjgl-jemalloc:3.3.1:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos.jar", "sha1": "56424dc8db3cfb8e7b594aa6d59a4f4387b7f544", "size": 117480, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos.jar"}}, "name": "org.lwjgl:lwjgl-jemalloc:3.3.1:natives-macos", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar", "sha1": "e577b87d8ad2ade361aaea2fcf226c660b15dee8", "size": 103475, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-macos-arm64.jar"}}, "name": "org.lwjgl:lwjgl-jemalloc:3.3.1:natives-macos-arm64", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-windows.jar", "sha1": "948a89b76a16aa324b046ae9308891216ffce5f9", "size": 135585, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-windows.jar"}}, "name": "org.lwjgl:lwjgl-jemalloc:3.3.1:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-windows-x86.jar", "sha1": "fb476c8ec110e1c137ad3ce8a7f7bfe6b11c6324", "size": 110405, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-windows-x86.jar"}}, "name": "org.lwjgl:lwjgl-jemalloc:3.3.1:natives-windows-x86", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", "sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", "size": 88237, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar"}}, "name": "org.lwjgl:lwjgl-openal:3.3.1"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux.jar", "sha1": "f906b6439f6daa66001182ea7727e3467a93681b", "size": 476825, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux.jar"}}, "name": "org.lwjgl:lwjgl-openal:3.3.1:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos.jar", "sha1": "3a57b8911835fb58b5e558d0ca0d28157e263d45", "size": 397196, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos.jar"}}, "name": "org.lwjgl:lwjgl-openal:3.3.1:natives-macos", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar", "sha1": "23d55e7490b57495320f6c9e1936d78fd72c4ef8", "size": 346125, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-macos-arm64.jar"}}, "name": "org.lwjgl:lwjgl-openal:3.3.1:natives-macos-arm64", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-windows.jar", "sha1": "30a474d0e57193d7bc128849a3ab66bc9316fdb1", "size": 576872, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-windows.jar"}}, "name": "org.lwjgl:lwjgl-openal:3.3.1:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-windows-x86.jar", "sha1": "888349f7b1be6fbae58bf8edfb9ef12def04c4e3", "size": 520313, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-windows-x86.jar"}}, "name": "org.lwjgl:lwjgl-openal:3.3.1:natives-windows-x86", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", "sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411", "size": 921563, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar"}}, "name": "org.lwjgl:lwjgl-opengl:3.3.1"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux.jar", "sha1": "ab9ab6fde3743e3550fa5d46d785ecb45b047d99", "size": 79125, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux.jar"}}, "name": "org.lwjgl:lwjgl-opengl:3.3.1:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos.jar", "sha1": "a0d12697ea019a7362eff26475b0531340e876a6", "size": 40709, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos.jar"}}, "name": "org.lwjgl:lwjgl-opengl:3.3.1:natives-macos", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar", "sha1": "eafe34b871d966292e8db0f1f3d6b8b110d4e91d", "size": 41665, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-macos-arm64.jar"}}, "name": "org.lwjgl:lwjgl-opengl:3.3.1:natives-macos-arm64", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-windows.jar", "sha1": "c1807e9bd571402787d7e37e3029776ae2513bb8", "size": 100205, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-windows.jar"}}, "name": "org.lwjgl:lwjgl-opengl:3.3.1:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-windows-x86.jar", "sha1": "deef3eb9b178ff2ff3ce893cc72ae741c3a17974", "size": 87463, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-windows-x86.jar"}}, "name": "org.lwjgl:lwjgl-opengl:3.3.1:natives-windows-x86", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", "sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f", "size": 112380, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar"}}, "name": "org.lwjgl:lwjgl-stb:3.3.1"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux.jar", "sha1": "3ee7aec8686e52867677110415566a5342a80230", "size": 227237, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux.jar"}}, "name": "org.lwjgl:lwjgl-stb:3.3.1:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos.jar", "sha1": "def8879b8d38a47a4cc1d48b1f9a7b772e51258e", "size": 203582, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos.jar"}}, "name": "org.lwjgl:lwjgl-stb:3.3.1:natives-macos", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar", "sha1": "fcf073ed911752abdca5f0b00a53cfdf17ff8e8b", "size": 178408, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-macos-arm64.jar"}}, "name": "org.lwjgl:lwjgl-stb:3.3.1:natives-macos-arm64", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-windows.jar", "sha1": "86315914ac119efdb02dc9e8e978ade84f1702af", "size": 256301, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-windows.jar"}}, "name": "org.lwjgl:lwjgl-stb:3.3.1:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-windows-x86.jar", "sha1": "a8d41f419eecb430b7c91ea2ce2c5c451cae2091", "size": 225147, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-windows-x86.jar"}}, "name": "org.lwjgl:lwjgl-stb:3.3.1:natives-windows-x86", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", "sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", "size": 6767, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar"}}, "name": "org.lwjgl:lwjgl-tinyfd:3.3.1"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-linux.jar", "sha1": "a35110b9471bea8cde826ab297550ee8c22f5035", "size": 45389, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-linux.jar"}}, "name": "org.lwjgl:lwjgl-tinyfd:3.3.1:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos.jar", "sha1": "78641a0fa5e9afa714adfdd152c357930c97a1ce", "size": 44821, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos.jar"}}, "name": "org.lwjgl:lwjgl-tinyfd:3.3.1:natives-macos", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar", "sha1": "972ecc17bad3571e81162153077b4d47b7b9eaa9", "size": 41380, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-macos-arm64.jar"}}, "name": "org.lwjgl:lwjgl-tinyfd:3.3.1:natives-macos-arm64", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-windows.jar", "sha1": "a5d830475ec0958d9fdba1559efa99aef211e6ff", "size": 127930, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-windows.jar"}}, "name": "org.lwjgl:lwjgl-tinyfd:3.3.1:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-windows-x86.jar", "sha1": "842eedd876fae354abc308c98a263f6bbc9e8a4d", "size": 110042, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-windows-x86.jar"}}, "name": "org.lwjgl:lwjgl-tinyfd:3.3.1:natives-windows-x86", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", "sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb", "size": 724243, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar"}}, "name": "org.lwjgl:lwjgl:3.3.1"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux.jar", "sha1": "1de885aba434f934201b99f2f1afb142036ac189", "size": 110704, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux.jar"}}, "name": "org.lwjgl:lwjgl:3.3.1:natives-linux", "rules": [{"action": "allow", "os": {"name": "linux"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos.jar", "sha1": "fc6bb723dec2cd031557dccb2a95f0ab80acb9db", "size": 55706, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos.jar"}}, "name": "org.lwjgl:lwjgl:3.3.1:natives-macos", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar", "sha1": "71d0d5e469c9c95351eb949064497e3391616ac9", "size": 42693, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-macos-arm64.jar"}}, "name": "org.lwjgl:lwjgl:3.3.1:natives-macos-arm64", "rules": [{"action": "allow", "os": {"name": "osx"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-windows.jar", "sha1": "0036c37f16ab611b3aa11f3bcf80b1d509b4ce6b", "size": 159361, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-windows.jar"}}, "name": "org.lwjgl:lwjgl:3.3.1:natives-windows", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-windows-x86.jar", "sha1": "3b14f4beae9dd39791ec9e12190a9380cd8a3ce6", "size": 134695, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-windows-x86.jar"}}, "name": "org.lwjgl:lwjgl:3.3.1:natives-windows-x86", "rules": [{"action": "allow", "os": {"name": "windows"}}]}, {"downloads": {"artifact": {"path": "org/slf4j/slf4j-api/2.0.1/slf4j-api-2.0.1.jar", "sha1": "f48d81adce2abf5ad3cfe463df517952749e03bc", "size": 61388, "url": "https://libraries.minecraft.net/org/slf4j/slf4j-api/2.0.1/slf4j-api-2.0.1.jar"}}, "name": "org.slf4j:slf4j-api:2.0.1"}], "logging": {"client": {"argument": "-Dlog4j.configurationFile=${path}", "file": {"id": "client-1.12.xml", "sha1": "bd65e7d2e3c237be76cfbef4c2405033d7f91521", "size": 888, "url": "https://launcher.mojang.com/v1/objects/bd65e7d2e3c237be76cfbef4c2405033d7f91521/client-1.12.xml"}, "type": "log4j2-xml"}}, "mainClass": "net.minecraft.client.main.Main", "minimumLauncherVersion": 21, "releaseTime": "2022-12-07T08:17:18+00:00", "time": "2022-12-07T08:17:18+00:00", "type": "release"} \ No newline at end of file diff --git a/test/data/CurseForge-MCVersion.json b/test/data/CurseForge-MCVersion.json deleted file mode 100644 index 13c046729..000000000 --- a/test/data/CurseForge-MCVersion.json +++ /dev/null @@ -1,878 +0,0 @@ -[ - { - "id": 74, - "gameVersionId": 8857, - "versionString": "1.18.1", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/7e46fb47609401970e2818989fa584fd467cd036/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/b0bdc637e4c4cbf0501500cbaad5a757b04848ed/1.18.1.json", - "approved": false, - "dateModified": "2021-12-10T10:37:38.09Z", - "gameVersionTypeId": 73250, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 73, - "gameVersionId": 8830, - "versionString": "1.18", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/d49eb6caed53d23927648c97451503442f9e26fd/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/cdd1c0f485c0ea5a5aae60d4e62d316b2141f227/1.18.json", - "approved": false, - "dateModified": "2021-11-30T16:23:01.443Z", - "gameVersionTypeId": 73250, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 72, - "gameVersionId": 8516, - "versionString": "1.17.1", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/8d9b65467c7913fcf6f5b2e729d44a1e00fde150/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/8b976413591b4132fc4f27370dcd87ce1e50fb2f/1.17.1.json", - "approved": false, - "dateModified": "2021-07-06T14:16:03.99Z", - "gameVersionTypeId": 73242, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 71, - "gameVersionId": 8152, - "versionString": "1.17", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/1cf89c77ed5e72401b869f66410934804f3d6f52/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/44fa141917df947ed5a138f5cfe667a34f7bbaca/1.17.json", - "approved": false, - "dateModified": "2021-06-08T16:02:58.507Z", - "gameVersionTypeId": 73242, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 70, - "gameVersionId": 8203, - "versionString": "1.16.5", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/37fd3c903861eeff3bc24b71eed48f828b5269c8/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/436877ffaef948954053e1a78a366b8b7c204a91/1.16.5.json", - "approved": false, - "dateModified": "2021-01-15T14:14:48.927Z", - "gameVersionTypeId": 70886, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 69, - "gameVersionId": 8134, - "versionString": "1.16.4", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/1952d94a0784e7abda230aae6a1e8fc0522dba99/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/e54eda49b4a3b6dc407b952e494d0c32da422693/1.16.4.json", - "approved": false, - "dateModified": "2020-11-02T18:40:51.52Z", - "gameVersionTypeId": 70886, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 68, - "gameVersionId": 8056, - "versionString": "1.16.3", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/1321521b2caf934f7fc9665aab7e059a7b2bfcdf/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/0b83240b80de22bdd47d93d2a0ed6f45bbd2779f/1.16.3.json", - "approved": false, - "dateModified": "2020-09-10T14:44:20.46Z", - "gameVersionTypeId": 70886, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 67, - "gameVersionId": 8010, - "versionString": "1.16.2", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/653e97a2d1d76f87653f02242d243cdee48a5144/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/cc2ec1c0011fc8375a0d3527a375766fbffb89b5/1.16.2.json", - "approved": false, - "dateModified": "2020-08-11T16:42:21.887Z", - "gameVersionTypeId": 70886, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 66, - "gameVersionId": 7892, - "versionString": "1.16.1", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/c9abbe8ee4fa490751ca70635340b7cf00db83ff/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/6f3e929c1cb637fae53e255eb0883dda245d9a6f/1.16.1.json", - "approved": false, - "dateModified": "2020-06-24T12:41:11.853Z", - "gameVersionTypeId": 70886, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 65, - "gameVersionId": 7890, - "versionString": "1.16", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/d3b551b100080ea7943e653268bb7f5a479ce618/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/eeb5dedefe2ee986aa1a28fae226109861233c53/1.16.json", - "approved": false, - "dateModified": "2020-06-23T13:41:08.773Z", - "gameVersionTypeId": 70886, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 64, - "gameVersionId": 7722, - "versionString": "1.15.2", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/e3f78cd16f9eb9a52307ed96ebec64241cc5b32d/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/7a959e36be42526415e7da01312b3ce9de3b9d3a/1.15.2.json", - "approved": false, - "dateModified": "2020-01-21T15:28:39.183Z", - "gameVersionTypeId": 68722, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 63, - "gameVersionId": 7675, - "versionString": "1.15.1", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/8b11614bea9293592a947ea8f4fd72981ea66677/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/57c1b5f7376febf269f7e555f96def8f1a43bb76/1.15.1.json", - "approved": false, - "dateModified": "2019-12-17T13:27:03.323Z", - "gameVersionTypeId": 68722, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 62, - "gameVersionId": 7664, - "versionString": "1.15", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/7b07fd09d1e3aae1bc7a1304fedc73bfe5d81800/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/b1019338dba45caeba48d3bd5d798f885f92372b/1.15.json", - "approved": false, - "dateModified": "2019-12-10T15:26:51.073Z", - "gameVersionTypeId": 68722, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 61, - "gameVersionId": 7469, - "versionString": "1.14.4", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/8c325a0c5bd674dd747d6ebaa4c791fd363ad8a9/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/132979c36455cc1e17e5f9cc767b4e13c6947033/1.14.4.json", - "approved": false, - "dateModified": "2019-07-19T10:47:20.883Z", - "gameVersionTypeId": 64806, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 60, - "gameVersionId": 7413, - "versionString": "1.14.3", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/af100b34ec7ef2b8b9cf7775b544d21d690dddec/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/2b9823a8699fb6811acd1f553c5bee009d30f64e/1.14.3.json", - "approved": false, - "dateModified": "2019-06-24T15:46:57Z", - "gameVersionTypeId": 64806, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 59, - "gameVersionId": 7361, - "versionString": "1.14.2", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/ca6c5a139045967229975c0c0b7f93e78b4314c2/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/8b96abed06b23d3b752e653ada36062d70bf3da1/1.14.2.json", - "approved": false, - "dateModified": "2019-05-27T12:46:26.823Z", - "gameVersionTypeId": 64806, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 58, - "gameVersionId": 7344, - "versionString": "1.14.1", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/55ba86ddcbc3579397f41910463ffd4056e1e523/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/965bea1177bb974be2a0f7ee98ed732c0ab010b0/1.14.1.json", - "approved": false, - "dateModified": "2019-05-13T12:46:10.84Z", - "gameVersionTypeId": 64806, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 57, - "gameVersionId": 7318, - "versionString": "1.14", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/7a762a59345c13af7d87111207a93f5a8607f6c0/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/d3c54d6469145f7c918537209660bf20e9207057/1.14.json", - "approved": false, - "dateModified": "2019-04-23T15:20:21.687Z", - "gameVersionTypeId": 64806, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 56, - "gameVersionId": 7132, - "versionString": "1.13.2", - "jarDownloadUrl": "https://launcher.mojang.com/v1/objects/30bfe37a8db404db11c7edf02cb5165817afb4d9/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/v1/packages/081135896bfc24424033c1ec0b2e5d3c69c5f764/1.13.2.json", - "approved": false, - "dateModified": "2018-10-22T12:49:51Z", - "gameVersionTypeId": 55023, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 55, - "gameVersionId": 7107, - "versionString": "1.13.1", - "jarDownloadUrl": "https://launcher.mojang.com/mc/game/1.13.1/client/8de235e5ec3a7fce168056ea395d21cbdec18d7c/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/mc/game/6ff0d266e92bd52e97e1cc4adf339b1445870141/1.13.1.json", - "approved": false, - "dateModified": "2018-08-22T16:23:39.03Z", - "gameVersionTypeId": 55023, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 54, - "gameVersionId": 7081, - "versionString": "1.13", - "jarDownloadUrl": "https://launcher.mojang.com/mc/game/1.13/client/c0b970952cdd279912da384cdbfc0c26e6c6090b/client.jar", - "jsonDownloadUrl": "https://launchermeta.mojang.com/mc/game/3132596cced9f9d6f1ca97aeec75651e6a9df0bc/1.13.json", - "approved": false, - "dateModified": "2018-07-20T21:59:01.413Z", - "gameVersionTypeId": 55023, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 52, - "gameVersionId": 6756, - "versionString": "1.12.2", - "jarDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.12.2/1.12.2.jar", - "jsonDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.12.2/1.12.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 628, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 51, - "gameVersionId": 6711, - "versionString": "1.12.1", - "jarDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.12.1/1.12.1.jar", - "jsonDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.12.1/1.12.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 628, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 50, - "gameVersionId": 6580, - "versionString": "1.12", - "jarDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.12/1.12.jar", - "jsonDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.12/1.12.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 628, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 49, - "gameVersionId": 6452, - "versionString": "1.11.2", - "jarDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.11.2/1.11.2.jar", - "jsonDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.11.2/1.11.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 599, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 48, - "gameVersionId": 6451, - "versionString": "1.11.1", - "jarDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.11.1/1.11.1.jar", - "jsonDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.11.1/1.11.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 599, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 47, - "gameVersionId": 6317, - "versionString": "1.11", - "jarDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.11/1.11.jar", - "jsonDownloadUrl": "https://s3.amazonaws.com/Minecraft.Download/versions/1.11/1.11.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 599, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 46, - "gameVersionId": 6170, - "versionString": "1.10.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.10.2/1.10.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.10.2/1.10.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 572, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 45, - "gameVersionId": 6160, - "versionString": "1.10.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.10.1/1.10.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.10.1/1.10.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 572, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 44, - "gameVersionId": 6144, - "versionString": "1.10", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.10/1.10.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.10/1.10.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 572, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 42, - "gameVersionId": 6084, - "versionString": "1.9.4", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.4/1.9.4.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.4/1.9.4.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 552, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 43, - "gameVersionId": 6085, - "versionString": "1.9.3", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.3/1.9.3.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.3/1.9.3.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 552, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 40, - "gameVersionId": 5997, - "versionString": "1.9.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.2/1.9.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.2/1.9.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 552, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 41, - "gameVersionId": 5998, - "versionString": "1.9.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.1/1.9.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9.1/1.9.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 552, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 39, - "gameVersionId": 5946, - "versionString": "1.9", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9/1.9.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.9/1.9.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 552, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 34, - "gameVersionId": 5806, - "versionString": "1.8.9", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.9/1.8.9.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.9/1.8.9.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 33, - "gameVersionId": 5703, - "versionString": "1.8.8", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.8/1.8.8.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.8/1.8.8.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 32, - "gameVersionId": 5642, - "versionString": "1.8.7", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.7/1.8.7.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.7/1.8.7.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 1, - "gameVersionId": 4480, - "versionString": "1.8.6", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.6/1.8.6.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.6/1.8.6.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 2, - "gameVersionId": 4479, - "versionString": "1.8.5", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.5/1.8.5.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.5/1.8.5.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 3, - "gameVersionId": 4478, - "versionString": "1.8.4", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.4/1.8.4.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.4/1.8.4.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 4, - "gameVersionId": 4466, - "versionString": "1.8.3", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.3/1.8.3.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.3/1.8.3.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 5, - "gameVersionId": 4465, - "versionString": "1.8.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.2/1.8.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.2/1.8.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 6, - "gameVersionId": 4463, - "versionString": "1.8.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.1/1.8.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8.1/1.8.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 7, - "gameVersionId": 4455, - "versionString": "1.8", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8/1.8.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.8/1.8.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 4, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 8, - "gameVersionId": 4449, - "versionString": "1.7.10", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.10/1.7.10.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.10/1.7.10.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 9, - "gameVersionId": 4448, - "versionString": "1.7.9", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.9/1.7.9.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.9/1.7.9.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 35, - "gameVersionId": 4447, - "versionString": "1.7.8", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.8/1.7.8.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.8/1.7.8.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 36, - "gameVersionId": 4446, - "versionString": "1.7.7", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.7/1.7.7.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.7/1.7.7.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 37, - "gameVersionId": 4445, - "versionString": "1.7.6", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.6/1.7.6.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.6/1.7.6.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 10, - "gameVersionId": 4444, - "versionString": "1.7.5", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.5/1.7.5.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.5/1.7.5.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 11, - "gameVersionId": 367, - "versionString": "1.7.4", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.4/1.7.4.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.4/1.7.4.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 38, - "gameVersionId": 5912, - "versionString": "1.7.3", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.3/1.7.3.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.3/1.7.3.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 12, - "gameVersionId": 361, - "versionString": "1.7.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.2/1.7.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.7.2/1.7.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 5, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 13, - "gameVersionId": 326, - "versionString": "1.6.4", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.4/1.6.4.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.4/1.6.4.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 6, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 14, - "gameVersionId": 320, - "versionString": "1.6.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.2/1.6.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.2/1.6.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 6, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 15, - "gameVersionId": 318, - "versionString": "1.6.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.1/1.6.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.1/1.6.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 6, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 16, - "gameVersionId": 312, - "versionString": "1.5.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.5.2/1.5.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.5.2/1.5.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 11, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 17, - "gameVersionId": 280, - "versionString": "1.5.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.5.1/1.5.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.5.1/1.5.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 11, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 18, - "gameVersionId": 272, - "versionString": "1.4.7", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.7/1.4.7.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.7/1.4.7.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 12, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 19, - "gameVersionId": 268, - "versionString": "1.4.6", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.6/1.4.6.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.6/1.4.6.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 12, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 20, - "gameVersionId": 4461, - "versionString": "1.4.5", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.5/1.4.5.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.5/1.4.5.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 12, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 21, - "gameVersionId": 4460, - "versionString": "1.4.4", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.4/1.4.4.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.4/1.4.4.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 12, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 22, - "gameVersionId": 255, - "versionString": "1.4.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.2/1.4.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.4.2/1.4.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 12, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 23, - "gameVersionId": 246, - "versionString": "1.3.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.3.2/1.3.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.3.2/1.3.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 13, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 24, - "gameVersionId": 241, - "versionString": "1.3.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.3.1/1.3.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.3.1/1.3.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 13, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 25, - "gameVersionId": 204, - "versionString": "1.2.5", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.5/1.2.5.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.5/1.2.5.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 14, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 26, - "gameVersionId": 4462, - "versionString": "1.2.4", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.4/1.2.4.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.4/1.2.4.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 14, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 27, - "gameVersionId": 203, - "versionString": "1.2.3", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.3/1.2.3.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.3/1.2.3.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 14, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 28, - "gameVersionId": 202, - "versionString": "1.2.2", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.2/1.2.2.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.2/1.2.2.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 14, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 29, - "gameVersionId": 201, - "versionString": "1.2.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.1/1.2.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.2.1/1.2.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 14, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 30, - "gameVersionId": 186, - "versionString": "1.1", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.1/1.1.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.1/1.1.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 15, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - }, - { - "id": 31, - "gameVersionId": 4482, - "versionString": "1.0", - "jarDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.0/1.0.jar", - "jsonDownloadUrl": "http://s3.amazonaws.com/Minecraft.Download/versions/1.0/1.0.json", - "approved": false, - "dateModified": "2018-03-29T17:24:38.393Z", - "gameVersionTypeId": 16, - "gameVersionStatus": 1, - "gameVersionTypeStatus": 1 - } -] \ No newline at end of file diff --git a/test/data/CurseForge-Modpack.json b/test/data/CurseForge-Modpack.json deleted file mode 100644 index 421d986e6..000000000 --- a/test/data/CurseForge-Modpack.json +++ /dev/null @@ -1,7994 +0,0 @@ -[ - { - "id": 429793, - "name": "Better Minecraft [FORGE] - 1.16.5", - "authors": [ - { - "name": "xSharkieTV", - "url": "https://www.curseforge.com/members/101555282-xsharkietv?username=xsharkietv", - "projectId": 429793, - "id": 340739, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 101555282, - "twitchId": 146047538 - }, - { - "name": "LunaPixelStudios", - "url": "https://www.curseforge.com/members/102066281-lunapixelstudios?username=lunapixelstudios", - "projectId": 429793, - "id": 432186, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 102066281, - "twitchId": 662941835 - } - ], - "attachments": [ - { - "id": 332996, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/996/256/256/637465096591253067.png", - "title": "637465096591253067.png", - "url": "https://media.forgecdn.net/avatars/332/996/637465096591253067.png", - "status": 1 - }, - { - "id": 343547, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/343/547/256/256/637488757873143837.png", - "title": "637488757873143837.png", - "url": "https://media.forgecdn.net/avatars/343/547/637488757873143837.png", - "status": 1 - }, - { - "id": 425722, - "projectId": 429793, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/425/722/256/256/637655127208469637.png", - "title": "637655127208469637.png", - "url": "https://media.forgecdn.net/avatars/425/722/637655127208469637.png", - "status": 1 - }, - { - "id": 332992, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/992/256/256/637465088449288029.png", - "title": "637465088449288029.png", - "url": "https://media.forgecdn.net/avatars/332/992/637465088449288029.png", - "status": 1 - }, - { - "id": 332977, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/977/256/256/637465058707740168.png", - "title": "637465058707740168.png", - "url": "https://media.forgecdn.net/avatars/332/977/637465058707740168.png", - "status": 1 - }, - { - "id": 332997, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/997/256/256/637465099726447084.png", - "title": "637465099726447084.png", - "url": "https://media.forgecdn.net/avatars/332/997/637465099726447084.png", - "status": 1 - }, - { - "id": 332976, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/976/256/256/637465058417338672.png", - "title": "637465058417338672.png", - "url": "https://media.forgecdn.net/avatars/332/976/637465058417338672.png", - "status": 1 - }, - { - "id": 386982, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/386/982/256/256/637576474833266829.png", - "title": "637576474833266829.png", - "url": "https://media.forgecdn.net/avatars/386/982/637576474833266829.png", - "status": 1 - }, - { - "id": 336756, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/336/756/256/256/637473348112597645.png", - "title": "637473348112597645.png", - "url": "https://media.forgecdn.net/avatars/336/756/637473348112597645.png", - "status": 1 - }, - { - "id": 337040, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/337/40/256/256/637473915918014272.png", - "title": "637473915918014272.png", - "url": "https://media.forgecdn.net/avatars/337/40/637473915918014272.png", - "status": 1 - }, - { - "id": 336758, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/336/758/256/256/637473351768312840.png", - "title": "637473351768312840.png", - "url": "https://media.forgecdn.net/avatars/336/758/637473351768312840.png", - "status": 1 - }, - { - "id": 332968, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/968/256/256/637465046108355697.png", - "title": "637465046108355697.png", - "url": "https://media.forgecdn.net/avatars/332/968/637465046108355697.png", - "status": 1 - }, - { - "id": 332991, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/991/256/256/637465087473398645.png", - "title": "637465087473398645.png", - "url": "https://media.forgecdn.net/avatars/332/991/637465087473398645.png", - "status": 1 - }, - { - "id": 332981, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/981/256/256/637465066795607282.png", - "title": "637465066795607282.png", - "url": "https://media.forgecdn.net/avatars/332/981/637465066795607282.png", - "status": 1 - }, - { - "id": 355186, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/355/186/256/256/637513853107725325.png", - "title": "637513853107725325.png", - "url": "https://media.forgecdn.net/avatars/355/186/637513853107725325.png", - "status": 1 - }, - { - "id": 324602, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/324/602/256/256/637440806625655285.png", - "title": "637440806625655285.png", - "url": "https://media.forgecdn.net/avatars/324/602/637440806625655285.png", - "status": 1 - }, - { - "id": 332990, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/990/256/256/637465086052164556.png", - "title": "637465086052164556.png", - "url": "https://media.forgecdn.net/avatars/332/990/637465086052164556.png", - "status": 1 - }, - { - "id": 340290, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/340/290/256/256/637481791988672598.png", - "title": "637481791988672598.png", - "url": "https://media.forgecdn.net/avatars/340/290/637481791988672598.png", - "status": 1 - }, - { - "id": 386785, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/386/785/256/256/637575964496471762.png", - "title": "637575964496471762.png", - "url": "https://media.forgecdn.net/avatars/386/785/637575964496471762.png", - "status": 1 - }, - { - "id": 324571, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/324/571/256/256/637440745016477221.png", - "title": "637440745016477221.png", - "url": "https://media.forgecdn.net/avatars/324/571/637440745016477221.png", - "status": 1 - }, - { - "id": 355185, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/355/185/256/256/637513850272379756.png", - "title": "637513850272379756.png", - "url": "https://media.forgecdn.net/avatars/355/185/637513850272379756.png", - "status": 1 - }, - { - "id": 332982, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/982/256/256/637465068157725272.png", - "title": "637465068157725272.png", - "url": "https://media.forgecdn.net/avatars/332/982/637465068157725272.png", - "status": 1 - }, - { - "id": 390908, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/390/908/310/172/better-minecraft-forge-edition-bg.png", - "title": "Logo2", - "url": "https://media.forgecdn.net/attachments/390/908/better-minecraft-forge-edition-bg.png", - "status": 1 - }, - { - "id": 390907, - "projectId": 429793, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/390/907/310/172/logo.png", - "title": "Logo", - "url": "https://media.forgecdn.net/attachments/390/907/logo.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://discord.gg/9S5tfrY4Fr", - "wikiUrl": "https://github.com/LunaPixelStudios/Better-Minecraft-FORGE-1.16.5/wiki", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/better-minecraft-modpack", - "gameId": 432, - "summary": "A Proper Vanilla+ Modpack | Caves & Cliffs Update! & The Wild UPDATE!", - "defaultFileId": 3559289, - "downloadCount": 2294840.0, - "latestFiles": [ - { - "id": 3553485, - "displayName": "Better Minecraft [FORGE] v47.5", - "fileName": "Better Minecraft [FORGE] v47.5.zip", - "fileDate": "2021-12-07T08:35:33.88Z", - "fileLength": 21930331, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3553/485/Better Minecraft [FORGE] v47.5.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 333202558, - "type": 3 - } - ], - "packageFingerprint": 872854727, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2617901, - "fileLegacyMappingId": null, - "projectId": 429793, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 801835208, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3537101, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "fd0639d766fc2f56f457c8c7225fa1dd510b665d" - }, - { - "algorithm": 2, - "value": "9e8571d72ab121172f9c96bedd9df353" - } - ], - "downloadCount": 5926 - }, - { - "id": 3554415, - "displayName": "Better Minecraft [FORGE] v48", - "fileName": "Better Minecraft [FORGE] v48.zip", - "fileDate": "2021-12-08T00:26:01.317Z", - "fileLength": 21928625, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3554/415/Better Minecraft [FORGE] v48.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 789812178, - "type": 3 - } - ], - "packageFingerprint": 2063991713, - "gameVersion": [ - "1.16.5", - "Forge" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2619022, - "fileLegacyMappingId": null, - "projectId": 429793, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 802637227, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3539534, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3554548, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "f01648ca6ded826fa34dacfa92305714ad2ed775" - }, - { - "algorithm": 2, - "value": "afc4bb94e50c0c68f24303c4ca5af7d5" - } - ], - "downloadCount": 17654 - }, - { - "id": 3559289, - "displayName": "Better Minecraft [FORGE] v49", - "fileName": "Better Minecraft [FORGE] v49.zip", - "fileDate": "2021-12-11T05:38:55.807Z", - "fileLength": 22424637, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/289/Better Minecraft [FORGE] v49.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3006561922, - "type": 3 - } - ], - "packageFingerprint": 1718433062, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624693, - "fileLegacyMappingId": null, - "projectId": 429793, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805461148, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3552004, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3559296, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "5ebd22d2492d2ebdeddef2347e48242b02baf7d3" - }, - { - "algorithm": 2, - "value": "0250467635c55d9ddcdc639c343a0d64" - } - ], - "downloadCount": 8 - } - ], - "categories": [ - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 429793, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 429793, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 429793, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 429793, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 5128, - "name": "Vanilla+", - "url": "https://www.curseforge.com/minecraft/modpacks/vanilla", - "avatarUrl": "https://media.forgecdn.net/avatars/451/388/637713564446392425.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 429793, - "avatarId": 451388, - "gameId": 432, - "slug": "vanilla", - "dateModified": "2021-11-01T09:40:44.687Z" - } - ], - "status": 4, - "primaryCategoryId": 5128, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "better-minecraft-modpack", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.16.5", - "projectFileId": 3559289, - "projectFileName": "Better Minecraft [FORGE] v49.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.5", - "projectFileId": 3553485, - "projectFileName": "Better Minecraft [FORGE] v47.5.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.4", - "projectFileId": 3384608, - "projectFileName": "Better Minecraft [FORGE] v27.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.3", - "projectFileId": 3215426, - "projectFileName": "Better Minecraft 1.16.5 [FORGE] v2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.1", - "projectFileId": 3173379, - "projectFileName": "Better Minecraft 1.16.1 [FORGE] v1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16", - "projectFileId": 3173379, - "projectFileName": "Better Minecraft 1.16.1 [FORGE] v1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.15.2", - "projectFileId": 3173374, - "projectFileName": "Better Minecraft 1.15.2 [FORGE] v1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 68722 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 3145017, - "projectFileName": "A Better Experience 1.12.2-v1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - } - ], - "isFeatured": false, - "popularityScore": 32544.30859375, - "gamePopularityRank": 785, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "modLoaders": [ - "Forge" - ], - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T06:02:48.053Z", - "dateCreated": "2020-12-20T15:21:41.61Z", - "dateReleased": "2021-12-11T05:47:41.213Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 285109, - "name": "RLCraft", - "authors": [ - { - "name": "Shivaxi", - "url": "https://www.curseforge.com/members/11806322-shivaxi?username=shivaxi", - "projectId": 285109, - "id": 183764, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 11806322, - "twitchId": 39682137 - } - ], - "attachments": [ - { - "id": 136944, - "projectId": 285109, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/136/944/256/256/636511227443004307.png", - "title": "636511227443004307.png", - "url": "https://media.forgecdn.net/avatars/136/944/636511227443004307.png", - "status": 1 - } - ], - "wikiUrl": "", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/rlcraft", - "gameId": 432, - "summary": "A modpack specially designed to bring an incredibly hardcore and semi-realism challenge revolving around survival, RPG elements, and adventure-like exploration.", - "defaultFileId": 2935316, - "downloadCount": 10189098.0, - "latestFiles": [ - { - "id": 2708027, - "displayName": "RLCraft 1.12.2 - Alpha v2.0.zip", - "fileName": "RLCraft 1.12.2 - Alpha v2.0.zip", - "fileDate": "2019-05-06T18:46:15.887Z", - "fileLength": 1520136, - "releaseType": 3, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2708/27/RLCraft 1.12.2 - Alpha v2.0.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 240131959, - "type": 3 - } - ], - "packageFingerprint": 3912887938, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1556001, - "fileLegacyMappingId": null, - "projectId": 285109, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 313727412, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1806794, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "404470412ea021c8936a8267f21e3a72d361509b" - }, - { - "algorithm": 2, - "value": "f60b063ffaf0167730ae9f77e69eee27" - } - ], - "downloadCount": 56206 - }, - { - "id": 2935316, - "displayName": "RLCraft 1.12.2 - Beta v2.8.2.zip", - "fileName": "RLCraft 1.12.2 - Beta v2.8.2.zip", - "fileDate": "2020-04-20T08:11:34.3Z", - "fileLength": 30627569, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2935/316/RLCraft 1.12.2 - Beta v2.8.2.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3991312357, - "type": 3 - } - ], - "packageFingerprint": 2523815034, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1835368, - "fileLegacyMappingId": null, - "projectId": 285109, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 443983128, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2187728, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 2935323, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "7f2402916e448241f597cc66afff983458523228" - }, - { - "algorithm": 2, - "value": "6d72fea71eeceba791792d5b2c716b42" - } - ], - "downloadCount": 4558775 - } - ], - "categories": [ - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 285109, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 285109, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - }, - { - "categoryId": 4479, - "name": "Hardcore", - "url": "https://www.curseforge.com/minecraft/modpacks/hardcore", - "avatarUrl": "https://media.forgecdn.net/avatars/14/473/635596760504656528.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 285109, - "avatarId": 14473, - "gameId": 432, - "slug": "hardcore", - "dateModified": "2015-02-16T15:34:10.467Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 285109, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 285109, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "rlcraft", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 2935316, - "projectFileName": "RLCraft 1.12.2 - Beta v2.8.2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 2708027, - "projectFileName": "RLCraft 1.12.2 - Alpha v2.0.zip", - "fileType": 3, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.11.2", - "projectFileId": 2532808, - "projectFileName": "RLCraft 1.11.2 - Beta v1.4.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 599 - } - ], - "isFeatured": false, - "popularityScore": 31168.431640625, - "gamePopularityRank": 827, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-09-01T00:55:21.043Z", - "dateCreated": "2018-01-10T01:25:44.237Z", - "dateReleased": "2020-06-28T18:37:51.253Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 389615, - "name": "The Pixelmon Modpack", - "authors": [ - { - "name": "PixelmonMod", - "url": "https://www.curseforge.com/members/101171654-pixelmonmod?username=pixelmonmod", - "projectId": 389615, - "id": 297046, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 101171654, - "twitchId": 541880465 - }, - { - "name": "Isiliden", - "url": "https://www.curseforge.com/members/32340198-isiliden?username=isiliden", - "projectId": 389615, - "id": 466783, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 32340198, - "twitchId": 40580822 - } - ], - "attachments": [ - { - "id": 279234, - "projectId": 389615, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/279/234/256/256/637276853291457748.png", - "title": "637276853291457748.png", - "url": "https://media.forgecdn.net/avatars/279/234/637276853291457748.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://pixelmonmod.com/tracker.php", - "wikiUrl": "https://pixelmonmod.com/", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/the-pixelmon-modpack", - "gameId": 432, - "summary": "This is a Modpack designed to give players an easy time getting started.", - "defaultFileId": 3558597, - "downloadCount": 3082363.0, - "latestFiles": [ - { - "id": 3545325, - "displayName": " The Pixelmon Modpack 8.3.4", - "fileName": "The Pixelmon Modpack-8.3.4.zip", - "fileDate": "2021-12-02T16:49:05.447Z", - "fileLength": 8490053, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3545/325/The Pixelmon Modpack-8.3.4.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 89763737, - "type": 3 - } - ], - "packageFingerprint": 877749191, - "gameVersion": [ - "Forge", - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2608427, - "fileLegacyMappingId": null, - "projectId": 389615, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 798484760, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3518296, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "ace5b074441a11657040f13e03fa980c59e7040b" - }, - { - "algorithm": 2, - "value": "4a3b2b6d22bf35ef79ac830c18c65e0c" - } - ], - "downloadCount": 71320 - }, - { - "id": 3558597, - "displayName": "The Pixelmon Modpack 8.3.4a", - "fileName": "The Pixelmon Modpack-8.3.4a.zip", - "fileDate": "2021-12-10T19:37:10.98Z", - "fileLength": 8045778, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3558/597/The Pixelmon Modpack-8.3.4a.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3447220236, - "type": 3 - } - ], - "packageFingerprint": 1953456534, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2623883, - "fileLegacyMappingId": null, - "projectId": 389615, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 804862723, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3549834, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "e431005aa84661feaa641d8e08ee0386a2c41e08" - }, - { - "algorithm": 2, - "value": "0c5fddd654bffccb11eb6e90c4217fa0" - } - ], - "downloadCount": 649 - } - ], - "categories": [ - { - "categoryId": 4480, - "name": "Map Based", - "url": "https://www.curseforge.com/minecraft/modpacks/map-based", - "avatarUrl": "https://media.forgecdn.net/avatars/14/475/635596760683250342.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 389615, - "avatarId": 14475, - "gameId": 432, - "slug": "map-based", - "dateModified": "2015-02-16T15:34:28.34Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 389615, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 389615, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 389615, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "the-pixelmon-modpack", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 3558597, - "projectFileName": "The Pixelmon Modpack-8.3.4a.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - } - ], - "isFeatured": true, - "popularityScore": 27532.416015625, - "gamePopularityRank": 957, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-12T02:52:50.21Z", - "dateCreated": "2020-06-13T22:48:49.073Z", - "dateReleased": "2021-12-10T19:37:10.98Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 381671, - "name": "All the Mods 6 - ATM6 - 1.16.5", - "authors": [ - { - "name": "ATMTeam", - "url": "https://www.curseforge.com/members/36387323-atmteam?username=atmteam", - "projectId": 381671, - "id": 288560, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 36387323, - "twitchId": 170399703 - } - ], - "attachments": [ - { - "id": 270284, - "projectId": 381671, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/270/284/256/256/637244555892739649.png", - "title": "637244555892739649.png", - "url": "https://media.forgecdn.net/avatars/270/284/637244555892739649.png", - "status": 1 - }, - { - "id": 289318, - "projectId": 381671, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/289/318/256/256/637314730218552799_animated.gif", - "title": "637314730218552799.gif", - "url": "https://media.forgecdn.net/avatars/289/318/637314730218552799.gif", - "status": 1 - }, - { - "id": 289320, - "projectId": 381671, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/289/320/256/256/637314732010369633_animated.gif", - "title": "637314732010369633.gif", - "url": "https://media.forgecdn.net/avatars/289/320/637314732010369633.gif", - "status": 1 - } - ], - "issueTrackerUrl": "https://github.com/AllTheMods/ATM-6/issues", - "wikiUrl": "https://github.com/AllTheMods/ATM-6/wiki", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/all-the-mods-6", - "gameId": 432, - "summary": "HUGE 1.16.5 Modpack. Can you get to the Creative items by making the \"ATM Star\"? ", - "defaultFileId": 3558717, - "downloadCount": 4259644.0, - "latestFiles": [ - { - "id": 3558493, - "displayName": " AllTheMods 6-1.8.13", - "fileName": "AllTheMods 6-1.8.13.zip", - "fileDate": "2021-12-10T18:25:25.133Z", - "fileLength": 4538508, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3558/493/AllTheMods 6-1.8.13.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1911439843, - "type": 3 - } - ], - "packageFingerprint": 3498464456, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2623763, - "fileLegacyMappingId": null, - "projectId": 381671, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 804793755, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3549501, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3558507, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "3d6f310776936d160f1cd9c5dbee98511db396b4" - }, - { - "algorithm": 2, - "value": "ea96b204c3f600905c4acec31bf1b4d6" - } - ], - "downloadCount": 595 - }, - { - "id": 3558717, - "displayName": "AllTheMods 6-1.8.14", - "fileName": "AllTheMods 6-1.8.14.zip", - "fileDate": "2021-12-10T20:47:54.243Z", - "fileLength": 4538330, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3558/717/AllTheMods 6-1.8.14.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2687463966, - "type": 3 - } - ], - "packageFingerprint": 3835292064, - "gameVersion": [ - "1.16.5", - "Forge" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624025, - "fileLegacyMappingId": null, - "projectId": 381671, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 804942450, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3550230, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3558719, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "961ad3a8cf8b763fddc0bb25c92a938231c5f2dd" - }, - { - "algorithm": 2, - "value": "982a55e004e781e5a1a3760c42457a71" - } - ], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 381671, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 381671, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - }, - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 381671, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - }, - { - "categoryId": 4482, - "name": "Extra Large", - "url": "https://www.curseforge.com/minecraft/modpacks/extra-large", - "avatarUrl": "https://media.forgecdn.net/avatars/14/472/635596760403562826.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 381671, - "avatarId": 14472, - "gameId": 432, - "slug": "extra-large", - "dateModified": "2015-02-16T15:34:00.357Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 381671, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - } - ], - "status": 4, - "primaryCategoryId": 4472, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "all-the-mods-6", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.16.5", - "projectFileId": 3558717, - "projectFileName": "AllTheMods 6-1.8.14.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.4", - "projectFileId": 3174524, - "projectFileName": "All_the_Mods_6-1.4.1.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.3", - "projectFileId": 3101004, - "projectFileName": "All the Mods 6-1.2.1.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.1", - "projectFileId": 3042203, - "projectFileName": "All the Mods 6-0.29.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - } - ], - "isFeatured": true, - "popularityScore": 26365.556640625, - "gamePopularityRank": 997, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-10T21:02:05.187Z", - "dateCreated": "2020-05-07T13:39:49.183Z", - "dateReleased": "2021-12-10T20:50:32.293Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 296062, - "name": "SkyFactory 4", - "authors": [ - { - "name": "darkosto", - "url": "https://www.curseforge.com/members/15754032-darkosto?username=darkosto", - "projectId": 296062, - "id": 196009, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 15754032, - "twitchId": 63937599 - }, - { - "name": "Firstnecron", - "url": "https://www.curseforge.com/members/809144-firstnecron?username=firstnecron", - "projectId": 296062, - "id": 221857, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 809144, - "twitchId": 36108934 - }, - { - "name": "Blargerist", - "url": "https://www.curseforge.com/members/15911169-blargerist?username=blargerist", - "projectId": 296062, - "id": 221856, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 15911169, - "twitchId": 58423212 - } - ], - "attachments": [ - { - "id": 157621, - "projectId": 296062, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/157/621/256/256/636646986533428173.png", - "title": "636646986533428173.png", - "url": "https://media.forgecdn.net/avatars/157/621/636646986533428173.png", - "status": 1 - }, - { - "id": 199573, - "projectId": 296062, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/199/573/256/256/636907930795697123.png", - "title": "636907930795697123.png", - "url": "https://media.forgecdn.net/avatars/199/573/636907930795697123.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://github.com/Darkosto/SkyFactory4Issues/issues", - "wikiUrl": "", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/skyfactory-4", - "gameId": 432, - "summary": "The ultimate skyblock modpack! Watch development at: darkosto.tv/SkyFactoryLive", - "defaultFileId": 3559103, - "downloadCount": 6645096.0, - "latestFiles": [ - { - "id": 2720596, - "displayName": "SkyFactory 4 - 4.0.6", - "fileName": "SkyFactory4-4.0.6.zip", - "fileDate": "2019-06-05T22:22:23.44Z", - "fileLength": 19828128, - "releaseType": 3, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2720/596/SkyFactory4-4.0.6.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3458593623, - "type": 3 - } - ], - "packageFingerprint": 3315506908, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1571098, - "fileLegacyMappingId": null, - "projectId": 296062, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 321589125, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1827930, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 2720605, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "a619e1b6509102343e529c15c1c42fdbae6e3a25" - }, - { - "algorithm": 2, - "value": "9815fd17a7314ebff8e70090d4da63a5" - } - ], - "downloadCount": 12012 - }, - { - "id": 3012232, - "displayName": "SkyFactory 4 - 4.2.1", - "fileName": "SkyFactory-4_4.2.1.zip", - "fileDate": "2020-07-23T03:44:18.84Z", - "fileLength": 23112081, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3012/232/SkyFactory-4_4.2.1.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 597524226, - "type": 3 - } - ], - "packageFingerprint": 2992501297, - "gameVersion": [ - "1.12.1", - "1.12", - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000001", - "gameVersion": "1.12.1", - "gameVersionReleaseDate": "2017-08-03T05:00:00Z", - "gameVersionName": "1.12.1", - "gameVersionTypeId": 628 - }, - { - "gameVersionPadded": "0000000001.0000000012", - "gameVersion": "1.12", - "gameVersionReleaseDate": "2017-06-07T05:00:00Z", - "gameVersionName": "1.12", - "gameVersionTypeId": 628 - }, - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1932306, - "fileLegacyMappingId": null, - "projectId": 296062, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 492910830, - "gameVersionDateReleased": "2017-06-07T05:00:00Z", - "gameVersionMappingId": 2335613, - "gameVersionId": 6580, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3012234, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "d602ab6e142f8c883883cc8bfeb2050bbae0418f" - }, - { - "algorithm": 2, - "value": "2c31c9ba362578adf2349fcb27eef547" - } - ], - "downloadCount": 14301 - }, - { - "id": 3012798, - "displayName": "SkyFactory 4 - 4.2.2", - "fileName": "SkyFactory-4_4.2.2.zip", - "fileDate": "2020-07-23T19:00:08.983Z", - "fileLength": 22997186, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3012/798/SkyFactory-4_4.2.2.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1700635929, - "type": 3 - } - ], - "packageFingerprint": 3294123038, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1933006, - "fileLegacyMappingId": null, - "projectId": 296062, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 493257352, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2336848, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3012800, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "beb50e15da930f642cb1d9a50a961bfaa3a44893" - }, - { - "algorithm": 2, - "value": "d7104791d8ed6aa889d216e738b0f286" - } - ], - "downloadCount": 2396286 - }, - { - "id": 3559103, - "displayName": "SkyFactory 4 - 4.2.3", - "fileName": "SkyFactory-4-4.2.3.zip", - "fileDate": "2021-12-11T01:35:02.193Z", - "fileLength": 22997210, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/103/SkyFactory-4-4.2.3.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2297474910, - "type": 3 - } - ], - "packageFingerprint": 1324733695, - "gameVersion": [ - "Forge", - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624464, - "fileLegacyMappingId": null, - "projectId": 296062, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805308479, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3551479, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "bd976343c6d589866aca44b5cef8163d8de7cf0b" - }, - { - "algorithm": 2, - "value": "7639ca53c90bcc4bcc9f63c72727db94" - } - ], - "downloadCount": 5087 - } - ], - "categories": [ - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 296062, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - }, - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 296062, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 296062, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4736, - "name": "Skyblock", - "url": "https://www.curseforge.com/minecraft/modpacks/skyblock", - "avatarUrl": "https://media.forgecdn.net/avatars/162/818/636678840408956323.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 296062, - "avatarId": 162818, - "gameId": 432, - "slug": "skyblock", - "dateModified": "2018-07-22T19:20:40.897Z" - } - ], - "status": 4, - "primaryCategoryId": 4736, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "skyfactory-4", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 3559103, - "projectFileName": "SkyFactory-4-4.2.3.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 3012232, - "projectFileName": "SkyFactory-4_4.2.1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12", - "projectFileId": 3012232, - "projectFileName": "SkyFactory-4_4.2.1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.1", - "projectFileId": 3012232, - "projectFileName": "SkyFactory-4_4.2.1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 2720596, - "projectFileName": "SkyFactory4-4.0.6.zip", - "fileType": 3, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - } - ], - "isFeatured": false, - "popularityScore": 21742.0625, - "gamePopularityRank": 1199, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T12:16:19.227Z", - "dateCreated": "2018-06-15T22:30:53.297Z", - "dateReleased": "2021-12-11T01:35:02.193Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 452013, - "name": "Better Minecraft [FABRIC] - 1.18", - "authors": [ - { - "name": "xSharkieTV", - "url": "https://www.curseforge.com/members/101555282-xsharkietv?username=xsharkietv", - "projectId": 452013, - "id": 364577, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 101555282, - "twitchId": 146047538 - }, - { - "name": "LunaPixelStudios", - "url": "https://www.curseforge.com/members/102066281-lunapixelstudios?username=lunapixelstudios", - "projectId": 452013, - "id": 432192, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 102066281, - "twitchId": 662941835 - } - ], - "attachments": [ - { - "id": 349543, - "projectId": 452013, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/349/543/256/256/637501526363192413.png", - "title": "637501526363192413.png", - "url": "https://media.forgecdn.net/avatars/349/543/637501526363192413.png", - "status": 1 - }, - { - "id": 349972, - "projectId": 452013, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/349/972/256/256/637502441019006138.png", - "title": "637502441019006138.png", - "url": "https://media.forgecdn.net/avatars/349/972/637502441019006138.png", - "status": 1 - }, - { - "id": 355188, - "projectId": 452013, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/355/188/256/256/637513854618191968.png", - "title": "637513854618191968.png", - "url": "https://media.forgecdn.net/avatars/355/188/637513854618191968.png", - "status": 1 - }, - { - "id": 390421, - "projectId": 452013, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/390/421/256/256/637582084265873360.png", - "title": "637582084265873360.png", - "url": "https://media.forgecdn.net/avatars/390/421/637582084265873360.png", - "status": 1 - }, - { - "id": 427901, - "projectId": 452013, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/427/901/256/256/637660316263857358.png", - "title": "637660316263857358.png", - "url": "https://media.forgecdn.net/avatars/427/901/637660316263857358.png", - "status": 1 - }, - { - "id": 368595, - "projectId": 452013, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/368/595/310/172/logofabric.png", - "title": "Logo", - "url": "https://media.forgecdn.net/attachments/368/595/logofabric.png", - "status": 1 - }, - { - "id": 368596, - "projectId": 452013, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/368/596/310/172/fabric.png", - "title": "Logo2", - "url": "https://media.forgecdn.net/attachments/368/596/fabric.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://discord.gg/9S5tfrY4Fr", - "wikiUrl": "https://github.com/LunaPixelStudios/Better-Minecraft-FABRIC", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/better-minecraft-fabric", - "gameId": 432, - "summary": "A Proper Vanilla+ Modpack for the Fabric Mod Loader", - "defaultFileId": 3559317, - "downloadCount": 865161.0, - "latestFiles": [ - { - "id": 3554487, - "displayName": "Better Minecraft [FABRIC] v5", - "fileName": "Better Minecraft [FABRIC] v5.zip", - "fileDate": "2021-12-08T01:54:24.117Z", - "fileLength": 80594839, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3554/487/Better Minecraft [FABRIC] v5.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2575027439, - "type": 3 - } - ], - "packageFingerprint": 3868039331, - "gameVersion": [ - "1.18" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000018", - "gameVersion": "1.18", - "gameVersionReleaseDate": "2021-11-30T16:23:01.427Z", - "gameVersionName": "1.18", - "gameVersionTypeId": 73250 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2619114, - "fileLegacyMappingId": null, - "projectId": 452013, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 802688317, - "gameVersionDateReleased": "2021-11-30T16:23:01.427Z", - "gameVersionMappingId": 3539677, - "gameVersionId": 8830, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3554491, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "2c1b20ac56bb4a604011d161ebe694d7e5f95cc8" - }, - { - "algorithm": 2, - "value": "f8f2ba5bb5566293f56992bb71d4e066" - } - ], - "downloadCount": 9098 - }, - { - "id": 3559317, - "displayName": "Better Minecraft [FABRIC] v8", - "fileName": "Better Minecraft [FABRIC] v8.zip", - "fileDate": "2021-12-11T06:28:19.107Z", - "fileLength": 81647877, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/317/Better Minecraft [FABRIC] v8.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3732590067, - "type": 3 - } - ], - "packageFingerprint": 3019581626, - "gameVersion": [ - "Fabric", - "1.18" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Fabric", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000018", - "gameVersion": "1.18", - "gameVersionReleaseDate": "2021-11-30T16:23:01.427Z", - "gameVersionName": "1.18", - "gameVersionTypeId": 73250 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624724, - "fileLegacyMappingId": null, - "projectId": 452013, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805486638, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3552082, - "gameVersionId": 7499, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3559318, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "899ea03fa973ff5c163784c25a2806ca60974d4c" - }, - { - "algorithm": 2, - "value": "0f948a8f65f431675622d8add89d9a57" - } - ], - "downloadCount": 106 - } - ], - "categories": [ - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 452013, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - }, - { - "categoryId": 5128, - "name": "Vanilla+", - "url": "https://www.curseforge.com/minecraft/modpacks/vanilla", - "avatarUrl": "https://media.forgecdn.net/avatars/451/388/637713564446392425.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 452013, - "avatarId": 451388, - "gameId": 432, - "slug": "vanilla", - "dateModified": "2021-11-01T09:40:44.687Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 452013, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 452013, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 452013, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - } - ], - "status": 4, - "primaryCategoryId": 5128, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "better-minecraft-fabric", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.18", - "projectFileId": 3559317, - "projectFileName": "Better Minecraft [FABRIC] v8.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 73250 - }, - { - "gameVersion": "1.17.1", - "projectFileId": 3538692, - "projectFileName": "Better Minecraft [FABRIC] v19.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 73242 - }, - { - "gameVersion": "1.17", - "projectFileId": 3538692, - "projectFileName": "Better Minecraft [FABRIC] v19.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 73242 - }, - { - "gameVersion": "1.16.5", - "projectFileId": 3334604, - "projectFileName": "Better Minecraft [FABRIC] v8.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.4", - "projectFileId": 3249498, - "projectFileName": "Better Minecraft 1.16.5 [FABRIC] v3.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.3", - "projectFileId": 3222386, - "projectFileName": "Better Minecraft 1.16.5 [FABRIC] v1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - } - ], - "isFeatured": false, - "popularityScore": 18841.1953125, - "gamePopularityRank": 1376, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T07:18:27.953Z", - "dateCreated": "2021-02-28T23:43:56.27Z", - "dateReleased": "2021-12-11T06:32:45.53Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 286377, - "name": "Modern Skyblock 3: Departed", - "authors": [ - { - "name": "TheLimePixel", - "url": "https://www.curseforge.com/members/26268145-thelimepixel?username=thelimepixel", - "projectId": 286377, - "id": 185185, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 26268145, - "twitchId": 94386437 - } - ], - "attachments": [ - { - "id": 139388, - "projectId": 286377, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/139/388/256/256/636527692460722826.png", - "title": "636527692460722826.png", - "url": "https://media.forgecdn.net/avatars/139/388/636527692460722826.png", - "status": 1 - }, - { - "id": 141791, - "projectId": 286377, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/141/791/256/256/636544733395786830.png", - "title": "636544733395786830.png", - "url": "https://media.forgecdn.net/avatars/141/791/636544733395786830.png", - "status": 1 - } - ], - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/modern-skyblock-3-departed", - "gameId": 432, - "summary": "A pack departing from the old progression system based around recipe changes | Without Ex Nihilo | 3 Modes | 250+ Mods | 1k+ Quests | for MC 1.12", - "defaultFileId": 2680147, - "downloadCount": 620003.0, - "latestFiles": [ - { - "id": 2533746, - "displayName": "Modern Skyblock-3.1.5", - "fileName": "Modern Skyblock-3.1.5.zip", - "fileDate": "2018-02-22T00:21:25.64Z", - "fileLength": 18222533, - "releaseType": 3, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2533/746/Modern Skyblock-3.1.5.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3149212858, - "type": 3 - } - ], - "packageFingerprint": 2644132694, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1349280, - "fileLegacyMappingId": null, - "projectId": 286377, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 226956481, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1533870, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - }, - { - "id": 2573334, - "displayName": "Modern Skyblock-3.4.1.3", - "fileName": "Modern Skyblock-3.4.1.3.zip", - "fileDate": "2018-06-16T16:13:54.303Z", - "fileLength": 19181186, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2573/334/Modern Skyblock-3.4.1.3.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3843820780, - "type": 3 - } - ], - "packageFingerprint": 2996917580, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1396929, - "fileLegacyMappingId": null, - "projectId": 286377, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 247585450, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1600393, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 2573348, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - }, - { - "id": 2680147, - "displayName": "Modern Skyblock-3.6.0", - "fileName": "Modern Skyblock-3.6.0.zip", - "fileDate": "2019-02-25T17:30:36.77Z", - "fileLength": 10946503, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2680/147/Modern Skyblock-3.6.0.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 224409949, - "type": 3 - } - ], - "packageFingerprint": 3769396643, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1522959, - "fileLegacyMappingId": null, - "projectId": 286377, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 296705965, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1762145, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 2680204, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 286377, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4736, - "name": "Skyblock", - "url": "https://www.curseforge.com/minecraft/modpacks/skyblock", - "avatarUrl": "https://media.forgecdn.net/avatars/162/818/636678840408956323.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 286377, - "avatarId": 162818, - "gameId": 432, - "slug": "skyblock", - "dateModified": "2018-07-22T19:20:40.897Z" - }, - { - "categoryId": 4482, - "name": "Extra Large", - "url": "https://www.curseforge.com/minecraft/modpacks/extra-large", - "avatarUrl": "https://media.forgecdn.net/avatars/14/472/635596760403562826.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 286377, - "avatarId": 14472, - "gameId": 432, - "slug": "extra-large", - "dateModified": "2015-02-16T15:34:00.357Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 286377, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - } - ], - "status": 4, - "primaryCategoryId": 4736, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "modern-skyblock-3-departed", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 2680147, - "projectFileName": "Modern Skyblock-3.6.0.zip", - "fileType": 1, - "gameVersionFlavor": null - }, - { - "gameVersion": "1.12.2", - "projectFileId": 2573334, - "projectFileName": "Modern Skyblock-3.4.1.3.zip", - "fileType": 2, - "gameVersionFlavor": null - }, - { - "gameVersion": "1.12.2", - "projectFileId": 2533746, - "projectFileName": "Modern Skyblock-3.1.5.zip", - "fileType": 3, - "gameVersionFlavor": null - } - ], - "isFeatured": false, - "popularityScore": 3861.947021484375, - "gamePopularityRank": 1414, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2019-02-25T19:51:58.6Z", - "dateCreated": "2018-01-28T20:47:26.04Z", - "dateReleased": "2019-02-25T19:28:42.483Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 445369, - "name": "Mustard Virus by Forge Labs", - "authors": [ - { - "name": "mustardsean2", - "url": "https://www.curseforge.com/members/101511799-mustardsean2?username=mustardsean2", - "projectId": 445369, - "id": 357508, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 101511799, - "twitchId": 177689886 - }, - { - "name": "Gopvikeen", - "url": "https://www.curseforge.com/members/100920818-gopvikeen?username=gopvikeen", - "projectId": 445369, - "id": 362293, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 100920818, - "twitchId": 227513569 - } - ], - "attachments": [ - { - "id": 342120, - "projectId": 445369, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/342/120/256/256/637485779500419856.jpeg", - "title": "637485779500419856.jpeg", - "url": "https://media.forgecdn.net/avatars/342/120/637485779500419856.jpeg", - "status": 1 - } - ], - "wikiUrl": "", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/zombie-apocalypse-slow-zombies-by-forge-labs", - "gameId": 432, - "summary": "A Zombie Apocalypse! ", - "defaultFileId": 3510753, - "downloadCount": 1103064.0, - "latestFiles": [ - { - "id": 3213904, - "displayName": "100 Days - Zombie Apocalypse (SLOW ZOMBIES).zip", - "fileName": "100 Days - Zombie Apocalypse (SLOW ZOMBIES).zip", - "fileDate": "2021-02-23T06:46:27.267Z", - "fileLength": 147073, - "releaseType": 3, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3213/904/100 Days - Zombie Apocalypse (SLOW ZOMBIES).zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3616472563, - "type": 3 - } - ], - "packageFingerprint": 994954255, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2185250, - "fileLegacyMappingId": null, - "projectId": 445369, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 611183086, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2743852, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "7d42dde0e003199d9412598c71fe19b66b0b0eb0" - }, - { - "algorithm": 2, - "value": "7a059a8734574a152cea265d1329edba" - } - ], - "downloadCount": 21504 - }, - { - "id": 3501808, - "displayName": "Mustard Virus-2.0.zip", - "fileName": "Mustard Virus-2.0.zip", - "fileDate": "2021-10-24T02:49:37.263Z", - "fileLength": 180215, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3501/808/Mustard Virus-2.0.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1407181937, - "type": 3 - } - ], - "packageFingerprint": 1135339324, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2553819, - "fileLegacyMappingId": null, - "projectId": 445369, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 768967808, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3407863, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "48403ced3c9f40321ed3f6e724de9963f902e176" - }, - { - "algorithm": 2, - "value": "cf6e1937f0bedd2d4b2a3f3bdb51c993" - } - ], - "downloadCount": 13456 - }, - { - "id": 3510753, - "displayName": "Mustard Virus 2.1(FOR NEW MAP ONLY)", - "fileName": "Mustard Virus 2 by Forge Labs-2.1 (1).zip", - "fileDate": "2021-10-31T18:04:18.31Z", - "fileLength": 181815, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3510/753/Mustard Virus 2 by Forge Labs-2.1 (1).zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2223318220, - "type": 3 - } - ], - "packageFingerprint": 1913659811, - "gameVersion": [ - "Forge", - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2565188, - "fileLegacyMappingId": null, - "projectId": 445369, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 774451148, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3429255, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "f52babaca5855755e433d7d1a4e96bc75b48c59a" - }, - { - "algorithm": 2, - "value": "32fb3967828a1adf75172bdaadb18296" - } - ], - "downloadCount": 85957 - } - ], - "categories": [ - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 445369, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4480, - "name": "Map Based", - "url": "https://www.curseforge.com/minecraft/modpacks/map-based", - "avatarUrl": "https://media.forgecdn.net/avatars/14/475/635596760683250342.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 445369, - "avatarId": 14475, - "gameId": 432, - "slug": "map-based", - "dateModified": "2015-02-16T15:34:28.34Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 445369, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4479, - "name": "Hardcore", - "url": "https://www.curseforge.com/minecraft/modpacks/hardcore", - "avatarUrl": "https://media.forgecdn.net/avatars/14/473/635596760504656528.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 445369, - "avatarId": 14473, - "gameId": 432, - "slug": "hardcore", - "dateModified": "2015-02-16T15:34:10.467Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "zombie-apocalypse-slow-zombies-by-forge-labs", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 3510753, - "projectFileName": "Mustard Virus 2 by Forge Labs-2.1 (1).zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 3213904, - "projectFileName": "100 Days - Zombie Apocalypse (SLOW ZOMBIES).zip", - "fileType": 3, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - } - ], - "isFeatured": false, - "popularityScore": 16373.7724609375, - "gamePopularityRank": 1539, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-11-01T08:21:37.987Z", - "dateCreated": "2021-02-10T18:19:09.993Z", - "dateReleased": "2021-10-31T18:04:18.31Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 289267, - "name": "Roguelike Adventures and Dungeons", - "authors": [ - { - "name": "dreams01", - "url": "https://www.curseforge.com/members/6968124-dreams01?username=dreams01", - "projectId": 289267, - "id": 188373, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 6968124, - "twitchId": 70745036 - } - ], - "attachments": [ - { - "id": 144220, - "projectId": 289267, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/144/220/256/256/636554450588038771.png", - "title": "636554450588038771.png", - "url": "https://media.forgecdn.net/avatars/144/220/636554450588038771.png", - "status": 1 - }, - { - "id": 225127, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/127/310/172/2018-02-28_11.png", - "title": "Betweenlands", - "url": "https://media.forgecdn.net/attachments/225/127/2018-02-28_11.png", - "status": 1 - }, - { - "id": 225122, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/122/310/172/2018-02-28_11.png", - "title": "Dungeon2", - "url": "https://media.forgecdn.net/attachments/225/122/2018-02-28_11.png", - "status": 1 - }, - { - "id": 225125, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/125/310/172/2018-02-28_11.png", - "title": "Twilight", - "url": "https://media.forgecdn.net/attachments/225/125/2018-02-28_11.png", - "status": 1 - }, - { - "id": 225126, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/126/310/172/2018-02-28_11.png", - "title": "Twilight2", - "url": "https://media.forgecdn.net/attachments/225/126/2018-02-28_11.png", - "status": 1 - }, - { - "id": 225120, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/120/310/172/2018-02-27_21.png", - "title": "Aether", - "url": "https://media.forgecdn.net/attachments/225/120/2018-02-27_21.png", - "status": 1 - }, - { - "id": 225123, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/123/310/172/2018-02-28_11.png", - "title": "Travelling Merchant", - "url": "https://media.forgecdn.net/attachments/225/123/2018-02-28_11.png", - "status": 1 - }, - { - "id": 267833, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/267/833/310/172/2019-09-30_23.png", - "title": "dragons!", - "url": "https://media.forgecdn.net/attachments/267/833/2019-09-30_23.png", - "status": 1 - }, - { - "id": 225128, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/128/310/172/2018-02-28_11.png", - "title": "Betweenlands2", - "url": "https://media.forgecdn.net/attachments/225/128/2018-02-28_11.png", - "status": 1 - }, - { - "id": 225124, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/124/310/172/2018-02-28_11.png", - "title": "Dungeon3", - "url": "https://media.forgecdn.net/attachments/225/124/2018-02-28_11.png", - "status": 1 - }, - { - "id": 267832, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/267/832/310/172/2019-08-20_18.png", - "title": "quests!", - "url": "https://media.forgecdn.net/attachments/267/832/2019-08-20_18.png", - "status": 1 - }, - { - "id": 225121, - "projectId": 289267, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/225/121/310/172/2018-02-28_11.png", - "title": "Dungeon1", - "url": "https://media.forgecdn.net/attachments/225/121/2018-02-28_11.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://discord.gg/xv7NbVDPEH", - "wikiUrl": "", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/roguelike-adventures-and-dungeons", - "gameId": 432, - "summary": "Exploration-based rogue-like modpack with Quests, Character and Tools Leveling, tons of new structures, Twilight Forest, Aether, The Betweenlands, Tropicraft, Cavern, Beneath dimension and more!", - "defaultFileId": 3530433, - "downloadCount": 5073153.0, - "latestFiles": [ - { - "id": 3464722, - "displayName": " R.A.D. - 1.47a", - "fileName": "RAD-1.47a.zip", - "fileDate": "2021-09-19T13:46:58.853Z", - "fileLength": 34933775, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3464/722/RAD-1.47a.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1922737150, - "type": 3 - } - ], - "packageFingerprint": 940062259, - "gameVersion": [ - "Forge", - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2506686, - "fileLegacyMappingId": null, - "projectId": 289267, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 747709918, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3317801, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3466059, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "5023995eb83b7d4137d25f4cc88de55dead8380c" - }, - { - "algorithm": 2, - "value": "49c7cad9c58e2535b76c9b0784f95aec" - } - ], - "downloadCount": 172880 - }, - { - "id": 3530433, - "displayName": "R.A.D. - 1.48", - "fileName": "RAD-1.48.zip", - "fileDate": "2021-11-19T11:11:48.807Z", - "fileLength": 34959223, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3530/433/RAD-1.48.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 4131447137, - "type": 3 - } - ], - "packageFingerprint": 4055706768, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2589979, - "fileLegacyMappingId": null, - "projectId": 289267, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 788062559, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3478167, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3530857, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "9f3d9d932e864434230e589d0bd05e7faddb9bc8" - }, - { - "algorithm": 2, - "value": "d5c0265edf580e36d3086747b6523e62" - } - ], - "downloadCount": 37133 - } - ], - "categories": [ - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 289267, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 289267, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 289267, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 289267, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 289267, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 289267, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 289267, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - } - ], - "status": 4, - "primaryCategoryId": 4476, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "roguelike-adventures-and-dungeons", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 3530433, - "projectFileName": "RAD-1.48.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - } - ], - "isFeatured": false, - "popularityScore": 16490.171875, - "gamePopularityRank": 1542, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-11-23T22:16:27.66Z", - "dateCreated": "2018-02-28T20:04:18.773Z", - "dateReleased": "2021-11-19T20:28:22.203Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 367635, - "name": "HR: New Beginnings", - "authors": [ - { - "name": "Haydennz", - "url": "https://www.curseforge.com/members/100902967-haydennz?username=haydennz", - "projectId": 367635, - "id": 273640, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 100902967, - "twitchId": 85468659 - } - ], - "attachments": [ - { - "id": 254719, - "projectId": 367635, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/254/719/256/256/637199416702301560.png", - "title": "637199416702301560.png", - "url": "https://media.forgecdn.net/avatars/254/719/637199416702301560.png", - "status": 1 - } - ], - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/hr-new-beginnings", - "gameId": 432, - "summary": "New Beginnings is a 1.15.2 modpack that adds to the vanilla experience. ", - "defaultFileId": 3204600, - "downloadCount": 585178.0, - "latestFiles": [ - { - "id": 3204600, - "displayName": "New Beginnings 2.6.0", - "fileName": "New Beginnings-2.6.0.zip", - "fileDate": "2021-02-15T08:21:14.383Z", - "fileLength": 6175174, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3204/600/New Beginnings-2.6.0.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1692179199, - "type": 3 - } - ], - "packageFingerprint": 3561721027, - "gameVersion": [ - "1.15.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000015.0000000002", - "gameVersion": "1.15.2", - "gameVersionReleaseDate": "2020-01-22T00:00:00Z", - "gameVersionName": "1.15.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2173094, - "fileLegacyMappingId": null, - "projectId": 367635, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 605985194, - "gameVersionDateReleased": "2020-01-22T00:00:00Z", - "gameVersionMappingId": 2723716, - "gameVersionId": 7722, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3204605, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 367635, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 367635, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 367635, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 367635, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - } - ], - "status": 4, - "primaryCategoryId": 4476, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "hr-new-beginnings", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.15.2", - "projectFileId": 3204600, - "projectFileName": "New Beginnings-2.6.0.zip", - "fileType": 1, - "gameVersionFlavor": null - } - ], - "isFeatured": true, - "popularityScore": 9391.0478515625, - "gamePopularityRank": 1630, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-02-15T08:29:52.997Z", - "dateCreated": "2020-03-16T07:47:50.18Z", - "dateReleased": "2021-02-15T08:26:25.273Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 436653, - "name": "Mineshafts & Monsters", - "authors": [ - { - "name": "bstylia14", - "url": "https://www.curseforge.com/members/101308537-bstylia14?username=bstylia14", - "projectId": 436653, - "id": 348148, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 101308537, - "twitchId": 563171746 - } - ], - "attachments": [ - { - "id": 332591, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/591/256/256/637463985558252881.png", - "title": "637463985558252881.png", - "url": "https://media.forgecdn.net/avatars/332/591/637463985558252881.png", - "status": 1 - }, - { - "id": 332311, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/311/256/256/637463217975405960.png", - "title": "637463217975405960.png", - "url": "https://media.forgecdn.net/avatars/332/311/637463217975405960.png", - "status": 1 - }, - { - "id": 332588, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/588/256/256/637463983161292750.png", - "title": "637463983161292750.png", - "url": "https://media.forgecdn.net/avatars/332/588/637463983161292750.png", - "status": 1 - }, - { - "id": 332589, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/589/256/256/637463984624091540.png", - "title": "637463984624091540.png", - "url": "https://media.forgecdn.net/avatars/332/589/637463984624091540.png", - "status": 1 - }, - { - "id": 431482, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/431/482/256/256/637667048960893906_animated.gif", - "title": "637667048960893906.gif", - "url": "https://media.forgecdn.net/avatars/431/482/637667048960893906.gif", - "status": 1 - }, - { - "id": 431487, - "projectId": 436653, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/431/487/256/256/637667061539266129_animated.gif", - "title": "637667061539266129.gif", - "url": "https://media.forgecdn.net/avatars/431/487/637667061539266129.gif", - "status": 1 - }, - { - "id": 332587, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/332/587/256/256/637463983147068782.png", - "title": "637463983147068782.png", - "url": "https://media.forgecdn.net/avatars/332/587/637463983147068782.png", - "status": 1 - }, - { - "id": 412929, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/412/929/310/172/heddb18.png", - "title": "Screenshot 2", - "url": "https://media.forgecdn.net/attachments/412/929/heddb18.png", - "status": 1 - }, - { - "id": 412930, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/412/930/310/172/puedeeh.png", - "title": "Screenshot 3", - "url": "https://media.forgecdn.net/attachments/412/930/puedeeh.png", - "status": 1 - }, - { - "id": 412927, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/412/927/310/172/pvsrfig.png", - "title": "Screenshot 1", - "url": "https://media.forgecdn.net/attachments/412/927/pvsrfig.png", - "status": 1 - }, - { - "id": 412932, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/412/932/310/172/qvci5ia.png", - "title": "Screenshot 6", - "url": "https://media.forgecdn.net/attachments/412/932/qvci5ia.png", - "status": 1 - }, - { - "id": 412931, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/412/931/310/172/lbaweh9.jpg", - "title": "Screenshot 4", - "url": "https://media.forgecdn.net/attachments/412/931/lbaweh9.jpg", - "status": 1 - }, - { - "id": 412933, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/412/933/310/172/te3jmms.png", - "title": "Screenshot 6", - "url": "https://media.forgecdn.net/attachments/412/933/te3jmms.png", - "status": 1 - }, - { - "id": 333777, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/333/777/310/172/maxresdefault.jpg", - "title": "epic gameplay", - "url": "https://media.forgecdn.net/attachments/333/777/maxresdefault.jpg", - "status": 1 - }, - { - "id": 398153, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/398/153/310/172/unknown.png", - "title": "credit:mikoto", - "url": "https://media.forgecdn.net/attachments/398/153/unknown.png", - "status": 1 - }, - { - "id": 356348, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/356/348/310/172/untitled-design-29.png", - "title": "credit: Mongineer Gaming", - "url": "https://media.forgecdn.net/attachments/356/348/untitled-design-29.png", - "status": 1 - }, - { - "id": 379677, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/379/677/310/172/unknown.png", - "title": "credit: NocFA", - "url": "https://media.forgecdn.net/attachments/379/677/unknown.png", - "status": 1 - }, - { - "id": 355565, - "projectId": 436653, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/355/565/310/172/2021-04-10_15.jpg", - "title": "credit: nefarious", - "url": "https://media.forgecdn.net/attachments/355/565/2021-04-10_15.jpg", - "status": 1 - } - ], - "issueTrackerUrl": "https://discord.gg/Gc2vxg4WCW", - "wikiUrl": "https://mineshafts-monsters.fandom.com/wiki/Mineshafts_%26_Monsters_Wiki", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/mineshafts-monsters", - "gameId": 432, - "summary": "An RPG modpack that actually gives the player an immersive storyline and complex worldview.", - "defaultFileId": 3559119, - "downloadCount": 880827.0, - "latestFiles": [ - { - "id": 3554913, - "displayName": "Mineshafts & Monsters - 1.10.1__hf", - "fileName": "Mineshafts Monsters - 1.10.1_hf.zip", - "fileDate": "2021-12-08T12:59:22.373Z", - "fileLength": 362182677, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3554/913/Mineshafts Monsters - 1.10.1_hf.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 948907313, - "type": 3 - } - ], - "packageFingerprint": 2313662883, - "gameVersion": [ - "1.16-Snapshot", - "1.16.3", - "1.16.1", - "1.16", - "1.16.5", - "1.16.4", - "1.16.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016", - "gameVersion": "1.16", - "gameVersionReleaseDate": "2020-02-06T00:00:00Z", - "gameVersionName": "1.16-Snapshot", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000003", - "gameVersion": "1.16.3", - "gameVersionReleaseDate": "2020-09-10T14:44:20.443Z", - "gameVersionName": "1.16.3", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000001", - "gameVersion": "1.16.1", - "gameVersionReleaseDate": "2020-06-24T12:41:11.823Z", - "gameVersionName": "1.16.1", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016", - "gameVersion": "1.16", - "gameVersionReleaseDate": "2020-06-23T13:41:08.75Z", - "gameVersionName": "1.16", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000004", - "gameVersion": "1.16.4", - "gameVersionReleaseDate": "2020-11-02T18:40:51.49Z", - "gameVersionName": "1.16.4", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000002", - "gameVersion": "1.16.2", - "gameVersionReleaseDate": "2020-08-11T16:42:21.863Z", - "gameVersionName": "1.16.2", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2619639, - "fileLegacyMappingId": null, - "projectId": 436653, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 802994793, - "gameVersionDateReleased": "2020-02-06T00:00:00Z", - "gameVersionMappingId": 3540723, - "gameVersionId": 7751, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3554958, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "d3335059c585ffe10838b9bc4a427c817f7d4de7" - }, - { - "algorithm": 2, - "value": "ad18a6cc21f37c17851d7a2c785fc255" - } - ], - "downloadCount": 3748 - }, - { - "id": 3556726, - "displayName": "Mineshafts & Monsters - 1.10.2", - "fileName": "Mineshafts Monsters - 1.10.2.zip", - "fileDate": "2021-12-10T00:17:29.077Z", - "fileLength": 362222101, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3556/726/Mineshafts Monsters - 1.10.2.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1287822324, - "type": 3 - } - ], - "packageFingerprint": 3063899903, - "gameVersion": [ - "1.16-Snapshot", - "1.16.3", - "1.16.1", - "1.16", - "1.16.5", - "1.16.4", - "1.16.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016", - "gameVersion": "1.16", - "gameVersionReleaseDate": "2020-02-06T00:00:00Z", - "gameVersionName": "1.16-Snapshot", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000003", - "gameVersion": "1.16.3", - "gameVersionReleaseDate": "2020-09-10T14:44:20.443Z", - "gameVersionName": "1.16.3", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000001", - "gameVersion": "1.16.1", - "gameVersionReleaseDate": "2020-06-24T12:41:11.823Z", - "gameVersionName": "1.16.1", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016", - "gameVersion": "1.16", - "gameVersionReleaseDate": "2020-06-23T13:41:08.75Z", - "gameVersionName": "1.16", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000004", - "gameVersion": "1.16.4", - "gameVersionReleaseDate": "2020-11-02T18:40:51.49Z", - "gameVersionName": "1.16.4", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000002", - "gameVersion": "1.16.2", - "gameVersionReleaseDate": "2020-08-11T16:42:21.863Z", - "gameVersionName": "1.16.2", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2621829, - "fileLegacyMappingId": null, - "projectId": 436653, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 804252876, - "gameVersionDateReleased": "2020-02-06T00:00:00Z", - "gameVersionMappingId": 3545922, - "gameVersionId": 7751, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3556840, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "8c301538d37d912d9aa745e7e421662a6fee67b3" - }, - { - "algorithm": 2, - "value": "70687d8545ee50ee7b114a67981ba536" - } - ], - "downloadCount": 1963 - }, - { - "id": 3559119, - "displayName": "Mineshafts & Monsters - 1.10.2_hf2", - "fileName": "Mineshafts Monsters - 1.10.2_hf2.zip", - "fileDate": "2021-12-11T01:44:03.353Z", - "fileLength": 362206059, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/119/Mineshafts Monsters - 1.10.2_hf2.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 897817851, - "type": 3 - } - ], - "packageFingerprint": 1005976896, - "gameVersion": [ - "1.16-Snapshot", - "1.16.3", - "1.16.1", - "1.16", - "1.16.5", - "Forge", - "1.16.4", - "1.16.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016", - "gameVersion": "1.16", - "gameVersionReleaseDate": "2020-02-06T00:00:00Z", - "gameVersionName": "1.16-Snapshot", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000003", - "gameVersion": "1.16.3", - "gameVersionReleaseDate": "2020-09-10T14:44:20.443Z", - "gameVersionName": "1.16.3", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000001", - "gameVersion": "1.16.1", - "gameVersionReleaseDate": "2020-06-24T12:41:11.823Z", - "gameVersionName": "1.16.1", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016", - "gameVersion": "1.16", - "gameVersionReleaseDate": "2020-06-23T13:41:08.75Z", - "gameVersionName": "1.16", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000004", - "gameVersion": "1.16.4", - "gameVersionReleaseDate": "2020-11-02T18:40:51.49Z", - "gameVersionName": "1.16.4", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0000000001.0000000016.0000000002", - "gameVersion": "1.16.2", - "gameVersionReleaseDate": "2020-08-11T16:42:21.863Z", - "gameVersionName": "1.16.2", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624480, - "fileLegacyMappingId": null, - "projectId": 436653, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805319835, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3551530, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3559152, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "40fc9882f6f5784d2a7a3a8f72d0ab2dc7f2cc24" - }, - { - "algorithm": 2, - "value": "50517b900f73bd58705671b868c7fbee" - } - ], - "downloadCount": 61 - } - ], - "categories": [ - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 436653, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - }, - { - "categoryId": 4479, - "name": "Hardcore", - "url": "https://www.curseforge.com/minecraft/modpacks/hardcore", - "avatarUrl": "https://media.forgecdn.net/avatars/14/473/635596760504656528.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 436653, - "avatarId": 14473, - "gameId": 432, - "slug": "hardcore", - "dateModified": "2015-02-16T15:34:10.467Z" - }, - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 436653, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 436653, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 436653, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "mineshafts-monsters", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.16.3", - "projectFileId": 3559119, - "projectFileName": "Mineshafts Monsters - 1.10.2_hf2.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.1", - "projectFileId": 3559119, - "projectFileName": "Mineshafts Monsters - 1.10.2_hf2.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16", - "projectFileId": 3559119, - "projectFileName": "Mineshafts Monsters - 1.10.2_hf2.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.5", - "projectFileId": 3559119, - "projectFileName": "Mineshafts Monsters - 1.10.2_hf2.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.4", - "projectFileId": 3559119, - "projectFileName": "Mineshafts Monsters - 1.10.2_hf2.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.2", - "projectFileId": 3559119, - "projectFileName": "Mineshafts Monsters - 1.10.2_hf2.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.3", - "projectFileId": 3556726, - "projectFileName": "Mineshafts Monsters - 1.10.2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.1", - "projectFileId": 3556726, - "projectFileName": "Mineshafts Monsters - 1.10.2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16", - "projectFileId": 3556726, - "projectFileName": "Mineshafts Monsters - 1.10.2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.5", - "projectFileId": 3556726, - "projectFileName": "Mineshafts Monsters - 1.10.2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.4", - "projectFileId": 3556726, - "projectFileName": "Mineshafts Monsters - 1.10.2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.2", - "projectFileId": 3556726, - "projectFileName": "Mineshafts Monsters - 1.10.2.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - } - ], - "isFeatured": false, - "popularityScore": 14931.548828125, - "gamePopularityRank": 1648, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T02:29:35.427Z", - "dateCreated": "2021-01-15T15:36:37.517Z", - "dateReleased": "2021-12-11T02:18:34.987Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 388169, - "name": "Valhelsia 3 - 1.16", - "authors": [ - { - "name": "ValhelsiaTeam", - "url": "https://www.curseforge.com/members/100705180-valhelsiateam?username=valhelsiateam", - "projectId": 388169, - "id": 295486, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 100705180, - "twitchId": 478073736 - } - ], - "attachments": [ - { - "id": 284618, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/284/618/256/256/637298059239611590.png", - "title": "637298059239611590.png", - "url": "https://media.forgecdn.net/avatars/284/618/637298059239611590.png", - "status": 1 - }, - { - "id": 329366, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/329/366/256/256/637454812417283424.png", - "title": "637454812417283424.png", - "url": "https://media.forgecdn.net/avatars/329/366/637454812417283424.png", - "status": 1 - }, - { - "id": 348657, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/348/657/256/256/637499742860968837.png", - "title": "637499742860968837.png", - "url": "https://media.forgecdn.net/avatars/348/657/637499742860968837.png", - "status": 1 - }, - { - "id": 302976, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/302/976/256/256/637370089618732026.png", - "title": "637370089618732026.png", - "url": "https://media.forgecdn.net/avatars/302/976/637370089618732026.png", - "status": 1 - }, - { - "id": 320103, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/320/103/256/256/637427090456822913.png", - "title": "637427090456822913.png", - "url": "https://media.forgecdn.net/avatars/320/103/637427090456822913.png", - "status": 1 - }, - { - "id": 350454, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/350/454/256/256/637503621907243502.png", - "title": "637503621907243502.png", - "url": "https://media.forgecdn.net/avatars/350/454/637503621907243502.png", - "status": 1 - }, - { - "id": 369574, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/369/574/256/256/637539130176087157.png", - "title": "637539130176087157.png", - "url": "https://media.forgecdn.net/avatars/369/574/637539130176087157.png", - "status": 1 - }, - { - "id": 369576, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/369/576/256/256/637539133480225521.png", - "title": "637539133480225521.png", - "url": "https://media.forgecdn.net/avatars/369/576/637539133480225521.png", - "status": 1 - }, - { - "id": 286076, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/286/76/256/256/637303311276610817.png", - "title": "637303311276610817.png", - "url": "https://media.forgecdn.net/avatars/286/76/637303311276610817.png", - "status": 1 - }, - { - "id": 277634, - "projectId": 388169, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/277/634/256/256/637270674050000791.png", - "title": "637270674050000791.png", - "url": "https://media.forgecdn.net/avatars/277/634/637270674050000791.png", - "status": 1 - }, - { - "id": 397628, - "projectId": 388169, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/397/628/256/256/637598883971484204.png", - "title": "637598883971484204.png", - "url": "https://media.forgecdn.net/avatars/397/628/637598883971484204.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://github.com/ValhelsiaTeam/Valhelsia/issues", - "wikiUrl": "https://wiki.valhelsia.net", - "sourceUrl": "https://github.com/ValhelsiaTeam/Valhelsia", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/valhelsia-3", - "gameId": 432, - "summary": "A modpack in 1.16.5 with a mixture of technology, magic, exploration, adventure with Vanilla+ aspect...", - "defaultFileId": 3559712, - "downloadCount": 2758985.0, - "latestFiles": [ - { - "id": 3508444, - "displayName": "Valhelsia 3 - 3.4.5", - "fileName": "Valhelsia 3-3.4.5.zip", - "fileDate": "2021-10-29T15:57:31.847Z", - "fileLength": 34801950, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3508/444/Valhelsia 3-3.4.5.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1214761071, - "type": 3 - } - ], - "packageFingerprint": 3257248024, - "gameVersion": [ - "1.16.5", - "Forge" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2562137, - "fileLegacyMappingId": null, - "projectId": 388169, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 772912093, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3423094, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3508447, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "6b26f3a9da938e93289f541efe4d295eb04288c1" - }, - { - "algorithm": 2, - "value": "9f4a99539b13891880128824dd08f03e" - } - ], - "downloadCount": 73359 - }, - { - "id": 3559712, - "displayName": "Valhelsia 3 - 3.4.7", - "fileName": "Valhelsia_3-3.4.7.zip", - "fileDate": "2021-12-11T11:38:42.98Z", - "fileLength": 34810447, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/712/Valhelsia_3-3.4.7.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1069763036, - "type": 3 - } - ], - "packageFingerprint": 1657865818, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2625176, - "fileLegacyMappingId": null, - "projectId": 388169, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805609070, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3553511, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3559718, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "6d8b5037382d3a0b51a50c355f9874478c232fbe" - }, - { - "algorithm": 2, - "value": "b3d7c7409a6d2ddd1355c4377713cfbe" - } - ], - "downloadCount": 329 - } - ], - "categories": [ - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 388169, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - }, - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 388169, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 388169, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 388169, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 388169, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - } - ], - "status": 4, - "primaryCategoryId": 4472, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "valhelsia-3", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.16.5", - "projectFileId": 3559712, - "projectFileName": "Valhelsia_3-3.4.7.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.4", - "projectFileId": 3165371, - "projectFileName": "Valhelsia 3-3.1.5.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.3", - "projectFileId": 3110648, - "projectFileName": "Valhelsia 3-pre5-3.1.0.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.1", - "projectFileId": 3086614, - "projectFileName": "Valhelsia 3-3.0.21.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - } - ], - "isFeatured": false, - "popularityScore": 14523.609375, - "gamePopularityRank": 1679, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T14:50:05.29Z", - "dateCreated": "2020-06-06T19:10:04.933Z", - "dateReleased": "2021-12-11T11:45:47.447Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 482878, - "name": "Better Minecraft [FORGE] - 1.18", - "authors": [ - { - "name": "xSharkieTV", - "url": "https://www.curseforge.com/members/101555282-xsharkietv?username=xsharkietv", - "projectId": 482878, - "id": 397168, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 101555282, - "twitchId": 146047538 - }, - { - "name": "LunaPixelStudios", - "url": "https://www.curseforge.com/members/102066281-lunapixelstudios?username=lunapixelstudios", - "projectId": 482878, - "id": 432193, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 102066281, - "twitchId": 662941835 - } - ], - "attachments": [ - { - "id": 382812, - "projectId": 482878, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/382/812/256/256/637567483073235390.png", - "title": "637567483073235390.png", - "url": "https://media.forgecdn.net/avatars/382/812/637567483073235390.png", - "status": 1 - }, - { - "id": 386783, - "projectId": 482878, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/386/783/256/256/637575962376934903.png", - "title": "637575962376934903.png", - "url": "https://media.forgecdn.net/avatars/386/783/637575962376934903.png", - "status": 1 - }, - { - "id": 414789, - "projectId": 482878, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/414/789/256/256/637637377670119784.png", - "title": "637637377670119784.png", - "url": "https://media.forgecdn.net/avatars/414/789/637637377670119784.png", - "status": 1 - }, - { - "id": 427154, - "projectId": 482878, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/427/154/256/256/637658498733509427.png", - "title": "637658498733509427.png", - "url": "https://media.forgecdn.net/avatars/427/154/637658498733509427.png", - "status": 1 - }, - { - "id": 427155, - "projectId": 482878, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/427/155/256/256/637658498952878032.png", - "title": "637658498952878032.png", - "url": "https://media.forgecdn.net/avatars/427/155/637658498952878032.png", - "status": 1 - }, - { - "id": 385703, - "projectId": 482878, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/385/703/310/172/bm_modpack_bg.png", - "title": "Modpack Logo", - "url": "https://media.forgecdn.net/attachments/385/703/bm_modpack_bg.png", - "status": 1 - }, - { - "id": 367151, - "projectId": 482878, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/367/151/310/172/better-minecraft-logo.png", - "title": "Logo Modpack", - "url": "https://media.forgecdn.net/attachments/367/151/better-minecraft-logo.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://discord.com/invite/9S5tfrY4Fr", - "wikiUrl": "https://github.com/LunaPixelStudios/Better-Minecraft-FORGE-1.18/wiki", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/better-minecraft-modpack-new", - "gameId": 432, - "summary": "A Proper Vanilla+ Modpack for Forge Mod Loader", - "defaultFileId": 3559340, - "downloadCount": 404678.0, - "latestFiles": [ - { - "id": 3351315, - "displayName": "Better Minecraft [OfflineTV Edition] v3", - "fileName": "Better Minecraft [OfflineTV Edition] v3.zip", - "fileDate": "2021-06-14T05:38:27.887Z", - "fileLength": 24191402, - "releaseType": 3, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3351/315/Better Minecraft [OfflineTV Edition] v3.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 534781276, - "type": 3 - } - ], - "packageFingerprint": 2402828959, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2361680, - "fileLegacyMappingId": null, - "projectId": 482878, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 683464279, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3042216, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3351323, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "0f2866e6f95832a4386d9afd51348937d2dc1143" - }, - { - "algorithm": 2, - "value": "0be7b1e4c6c0090d65f2942cd9eae81e" - } - ], - "downloadCount": 26577 - }, - { - "id": 3547872, - "displayName": "Better Minecraft [FORGE] v1 - 1.18", - "fileName": "Better Minecraft [FORGE] v1.zip", - "fileDate": "2021-12-03T19:01:02.547Z", - "fileLength": 61326331, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3547/872/Better Minecraft [FORGE] v1.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 873089369, - "type": 3 - } - ], - "packageFingerprint": 317146610, - "gameVersion": [ - "1.18", - "1.18-Snapshot" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000018", - "gameVersion": "1.18", - "gameVersionReleaseDate": "2021-11-30T16:23:01.427Z", - "gameVersionName": "1.18", - "gameVersionTypeId": 73250 - }, - { - "gameVersionPadded": "0000000001.0000000018", - "gameVersion": "1.18", - "gameVersionReleaseDate": "2021-08-12T00:00:00Z", - "gameVersionName": "1.18-Snapshot", - "gameVersionTypeId": 73250 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2611230, - "fileLegacyMappingId": null, - "projectId": 482878, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 799225235, - "gameVersionDateReleased": "2021-08-12T00:00:00Z", - "gameVersionMappingId": 3523213, - "gameVersionId": 8633, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3547875, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "22bf7651f1f5a1ee25b10578ff0b82ca66d89646" - }, - { - "algorithm": 2, - "value": "588e5ae2beb92f3fa43bd59c30725fc4" - } - ], - "downloadCount": 12256 - }, - { - "id": 3559340, - "displayName": "Better Minecraft [FORGE] v4", - "fileName": "Better Minecraft [FORGE] v4.zip", - "fileDate": "2021-12-11T07:24:44.36Z", - "fileLength": 61342383, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/340/Better Minecraft [FORGE] v4.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 97155380, - "type": 3 - } - ], - "packageFingerprint": 1348039469, - "gameVersion": [ - "Forge", - "1.18" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000018", - "gameVersion": "1.18", - "gameVersionReleaseDate": "2021-11-30T16:23:01.427Z", - "gameVersionName": "1.18", - "gameVersionTypeId": 73250 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624755, - "fileLegacyMappingId": null, - "projectId": 482878, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805492192, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3552154, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3559364, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "f11b7f7ded1ffab6c84d916dce50abade94335f3" - }, - { - "algorithm": 2, - "value": "47c156c91403533f9919b67d0b36a99c" - } - ], - "downloadCount": 34 - } - ], - "categories": [ - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 482878, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 482878, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 482878, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - }, - { - "categoryId": 5128, - "name": "Vanilla+", - "url": "https://www.curseforge.com/minecraft/modpacks/vanilla", - "avatarUrl": "https://media.forgecdn.net/avatars/451/388/637713564446392425.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 482878, - "avatarId": 451388, - "gameId": 432, - "slug": "vanilla", - "dateModified": "2021-11-01T09:40:44.687Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 482878, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - } - ], - "status": 4, - "primaryCategoryId": 5128, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "better-minecraft-modpack-new", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.18", - "projectFileId": 3559340, - "projectFileName": "Better Minecraft [FORGE] v4.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 73250 - }, - { - "gameVersion": "1.17.1", - "projectFileId": 3538706, - "projectFileName": "Better Minecraft [FORGE] v11.5.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 73242 - }, - { - "gameVersion": "1.17", - "projectFileId": 3538706, - "projectFileName": "Better Minecraft [FORGE] v11.5.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 73242 - }, - { - "gameVersion": "1.16.5", - "projectFileId": 3351315, - "projectFileName": "Better Minecraft [OfflineTV Edition] v3.zip", - "fileType": 3, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - } - ], - "isFeatured": false, - "popularityScore": 13546.4560546875, - "gamePopularityRank": 1763, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T07:55:00.607Z", - "dateCreated": "2021-05-16T07:51:47.29Z", - "dateReleased": "2021-12-11T07:48:21.68Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 349129, - "name": "MC Eternal", - "authors": [ - { - "name": "adam98991", - "url": "https://www.curseforge.com/members/100190069-adam98991?username=adam98991", - "projectId": 349129, - "id": 253573, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 100190069, - "twitchId": 218492534 - } - ], - "attachments": [ - { - "id": 388926, - "projectId": 349129, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/388/926/256/256/637581040113975390.png", - "title": "637581040113975390.png", - "url": "https://media.forgecdn.net/avatars/388/926/637581040113975390.png", - "status": 1 - }, - { - "id": 388925, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/388/925/256/256/637581039551932886.png", - "title": "637581039551932886.png", - "url": "https://media.forgecdn.net/avatars/388/925/637581039551932886.png", - "status": 1 - }, - { - "id": 290282, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/290/282/256/256/637318215627853817.png", - "title": "637318215627853817.png", - "url": "https://media.forgecdn.net/avatars/290/282/637318215627853817.png", - "status": 1 - }, - { - "id": 233851, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/233/851/256/256/637074632697775685.png", - "title": "637074632697775685.png", - "url": "https://media.forgecdn.net/avatars/233/851/637074632697775685.png", - "status": 1 - }, - { - "id": 388921, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/388/921/256/256/637581034585105616.png", - "title": "637581034585105616.png", - "url": "https://media.forgecdn.net/avatars/388/921/637581034585105616.png", - "status": 1 - }, - { - "id": 266930, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/930/310/172/2019-10-27_01.png", - "title": "Ferrous", - "url": "https://media.forgecdn.net/attachments/266/930/2019-10-27_01.png", - "status": 1 - }, - { - "id": 305850, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/305/850/310/172/13.png", - "title": "Meteor", - "url": "https://media.forgecdn.net/attachments/305/850/13.png", - "status": 1 - }, - { - "id": 305854, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/305/854/310/172/12456.png", - "title": "Betweenlands Portal", - "url": "https://media.forgecdn.net/attachments/305/854/12456.png", - "status": 1 - }, - { - "id": 267289, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/267/289/310/172/2019-10-30_21.png", - "title": "Late night diggy diggy hole", - "url": "https://media.forgecdn.net/attachments/267/289/2019-10-30_21.png", - "status": 1 - }, - { - "id": 267308, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/267/308/310/172/2019-10-30_22.png", - "title": "Flowing dynamic river", - "url": "https://media.forgecdn.net/attachments/267/308/2019-10-30_22.png", - "status": 1 - }, - { - "id": 266919, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/919/310/172/2019-10-27_00.png", - "title": "Grave", - "url": "https://media.forgecdn.net/attachments/266/919/2019-10-27_00.png", - "status": 1 - }, - { - "id": 266932, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/932/310/172/2019-10-27_01.png", - "title": "Flowing stream", - "url": "https://media.forgecdn.net/attachments/266/932/2019-10-27_01.png", - "status": 1 - }, - { - "id": 266922, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/922/310/172/2019-10-27_00.png", - "title": "Dragon", - "url": "https://media.forgecdn.net/attachments/266/922/2019-10-27_00.png", - "status": 1 - }, - { - "id": 266935, - "projectId": 349129, - "description": "See no false advertising here! ", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/935/310/172/ezgif.gif", - "title": "Rat shooting cheese out of a cannon", - "url": "https://media.forgecdn.net/attachments/266/935/ezgif.gif", - "status": 1 - }, - { - "id": 266921, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/921/310/172/2019-10-27_00.png", - "title": "Wizard tower", - "url": "https://media.forgecdn.net/attachments/266/921/2019-10-27_00.png", - "status": 1 - }, - { - "id": 266931, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/931/310/172/2019-10-27_01.png", - "title": "Sleepy yeti *yawn*", - "url": "https://media.forgecdn.net/attachments/266/931/2019-10-27_01.png", - "status": 1 - }, - { - "id": 305852, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/305/852/310/172/1234.png", - "title": "Ratlantis", - "url": "https://media.forgecdn.net/attachments/305/852/1234.png", - "status": 1 - }, - { - "id": 266926, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/926/310/172/2019-10-27_00.png", - "title": "Sakura river", - "url": "https://media.forgecdn.net/attachments/266/926/2019-10-27_00.png", - "status": 1 - }, - { - "id": 305855, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/305/855/310/172/12345.png", - "title": "Thaumcraft Golem", - "url": "https://media.forgecdn.net/attachments/305/855/12345.png", - "status": 1 - }, - { - "id": 305853, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/305/853/310/172/123456.png", - "title": "Farm", - "url": "https://media.forgecdn.net/attachments/305/853/123456.png", - "status": 1 - }, - { - "id": 305978, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/305/978/310/172/2020-07-17_11.png", - "title": "Ethereal Moon Wizard", - "url": "https://media.forgecdn.net/attachments/305/978/2020-07-17_11.png", - "status": 1 - }, - { - "id": 266933, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/933/310/172/coming-halloween.gif", - "title": "Coming soon", - "url": "https://media.forgecdn.net/attachments/266/933/coming-halloween.gif", - "status": 1 - }, - { - "id": 266920, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/920/310/172/2019-10-27_00.png", - "title": "Night", - "url": "https://media.forgecdn.net/attachments/266/920/2019-10-27_00.png", - "status": 1 - }, - { - "id": 267306, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/267/306/310/172/2019-10-30_22.png", - "title": "Nordic Valley", - "url": "https://media.forgecdn.net/attachments/267/306/2019-10-30_22.png", - "status": 1 - }, - { - "id": 305977, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/305/977/310/172/2020-07-14_18.png", - "title": "The Moon", - "url": "https://media.forgecdn.net/attachments/305/977/2020-07-14_18.png", - "status": 1 - }, - { - "id": 266918, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/918/310/172/2019-10-27_00.png", - "title": "Swamp", - "url": "https://media.forgecdn.net/attachments/266/918/2019-10-27_00.png", - "status": 1 - }, - { - "id": 266923, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/923/310/172/2019-10-27_00.png", - "title": "Tower post dragon", - "url": "https://media.forgecdn.net/attachments/266/923/2019-10-27_00.png", - "status": 1 - }, - { - "id": 266925, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/925/310/172/2019-10-27_00.png", - "title": "Mystical", - "url": "https://media.forgecdn.net/attachments/266/925/2019-10-27_00.png", - "status": 1 - }, - { - "id": 266917, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/917/310/172/2019-10-03_23.png", - "title": "Dungeon 4", - "url": "https://media.forgecdn.net/attachments/266/917/2019-10-03_23.png", - "status": 1 - }, - { - "id": 274938, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/274/938/310/172/screenshot-2017-02-15-01.png", - "title": "I've heard red matter is good for mining copper?", - "url": "https://media.forgecdn.net/attachments/274/938/screenshot-2017-02-15-01.png", - "status": 1 - }, - { - "id": 266927, - "projectId": 349129, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/266/927/310/172/2019-10-27_00.png", - "title": "Ring a ring a rat", - "url": "https://media.forgecdn.net/attachments/266/927/2019-10-27_00.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://github.com/Minecraft-Eternal/MC-Eternal-1.12/issues", - "wikiUrl": "https://discord.gg/ZS6FRR3", - "sourceUrl": "https://github.com/Minecraft-Eternal/MC-Eternal-1.12", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/minecraft-eternal", - "gameId": 432, - "summary": "Something for everyone I promise! Hybrid pack with some fun twists, Over 1000+ Quests and challenges to complete!", - "defaultFileId": 3457321, - "downloadCount": 2971342.0, - "latestFiles": [ - { - "id": 3252080, - "displayName": "Minecraft Eternal 1.4.2 Beta", - "fileName": "Eternal 1.4.2 Beta.zip", - "fileDate": "2021-03-26T19:10:47.663Z", - "fileLength": 19256652, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3252/80/Eternal 1.4.2 Beta.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1854528652, - "type": 3 - } - ], - "packageFingerprint": 3455727664, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2235373, - "fileLegacyMappingId": null, - "projectId": 349129, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 632364831, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2820010, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "0cf93082e038228ff7dbdce91d9ebc11df31489d" - }, - { - "algorithm": 2, - "value": "15b0f0cf10e5ede8837e66a0663ae9ef" - } - ], - "downloadCount": 3938 - }, - { - "id": 3274947, - "displayName": "Minecraft Eternal 1.4.3.1", - "fileName": "Minecraft Eternal Client 1.4.3.1.zip", - "fileDate": "2021-04-13T16:08:51.603Z", - "fileLength": 19323498, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3274/947/Minecraft Eternal Client 1.4.3.1.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1770862951, - "type": 3 - } - ], - "packageFingerprint": 2585209844, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2264107, - "fileLegacyMappingId": null, - "projectId": 349129, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 643553302, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2863427, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3274957, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "bd7c4f487322cbdec319f023a0521516dcc11fc0" - }, - { - "algorithm": 2, - "value": "c4aa13bcb7b2afc47cd35d8bb9fa1bf6" - } - ], - "downloadCount": 88699 - }, - { - "id": 3457321, - "displayName": "Minecraft Eternal 1.5", - "fileName": "Minecraft Eternal Client 1.5.zip", - "fileDate": "2021-09-12T10:31:14.177Z", - "fileLength": 20085445, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3457/321/Minecraft Eternal Client 1.5.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3887553017, - "type": 3 - } - ], - "packageFingerprint": 159846428, - "gameVersion": [ - "Forge", - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2497370, - "fileLegacyMappingId": null, - "projectId": 349129, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 743596441, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3299760, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3457331, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "fc46d039714ef14fb3d09f05d23cb18fb0a3b485" - }, - { - "algorithm": 2, - "value": "ff545db4fc64861339b145bb64e613d1" - } - ], - "downloadCount": 169043 - } - ], - "categories": [ - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 349129, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 349129, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 349129, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - }, - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 349129, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 349129, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "minecraft-eternal", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 3457321, - "projectFileName": "Minecraft Eternal Client 1.5.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 3252080, - "projectFileName": "Eternal 1.4.2 Beta.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - } - ], - "isFeatured": false, - "popularityScore": 13341.37890625, - "gamePopularityRank": 1791, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-01T18:29:09.403Z", - "dateCreated": "2019-10-23T21:34:29.74Z", - "dateReleased": "2021-09-12T10:40:29.423Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 456356, - "name": "Medieval Minecraft [FORGE] - 1.16.5", - "authors": [ - { - "name": "xSharkieTV", - "url": "https://www.curseforge.com/members/101555282-xsharkietv?username=xsharkietv", - "projectId": 456356, - "id": 369200, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 101555282, - "twitchId": 146047538 - }, - { - "name": "LunaPixelStudios", - "url": "https://www.curseforge.com/members/102066281-lunapixelstudios?username=lunapixelstudios", - "projectId": 456356, - "id": 432188, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 102066281, - "twitchId": 662941835 - } - ], - "attachments": [ - { - "id": 357852, - "projectId": 456356, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/357/852/256/256/637517774640160181.png", - "title": "637517774640160181.png", - "url": "https://media.forgecdn.net/avatars/357/852/637517774640160181.png", - "status": 1 - }, - { - "id": 357855, - "projectId": 456356, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/357/855/256/256/637517777134779558.png", - "title": "637517777134779558.png", - "url": "https://media.forgecdn.net/avatars/357/855/637517777134779558.png", - "status": 1 - }, - { - "id": 358317, - "projectId": 456356, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/358/317/256/256/637518821972036477.png", - "title": "637518821972036477.png", - "url": "https://media.forgecdn.net/avatars/358/317/637518821972036477.png", - "status": 1 - }, - { - "id": 354385, - "projectId": 456356, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/354/385/256/256/637512088864563224.png", - "title": "637512088864563224.png", - "url": "https://media.forgecdn.net/avatars/354/385/637512088864563224.png", - "status": 1 - }, - { - "id": 427252, - "projectId": 456356, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/427/252/256/256/637658644701171074.png", - "title": "637658644701171074.png", - "url": "https://media.forgecdn.net/avatars/427/252/637658644701171074.png", - "status": 1 - }, - { - "id": 391531, - "projectId": 456356, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/391/531/310/172/logo.png", - "title": "Logo", - "url": "https://media.forgecdn.net/attachments/391/531/logo.png", - "status": 1 - }, - { - "id": 391532, - "projectId": 456356, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/391/532/310/172/medieval-forge-edition-1-1.png", - "title": "Logo2", - "url": "https://media.forgecdn.net/attachments/391/532/medieval-forge-edition-1-1.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://discord.com/invite/9S5tfrY4Fr", - "wikiUrl": "https://github.com/LunaPixelStudios/Medieval-Minecraft-FORGE-1.16.5/wiki", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/medieval-minecraft-modpack", - "gameId": 432, - "summary": "The Medieval Fantasy RPG Adventure & Exploration Minecraft Modpack everyone wants!", - "defaultFileId": 3559397, - "downloadCount": 399256.0, - "latestFiles": [ - { - "id": 3424716, - "displayName": "Medieval Minecraft [FORGE] v25.5", - "fileName": "Medieval Minecraft [FORGE] v25.5.zip", - "fileDate": "2021-08-13T23:17:55.093Z", - "fileLength": 201300164, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3424/716/Medieval Minecraft [FORGE] v25.5.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 1808708065, - "type": 3 - } - ], - "packageFingerprint": 2420561600, - "gameVersion": [ - "1.16.5", - "Forge" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2455210, - "fileLegacyMappingId": null, - "projectId": 456356, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 725668846, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3220229, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3424720, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "1a1471beee3f29e038409ea44c0a1226b8162bc8" - }, - { - "algorithm": 2, - "value": "1d330c8ff135becf5520297568435f63" - } - ], - "downloadCount": 8192 - }, - { - "id": 3539393, - "displayName": "Medieval Minecraft [FORGE] v41", - "fileName": "Medieval Minecraft [FORGE] v41.zip", - "fileDate": "2021-11-28T08:09:22.147Z", - "fileLength": 173726346, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3539/393/Medieval Minecraft [FORGE] v41.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 732391234, - "type": 3 - } - ], - "packageFingerprint": 1818407352, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2601415, - "fileLegacyMappingId": null, - "projectId": 456356, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 794474106, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3501743, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3539400, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "a3e847e899dace661c036fcfe7788cc1bdfbad7c" - }, - { - "algorithm": 2, - "value": "3b1afad5ba605a253d18b9f16ce31268" - } - ], - "downloadCount": 26424 - }, - { - "id": 3554704, - "displayName": "Medieval Minecraft [FORGE] v42", - "fileName": "Medieval Minecraft [FORGE] v42.zip", - "fileDate": "2021-12-08T07:29:13.93Z", - "fileLength": 174065327, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3554/704/Medieval Minecraft [FORGE] v42.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3669407294, - "type": 3 - } - ], - "packageFingerprint": 1024187699, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2619390, - "fileLegacyMappingId": null, - "projectId": 456356, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 802844785, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3540151, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3554713, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "a15bc5afd7b567bb4592d7611721125659b36e3d" - }, - { - "algorithm": 2, - "value": "ac70250797b95c61aeda1df93048f9a1" - } - ], - "downloadCount": 8551 - }, - { - "id": 3559397, - "displayName": "Medieval Minecraft [FORGE] v43", - "fileName": "Medieval Minecraft [FORGE] v43.zip", - "fileDate": "2021-12-11T08:39:08.723Z", - "fileLength": 173590177, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/397/Medieval Minecraft [FORGE] v43.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2698474274, - "type": 3 - } - ], - "packageFingerprint": 3638204693, - "gameVersion": [ - "1.16.5", - "Forge" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624827, - "fileLegacyMappingId": null, - "projectId": 456356, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805518437, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3552488, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3559420, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "e6cc484cb942edce5d1a821c48e6f999f96e376b" - }, - { - "algorithm": 2, - "value": "209d3bfa1c73b8a903884d422eda7df1" - } - ], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 456356, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 456356, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 456356, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 456356, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - }, - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 456356, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "medieval-minecraft-modpack", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.16.5", - "projectFileId": 3559397, - "projectFileName": "Medieval Minecraft [FORGE] v43.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.5", - "projectFileId": 3539393, - "projectFileName": "Medieval Minecraft [FORGE] v41.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - }, - { - "gameVersion": "1.16.4", - "projectFileId": 3487312, - "projectFileName": "Medieval Minecraft [FORGE] v32.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - } - ], - "isFeatured": false, - "popularityScore": 13062.0693359375, - "gamePopularityRank": 1809, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T09:01:33.683Z", - "dateCreated": "2021-03-13T05:08:06.407Z", - "dateReleased": "2021-12-11T08:54:33.24Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 290913, - "name": "StoneBlock", - "authors": [ - { - "name": "Sunekaer", - "url": "https://www.curseforge.com/members/8306579-sunekaer?username=sunekaer", - "projectId": 290913, - "id": 190202, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 8306579, - "twitchId": 39565227 - } - ], - "attachments": [ - { - "id": 147482, - "projectId": 290913, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/147/482/256/256/636576924791375629.png", - "title": "636576924791375629.png", - "url": "https://media.forgecdn.net/avatars/147/482/636576924791375629.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://github.com/Sunekaer/stoneBlock", - "wikiUrl": "", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/stoneblock", - "gameId": 432, - "summary": "Kinda like SkyBlock, except you don't start with a tree and you're surrounded by stone", - "defaultFileId": 3014251, - "downloadCount": 2693194.0, - "latestFiles": [ - { - "id": 2995906, - "displayName": "StoneBlock-1.0.36.zip", - "fileName": "StoneBlock-1.0.36.zip", - "fileDate": "2020-07-03T23:10:43.553Z", - "fileLength": 4821145, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2995/906/StoneBlock-1.0.36.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 455574393, - "type": 3 - } - ], - "packageFingerprint": 2720383399, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1912051, - "fileLegacyMappingId": null, - "projectId": 290913, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 482422438, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2303114, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 2995908, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - }, - { - "id": 3014251, - "displayName": "stoneBlock-1.0.37.zip", - "fileName": "stoneBlock-1.0.37.zip", - "fileDate": "2020-07-25T10:28:31.183Z", - "fileLength": 5070112, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3014/251/stoneBlock-1.0.37.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 388170277, - "type": 3 - } - ], - "packageFingerprint": 966672429, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1934786, - "fileLegacyMappingId": null, - "projectId": 290913, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 494141364, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2339500, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3016966, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 290913, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 290913, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 290913, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4482, - "name": "Extra Large", - "url": "https://www.curseforge.com/minecraft/modpacks/extra-large", - "avatarUrl": "https://media.forgecdn.net/avatars/14/472/635596760403562826.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 290913, - "avatarId": 14472, - "gameId": 432, - "slug": "extra-large", - "dateModified": "2015-02-16T15:34:00.357Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 290913, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "stoneblock", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 3014251, - "projectFileName": "stoneBlock-1.0.37.zip", - "fileType": 1, - "gameVersionFlavor": null - }, - { - "gameVersion": "1.12.2", - "projectFileId": 2995906, - "projectFileName": "StoneBlock-1.0.36.zip", - "fileType": 2, - "gameVersionFlavor": null - } - ], - "isFeatured": false, - "popularityScore": 11724.8642578125, - "gamePopularityRank": 1849, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2020-07-28T07:12:59.63Z", - "dateCreated": "2018-03-26T20:21:19.09Z", - "dateReleased": "2020-07-28T07:09:02.19Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 282762, - "name": "Decimation - Realistic Zombie Apocalypse Modpack", - "authors": [ - { - "name": "BoehMod", - "url": "https://www.curseforge.com/members/101270453-boehmod?username=boehmod", - "projectId": 282762, - "id": 305452, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 101270453, - "twitchId": 486036729 - } - ], - "attachments": [ - { - "id": 132473, - "projectId": 282762, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/132/473/256/256/636475587017635909.jpeg", - "title": "636475587017635909.jpeg", - "url": "https://media.forgecdn.net/avatars/132/473/636475587017635909.jpeg", - "status": 1 - } - ], - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/decimation-realistic-zombie-apocalypse-modpack", - "gameId": 432, - "summary": "Decimation is a Minecraft Zombie Apocalypse Mod, consisting of guns, war, tools and features that create the ultimate Zombie Apocalypse experience!", - "defaultFileId": 3151401, - "downloadCount": 533021.0, - "latestFiles": [ - { - "id": 2504790, - "displayName": "Deci-Modpack 0.16b", - "fileName": "Decimation - Zombie Apocalypse Modpack-0.16b.zip", - "fileDate": "2017-11-29T19:27:06.08Z", - "fileLength": 919, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2504/790/Decimation - Zombie Apocalypse Modpack-0.16b.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2853451393, - "type": 3 - } - ], - "packageFingerprint": 248621216, - "gameVersion": [ - "1.7.10" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000007.0000000010", - "gameVersion": "1.7.10", - "gameVersionReleaseDate": "2014-06-16T00:00:00Z", - "gameVersionName": "1.7.10" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1313782, - "fileLegacyMappingId": null, - "projectId": 282762, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 211857518, - "gameVersionDateReleased": "2014-06-16T00:00:00Z", - "gameVersionMappingId": 1481815, - "gameVersionId": 4449, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - }, - { - "id": 3151401, - "displayName": "1.21.10f", - "fileName": "Decimation - Zombie Apocalypse Modpack-1.21.10f.zip", - "fileDate": "2020-12-28T16:28:47.623Z", - "fileLength": 884, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3151/401/Decimation - Zombie Apocalypse Modpack-1.21.10f.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 4186962818, - "type": 3 - } - ], - "packageFingerprint": 37434162, - "gameVersion": [ - "1.7.10" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000007.0000000010", - "gameVersion": "1.7.10", - "gameVersionReleaseDate": "2014-06-16T00:00:00Z", - "gameVersionName": "1.7.10" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2104692, - "fileLegacyMappingId": null, - "projectId": 282762, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 575696641, - "gameVersionDateReleased": "2014-06-16T00:00:00Z", - "gameVersionMappingId": 2612129, - "gameVersionId": 4449, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 282762, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4475, - "name": "Adventure and RPG", - "url": "https://www.curseforge.com/minecraft/modpacks/adventure-and-rpg", - "avatarUrl": "https://media.forgecdn.net/avatars/14/480/635596775049811800.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 282762, - "avatarId": 14480, - "gameId": 432, - "slug": "adventure-and-rpg", - "dateModified": "2015-02-16T15:58:24.98Z" - }, - { - "categoryId": 4482, - "name": "Extra Large", - "url": "https://www.curseforge.com/minecraft/modpacks/extra-large", - "avatarUrl": "https://media.forgecdn.net/avatars/14/472/635596760403562826.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 282762, - "avatarId": 14472, - "gameId": 432, - "slug": "extra-large", - "dateModified": "2015-02-16T15:34:00.357Z" - }, - { - "categoryId": 4483, - "name": "Combat / PvP", - "url": "https://www.curseforge.com/minecraft/modpacks/combat-pvp", - "avatarUrl": "https://media.forgecdn.net/avatars/14/313/635591779575605594.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 282762, - "avatarId": 14313, - "gameId": 432, - "slug": "combat-pvp", - "dateModified": "2015-02-10T21:12:37.56Z" - }, - { - "categoryId": 4484, - "name": "Multiplayer", - "url": "https://www.curseforge.com/minecraft/modpacks/multiplayer", - "avatarUrl": "https://media.forgecdn.net/avatars/14/481/635596792838491141.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 282762, - "avatarId": 14481, - "gameId": 432, - "slug": "multiplayer", - "dateModified": "2015-02-16T16:28:03.85Z" - } - ], - "status": 4, - "primaryCategoryId": 4475, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "decimation-realistic-zombie-apocalypse-modpack", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.7.10", - "projectFileId": 3151401, - "projectFileName": "Decimation - Zombie Apocalypse Modpack-1.21.10f.zip", - "fileType": 1, - "gameVersionFlavor": null - }, - { - "gameVersion": "1.7.10", - "projectFileId": 2504790, - "projectFileName": "Decimation - Zombie Apocalypse Modpack-0.16b.zip", - "fileType": 2, - "gameVersionFlavor": null - } - ], - "isFeatured": false, - "popularityScore": 4940.8564453125, - "gamePopularityRank": 1854, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2020-12-28T16:29:24.723Z", - "dateCreated": "2017-11-29T19:25:01.733Z", - "dateReleased": "2020-12-28T16:28:47.623Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 439293, - "name": "All in One [Modded One Block]", - "authors": [ - { - "name": "NaturaSpell", - "url": "https://www.curseforge.com/members/43205480-naturaspell?username=naturaspell", - "projectId": 439293, - "id": 351017, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 43205480, - "twitchId": 180471771 - } - ], - "attachments": [ - { - "id": 335330, - "projectId": 439293, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/335/330/256/256/637470212015765057.png", - "title": "637470212015765057.png", - "url": "https://media.forgecdn.net/avatars/335/330/637470212015765057.png", - "status": 1 - }, - { - "id": 342091, - "projectId": 439293, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/342/91/256/256/637485703554418199.png", - "title": "637485703554418199.png", - "url": "https://media.forgecdn.net/avatars/342/91/637485703554418199.png", - "status": 1 - }, - { - "id": 338196, - "projectId": 439293, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/338/196/310/172/quests.png", - "title": "FTB Quests", - "url": "https://media.forgecdn.net/attachments/338/196/quests.png", - "status": 1 - }, - { - "id": 346251, - "projectId": 439293, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/346/251/310/172/lava_tank.png", - "title": "Pump out lava tank", - "url": "https://media.forgecdn.net/attachments/346/251/lava_tank.png", - "status": 1 - }, - { - "id": 335583, - "projectId": 439293, - "description": "You need to choose \"Garden of Glass\" generation to play in skyblock", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/335/583/310/172/generation-skyblock.png", - "title": "skyblock", - "url": "https://media.forgecdn.net/attachments/335/583/generation-skyblock.png", - "status": 1 - } - ], - "wikiUrl": "", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/all-in-one-modded-one-block", - "gameId": 432, - "summary": "A true modded One Block Pack with 11 different phases.", - "defaultFileId": 3550433, - "downloadCount": 474268.0, - "latestFiles": [ - { - "id": 3532877, - "displayName": "All in One Pack 1.4.7", - "fileName": "AllinOne-1.4.7.zip", - "fileDate": "2021-11-21T17:51:25.25Z", - "fileLength": 631836, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3532/877/AllinOne-1.4.7.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 4204844105, - "type": 3 - } - ], - "packageFingerprint": 1287611705, - "gameVersion": [ - "1.16.5" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2593096, - "fileLegacyMappingId": null, - "projectId": 439293, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 789769493, - "gameVersionDateReleased": "2021-01-15T14:14:48.91Z", - "gameVersionMappingId": 3485063, - "gameVersionId": 8203, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3532888, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "5493096841402ece6d72ec5aeff35c89ef3cc68e" - }, - { - "algorithm": 2, - "value": "7900c545d3e03a2703f9ba554a75fd1e" - } - ], - "downloadCount": 23274 - }, - { - "id": 3550433, - "displayName": "All in One Pack 1.4.9", - "fileName": "AllinOne-1.4.9.zip", - "fileDate": "2021-12-05T15:43:58.173Z", - "fileLength": 636677, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3550/433/AllinOne-1.4.9.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2162336281, - "type": 3 - } - ], - "packageFingerprint": 2532407421, - "gameVersion": [ - "1.16.5", - "Forge" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000016.0000000005", - "gameVersion": "1.16.5", - "gameVersionReleaseDate": "2021-01-15T14:14:48.91Z", - "gameVersionName": "1.16.5", - "gameVersionTypeId": 70886 - }, - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2614376, - "fileLegacyMappingId": null, - "projectId": 439293, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 800539692, - "gameVersionDateReleased": "2019-08-01T00:00:00Z", - "gameVersionMappingId": 3530646, - "gameVersionId": 7498, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3550440, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "7ba16f9483f67ac8d5e477d35351ea7885eb0cfa" - }, - { - "algorithm": 2, - "value": "c87e1d81f8e5cf74552cd9fd4a72146a" - } - ], - "downloadCount": 1 - } - ], - "categories": [ - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 439293, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4473, - "name": "Magic", - "url": "https://www.curseforge.com/minecraft/modpacks/magic", - "avatarUrl": "https://media.forgecdn.net/avatars/14/474/635596760578719019.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 439293, - "avatarId": 14474, - "gameId": 432, - "slug": "magic", - "dateModified": "2015-02-16T15:34:17.873Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 439293, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 439293, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4736, - "name": "Skyblock", - "url": "https://www.curseforge.com/minecraft/modpacks/skyblock", - "avatarUrl": "https://media.forgecdn.net/avatars/162/818/636678840408956323.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 439293, - "avatarId": 162818, - "gameId": 432, - "slug": "skyblock", - "dateModified": "2018-07-22T19:20:40.897Z" - } - ], - "status": 4, - "primaryCategoryId": 4736, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "all-in-one-modded-one-block", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.16.5", - "projectFileId": 3550433, - "projectFileName": "AllinOne-1.4.9.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 70886 - } - ], - "isFeatured": false, - "popularityScore": 12423.21484375, - "gamePopularityRank": 1879, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-05T15:59:50.86Z", - "dateCreated": "2021-01-23T17:53:21.553Z", - "dateReleased": "2021-12-05T15:55:05.607Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 268208, - "name": "SevTech: Ages", - "authors": [ - { - "name": "darkosto", - "url": "https://www.curseforge.com/members/15754032-darkosto?username=darkosto", - "projectId": 268208, - "id": 165006, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 15754032, - "twitchId": 63937599 - }, - { - "name": "Firstnecron", - "url": "https://www.curseforge.com/members/809144-firstnecron?username=firstnecron", - "projectId": 268208, - "id": 189976, - "projectTitleId": 2, - "projectTitleTitle": "Author", - "userId": 809144, - "twitchId": 36108934 - } - ], - "attachments": [ - { - "id": 147067, - "projectId": 268208, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/147/67/256/256/636574428512291945.png", - "title": "636574428512291945.png", - "url": "https://media.forgecdn.net/avatars/147/67/636574428512291945.png", - "status": 1 - }, - { - "id": 99917, - "projectId": 268208, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/99/917/256/256/636311391102627739.png", - "title": "636311391102627739.png", - "url": "https://media.forgecdn.net/avatars/99/917/636311391102627739.png", - "status": 1 - } - ], - "issueTrackerUrl": "https://github.com/DarkPacks/SevTech-Ages/issues", - "wikiUrl": "https://sevtechages.gamepedia.com/SevTech:_Ages_Wiki", - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/sevtech-ages", - "gameId": 432, - "summary": "The ultimate advanced progression modpack! Watch development at: darkosto.tv/SevTechLive", - "defaultFileId": 3559141, - "downloadCount": 4651757.0, - "latestFiles": [ - { - "id": 2549302, - "displayName": "SevTech: Ages - 3.0.5", - "fileName": "SevTech Ages-3.0.5.zip", - "fileDate": "2018-04-06T23:31:55.11Z", - "fileLength": 19617706, - "releaseType": 3, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2549/302/SevTech Ages-3.0.5.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 167329033, - "type": 3 - } - ], - "packageFingerprint": 4129718692, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1368455, - "fileLegacyMappingId": null, - "projectId": 268208, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 234798466, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1561254, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "ed1ebff84a4fd94b8594f61640136df5f2071fdf" - }, - { - "algorithm": 2, - "value": "35644cd99685244b5431e245b2f213d6" - } - ], - "downloadCount": 34369 - }, - { - "id": 3096770, - "displayName": "SevTech: Ages - 3.2.0", - "fileName": "SevTech Ages-3.2.0.zip", - "fileDate": "2020-10-30T03:46:10.453Z", - "fileLength": 21082952, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3096/770/SevTech Ages-3.2.0.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 962663531, - "type": 3 - } - ], - "packageFingerprint": 1655652121, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2035790, - "fileLegacyMappingId": null, - "projectId": 268208, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 544555829, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2502605, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3103908, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "15268265537085a701eab9989cc385224506189a" - }, - { - "algorithm": 2, - "value": "1eb7c0e87921426aca0442322f5dcd19" - } - ], - "downloadCount": 17471 - }, - { - "id": 3123514, - "displayName": "SevTech: Ages - 3.2.1", - "fileName": "SevTech-Ages_3.2.1.zip", - "fileDate": "2020-11-26T05:29:07.637Z", - "fileLength": 21045307, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3123/514/SevTech-Ages_3.2.1.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 3102308051, - "type": 3 - } - ], - "packageFingerprint": 1479697252, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2068697, - "fileLegacyMappingId": null, - "projectId": 268208, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 559573780, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 2558043, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 3125814, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "55ebe93d90132b9e8cb159f4ac068c6936eab237" - }, - { - "algorithm": 2, - "value": "dbf575bdb5aa9660f9376f063d89cbbd" - } - ], - "downloadCount": 462209 - }, - { - "id": 3559141, - "displayName": "SevTech: Ages - 3.2.2", - "fileName": "SevTech-Ages-3.2.2.zip", - "fileDate": "2021-12-11T02:12:23.463Z", - "fileLength": 20966488, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/3559/141/SevTech-Ages-3.2.2.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2357557322, - "type": 3 - } - ], - "packageFingerprint": 1883153563, - "gameVersion": [ - "Forge", - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0", - "gameVersion": "", - "gameVersionReleaseDate": "2019-08-01T00:00:00Z", - "gameVersionName": "Forge", - "gameVersionTypeId": 68441 - }, - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2", - "gameVersionTypeId": 628 - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 2624504, - "fileLegacyMappingId": null, - "projectId": 268208, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 805338758, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 3551573, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": null, - "gameVersionFlavor": null, - "hashes": [ - { - "algorithm": 1, - "value": "fa1f5a0cea62f55d1e451fae6f736ed2d8ae4c4a" - }, - { - "algorithm": 2, - "value": "88610c67145e1b96bdf95d3b22cb6f97" - } - ], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 268208, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 268208, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4482, - "name": "Extra Large", - "url": "https://www.curseforge.com/minecraft/modpacks/extra-large", - "avatarUrl": "https://media.forgecdn.net/avatars/14/472/635596760403562826.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 268208, - "avatarId": 14472, - "gameId": 432, - "slug": "extra-large", - "dateModified": "2015-02-16T15:34:00.357Z" - }, - { - "categoryId": 4476, - "name": "Exploration", - "url": "https://www.curseforge.com/minecraft/modpacks/exploration", - "avatarUrl": "https://media.forgecdn.net/avatars/14/486/635596815896417213.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 268208, - "avatarId": 14486, - "gameId": 432, - "slug": "exploration", - "dateModified": "2015-02-16T17:06:29.69Z" - } - ], - "status": 4, - "primaryCategoryId": 4472, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "sevtech-ages", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 3559141, - "projectFileName": "SevTech-Ages-3.2.2.zip", - "fileType": 1, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 3096770, - "projectFileName": "SevTech Ages-3.2.0.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12", - "projectFileId": 3038158, - "projectFileName": "SevTech-Ages_3.1.6-hotfix.1.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.1", - "projectFileId": 3012795, - "projectFileName": "SevTech-Ages_3.1.5.zip", - "fileType": 2, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - }, - { - "gameVersion": "1.12.2", - "projectFileId": 2549302, - "projectFileName": "SevTech Ages-3.0.5.zip", - "fileType": 3, - "gameVersionFlavor": null, - "gameVersionTypeId": 628 - } - ], - "isFeatured": false, - "popularityScore": 12186.3466796875, - "gamePopularityRank": 1891, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2021-12-11T02:25:47.853Z", - "dateCreated": "2017-05-23T17:25:10.183Z", - "dateReleased": "2021-12-11T02:12:23.463Z", - "isAvailable": true, - "isExperiemental": false - }, - { - "id": 303648, - "name": "GregBlock", - "authors": [ - { - "name": "TheLimePixel", - "url": "https://www.curseforge.com/members/26268145-thelimepixel?username=thelimepixel", - "projectId": 303648, - "id": 204437, - "projectTitleId": null, - "projectTitleTitle": null, - "userId": 26268145, - "twitchId": 94386437 - } - ], - "attachments": [ - { - "id": 176826, - "projectId": 303648, - "description": "", - "isDefault": true, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/176/826/256/256/636766588613534389.png", - "title": "636766588613534389.png", - "url": "https://media.forgecdn.net/avatars/176/826/636766588613534389.png", - "status": 1 - }, - { - "id": 172192, - "projectId": 303648, - "description": "", - "isDefault": false, - "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/172/192/256/256/636734850816562021.png", - "title": "636734850816562021.png", - "url": "https://media.forgecdn.net/avatars/172/192/636734850816562021.png", - "status": 1 - } - ], - "websiteUrl": "https://www.curseforge.com/minecraft/modpacks/gregblock", - "gameId": 432, - "summary": "A skyblock based around automation, industrialization and complexity, an Ex Nihilo skyblock based around GregTech.", - "defaultFileId": 2692706, - "downloadCount": 86795.0, - "latestFiles": [ - { - "id": 2692706, - "displayName": "GregBlock-2.2.0", - "fileName": "GregBlock-2.2.0.zip", - "fileDate": "2019-03-28T20:08:04.36Z", - "fileLength": 11230622, - "releaseType": 1, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2692/706/GregBlock-2.2.0.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2518248284, - "type": 3 - } - ], - "packageFingerprint": 108612250, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1537979, - "fileLegacyMappingId": null, - "projectId": 303648, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 303892909, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1780724, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 2692717, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - }, - { - "id": 2697712, - "displayName": "GregBlock-2.2.1", - "fileName": "GregBlock-2.2.1.zip", - "fileDate": "2019-04-10T19:59:45.857Z", - "fileLength": 11239744, - "releaseType": 2, - "fileStatus": 4, - "downloadUrl": "https://edge.forgecdn.net/files/2697/712/GregBlock-2.2.1.zip", - "isAlternate": false, - "alternateFileId": 0, - "dependencies": [], - "isAvailable": true, - "modules": [ - { - "foldername": "manifest.json", - "fingerprint": 2966149442, - "type": 3 - } - ], - "packageFingerprint": 3946343959, - "gameVersion": [ - "1.12.2" - ], - "sortableGameVersion": [ - { - "gameVersionPadded": "0000000001.0000000012.0000000002", - "gameVersion": "1.12.2", - "gameVersionReleaseDate": "2017-09-18T05:00:00Z", - "gameVersionName": "1.12.2" - } - ], - "installMetadata": null, - "changelog": null, - "hasInstallScript": false, - "isCompatibleWithClient": true, - "categorySectionPackageType": 5, - "restrictProjectFileAccess": 1, - "projectStatus": 4, - "renderCacheId": 1543862, - "fileLegacyMappingId": null, - "projectId": 303648, - "parentProjectFileId": null, - "parentFileLegacyMappingId": null, - "fileTypeId": null, - "exposeAsAlternative": null, - "packageFingerprintId": 306879627, - "gameVersionDateReleased": "2017-09-18T05:00:00Z", - "gameVersionMappingId": 1789429, - "gameVersionId": 6756, - "gameId": 432, - "isServerPack": false, - "serverPackFileId": 2697723, - "gameVersionFlavor": null, - "hashes": [], - "downloadCount": 0 - } - ], - "categories": [ - { - "categoryId": 4736, - "name": "Skyblock", - "url": "https://www.curseforge.com/minecraft/modpacks/skyblock", - "avatarUrl": "https://media.forgecdn.net/avatars/162/818/636678840408956323.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 303648, - "avatarId": 162818, - "gameId": 432, - "slug": "skyblock", - "dateModified": "2018-07-22T19:20:40.897Z" - }, - { - "categoryId": 4478, - "name": "Quests", - "url": "https://www.curseforge.com/minecraft/modpacks/quests", - "avatarUrl": "https://media.forgecdn.net/avatars/14/487/635596816137981263.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 303648, - "avatarId": 14487, - "gameId": 432, - "slug": "quests", - "dateModified": "2015-02-16T17:06:53.797Z" - }, - { - "categoryId": 4481, - "name": "Small / Light", - "url": "https://www.curseforge.com/minecraft/modpacks/small-light", - "avatarUrl": "https://media.forgecdn.net/avatars/14/478/635596761449660932.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 303648, - "avatarId": 14478, - "gameId": 432, - "slug": "small-light", - "dateModified": "2015-02-16T15:35:44.967Z" - }, - { - "categoryId": 4472, - "name": "Tech", - "url": "https://www.curseforge.com/minecraft/modpacks/tech", - "avatarUrl": "https://media.forgecdn.net/avatars/14/479/635596761534662757.png", - "parentId": 4471, - "rootId": 4471, - "projectId": 303648, - "avatarId": 14479, - "gameId": 432, - "slug": "tech", - "dateModified": "2015-02-16T15:35:53.467Z" - } - ], - "status": 4, - "primaryCategoryId": 4736, - "categorySection": { - "id": 11, - "gameId": 432, - "name": "Modpacks", - "packageType": 5, - "path": "downloads", - "initialInclusionPattern": "$^", - "extraIncludePattern": null, - "gameCategoryId": 4471 - }, - "slug": "gregblock", - "gameVersionLatestFiles": [ - { - "gameVersion": "1.12.2", - "projectFileId": 2697712, - "projectFileName": "GregBlock-2.2.1.zip", - "fileType": 2, - "gameVersionFlavor": null - }, - { - "gameVersion": "1.12.2", - "projectFileId": 2692706, - "projectFileName": "GregBlock-2.2.0.zip", - "fileType": 1, - "gameVersionFlavor": null - } - ], - "isFeatured": false, - "popularityScore": 1908.89111328125, - "gamePopularityRank": 1957, - "primaryLanguage": "enUS", - "gameSlug": "minecraft", - "gameName": "Minecraft", - "portalName": "www.curseforge.com", - "dateModified": "2019-04-11T04:49:02.553Z", - "dateCreated": "2018-09-25T15:11:21.61Z", - "dateReleased": "2019-04-10T20:25:46.18Z", - "isAvailable": true, - "isExperiemental": false - } -] \ No newline at end of file diff --git a/test/data/FTB-Modpack-35.json b/test/data/FTB-Modpack-35.json deleted file mode 100644 index c6e28d9b6..000000000 --- a/test/data/FTB-Modpack-35.json +++ /dev/null @@ -1,194 +0,0 @@ -{ - "synopsis": "Revelation is a general all-purpose modpack with optimal FPS, server performance and stability.", - "description": "Revelation is a general all-purpose modpack with optimal FPS, server performance and stability. This is the largest pack ever built to date by the Feed The Beast Team. Ongoing development, support and updates ensure you will have the best experience as a player whether your a seasoned player or new to modded Minecraft. \n \nRevelation is not a themed pack nor a focused pack, its a pack designed to cater to a large amount of players, whether you like tech mods, magic mods, exploration, building or all of those then this pack is for you. With a Forge Mod Loader count of well over 200, this pack has most everything a player is looking for with performance and stability being the main focus of design and mod choice. Minimal changes have been made to the mods so you experience each mod the way the developers intended. Also this proves very helpful when learning new mods using a Wiki. \n \nLarge well-known mods such as EnderIO, Applied Energistics, Thaumcraft, Botania, Thermal Expansion, Immersive Engineering and Project Red are present in Revelation, as well as some lesser known mods like Rustic, Gadgetry, Thuts Elevators and Xnet. Twilight Forest also makes a comeback in Revelation v2.0.0+ for all the players that enjoy exploration. \n \nWhether you want to build in a cave and reach the end of progression in a week, or build complex bases loaded with tech and magic infrastructure all while armoring up to take on the bosses in Twilight Forest, then Revelation is the perfect modpack for you. \n \n\\* A minimum of 4 GB of RAM is recommended for clients as well as servers for best performance \n\\* Latest version of JAVA 8 is recommended \n\\* JAVA 9 is NOT supported", - "art": [ - { - "width": 676, - "height": 676, - "compressed": true, - "url": "https://apps.modpacks.ch/modpacks/art/7/revelation.png", - "mirrors": [], - "sha1": "f4421773eb0c0fb95be2a52bceb035037351f376", - "size": 384443, - "id": 202, - "type": "square", - "updated": 1605189465 - } - ], - "links": [ - { - "id": 2, - "name": "Direwolf20 plays FTB Revelations", - "link": "https://www.youtube.com/watch?v=MxbYy4ROFVc", - "type": "video" - } - ], - "authors": [ - { - "website": "", - "id": 8, - "name": "FTB Team", - "type": "team", - "updated": 1585874706 - } - ], - "versions": [ - { - "specs": { - "id": 35, - "minimum": 4096, - "recommended": 6124 - }, - "targets": [ - { - "version": "14.23.5.2846", - "id": 1, - "name": "forge", - "type": "modloader", - "updated": 1585685146 - }, - { - "version": "1.12.2", - "id": 2, - "name": "minecraft", - "type": "game", - "updated": 1585685146 - } - ], - "id": 37, - "name": "3.2.0", - "type": "Release", - "updated": 1585743471 - }, - { - "specs": { - "id": 154, - "minimum": 4608, - "recommended": 6144 - }, - "targets": [ - { - "version": "14.23.5.2846", - "id": 1, - "name": "forge", - "type": "modloader", - "updated": 1585685146 - }, - { - "version": "1.12.2", - "id": 2, - "name": "minecraft", - "type": "game", - "updated": 1585685146 - } - ], - "id": 143, - "name": "3.3.0", - "type": "Release", - "updated": 1603038398 - }, - { - "specs": { - "id": 185, - "minimum": 4096, - "recommended": 6144 - }, - "targets": [ - { - "version": "14.23.5.2846", - "id": 1, - "name": "forge", - "type": "modloader", - "updated": 1585685146 - }, - { - "version": "1.12.2", - "id": 2, - "name": "minecraft", - "type": "game", - "updated": 1585685146 - } - ], - "id": 174, - "name": "3.4.0", - "type": "Release", - "updated": 1606846218 - }, - { - "specs": { - "id": 257, - "minimum": 4096, - "recommended": 6144 - }, - "targets": [ - { - "version": "14.23.5.2855", - "id": 621, - "name": "forge", - "type": "modloader", - "updated": 1620406428 - }, - { - "version": "1.12.2", - "id": 2, - "name": "minecraft", - "type": "game", - "updated": 1585685146 - } - ], - "id": 2059, - "name": "3.5.0", - "type": "Beta", - "updated": 1622742043 - } - ], - "installs": 798186, - "plays": 6557405, - "tags": [ - { - "id": 6, - "name": "1.12.2" - }, - { - "id": 9, - "name": "FTB" - }, - { - "id": 12, - "name": "Magic" - }, - { - "id": 13, - "name": "Tech" - }, - { - "id": 23, - "name": "Exploration" - }, - { - "id": 22, - "name": "Large" - } - ], - "featured": false, - "refreshed": 1639289124, - "notification": "", - "rating": { - "id": -1, - "configured": false, - "verified": false, - "age": 7, - "gambling": false, - "frightening": true, - "alcoholdrugs": false, - "nuditysexual": false, - "sterotypeshate": false, - "language": false, - "violence": true - }, - "status": "success", - "id": 35, - "name": "FTB Revelation", - "type": "release", - "updated": 1585743470 -} \ No newline at end of file diff --git a/test/data/FTB-Modpack.json b/test/data/FTB-Modpack.json deleted file mode 100644 index 7988851c4..000000000 --- a/test/data/FTB-Modpack.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "packs": [ - 35 - ], - "total": 1, - "limit": 0, - "status": "success", - "refreshed": 1639288949 -} \ No newline at end of file diff --git a/test/data/FTB-Tags.json b/test/data/FTB-Tags.json deleted file mode 100644 index 7efa83502..000000000 --- a/test/data/FTB-Tags.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "tags": [ - "FTB", - "Tech", - "Magic", - "Exploration", - "Large", - "1.7.10", - "1.12.2", - "Questing", - "Map", - "Challenge", - "1.4.7", - "Light", - "Kitchen", - "Sink", - "1.6.4", - "1.16", - "1.16.5", - "1.10.2", - "Creative", - "Building", - "Combat", - "Skyblock", - "Expert", - "1.5.2", - "Adventure", - "1.2.5", - "Small", - "1.16.3", - "1.15.2", - "1.14.4", - "Unstable", - "Sci-Fi", - "1.4.2", - "Fabric", - "1.15", - "1.12.1", - "ME5", - "Hardcore", - "PvP", - "RPG", - "1.16.4" - ], - "total": 41, - "limit": 100, - "refreshed": 1639288825, - "status": "success" -} \ No newline at end of file diff --git a/test/data/Fabric-1.17.1-args.json b/test/data/Fabric-1.17.1-args.json deleted file mode 100644 index 11410a93f..000000000 --- a/test/data/Fabric-1.17.1-args.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "game": [ - "--username", - "${auth_player_name}", - "--version", - "${version_name}", - "--gameDir", - "${game_directory}", - "--assetsDir", - "${assets_root}", - "--assetIndex", - "${assets_index_name}", - "--uuid", - "${auth_uuid}", - "--accessToken", - "${auth_access_token}", - "--userType", - "${user_type}", - "--versionType", - "${version_type}", - { - "rules": [ - { - "action": "allow", - "features": { - "is_demo_user": true - } - } - ], - "value": "--demo" - }, - { - "rules": [ - { - "action": "allow", - "features": { - "has_custom_resolution": true - } - } - ], - "value": [ - "--width", - "${resolution_width}", - "--height", - "${resolution_height}" - ] - } - ], - "jvm": [ - { - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ], - "value": [ - "-XstartOnFirstThread" - ] - }, - { - "rules": [ - { - "action": "allow", - "os": { - "name": "windows" - } - } - ], - "value": "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump" - }, - { - "rules": [ - { - "action": "allow", - "os": { - "name": "windows", - "version": "^10\\." - } - } - ], - "value": [ - "-Dos.name=Windows 10", - "-Dos.version=10.0" - ] - }, - { - "rules": [ - { - "action": "allow", - "os": { - "arch": "x86" - } - } - ], - "value": "-Xss1M" - }, - "-Djava.library.path=${natives_directory}", - "-Dminecraft.launcher.brand=${launcher_name}", - "-Dminecraft.launcher.version=${launcher_version}", - "-cp", - "${classpath}" - ], - "mainClass": "net.fabricmc.loader.impl.launch.knot.KnotClient" -} \ No newline at end of file diff --git a/test/data/Fabric-1.18-log.txt b/test/data/Fabric-1.18-log.txt deleted file mode 100644 index 6174291db..000000000 --- a/test/data/Fabric-1.18-log.txt +++ /dev/null @@ -1,104 +0,0 @@ -[21:15:04] [main/INFO]: Loading for game Minecraft 1.18 -[21:15:05] [main/INFO]: Loading 71 mods: - - blue_endless_jankson 1.2.1 via jankson - - cloth-basic-math 0.6.0 via cloth-config - - cloth-config 6.0.45 via rpmtw_update_mod - - com_squareup_okhttp3_okhttp 4.9.2 via rpmtw_update_mod - - com_squareup_okio_okio 2.10.0 via rpmtw_update_mod - - fabric 0.44.0+1.18 - - fabric-api-base 0.4.1+b4f4f6cdc8 via fabric - - fabric-api-lookup-api-v1 1.4.0+16d92c47c8 via fabric - - fabric-biome-api-v1 6.0.1+ded849a9c8 via fabric - - fabric-blockrenderlayer-v1 1.1.9+3ac43d95c8 via fabric - - fabric-command-api-v1 1.1.6+3ac43d95c8 via fabric - - fabric-commands-v0 0.2.5+b4f4f6cdc8 via fabric - - fabric-containers-v0 0.1.18+d154e2c6c8 via fabric - - fabric-content-registries-v0 0.4.5+6f53a73dc8 via fabric - - fabric-crash-report-info-v1 0.1.8+3ac43d95c8 via fabric - - fabric-dimensions-v1 2.1.7+43d29571c8 via fabric - - fabric-entity-events-v1 1.4.5+6b21378ac8 via fabric - - fabric-events-interaction-v0 0.4.16+bfa23f17c8 via fabric - - fabric-events-lifecycle-v0 0.2.6+b4f4f6cdc8 via fabric - - fabric-game-rule-api-v1 1.0.10+3ac43d95c8 via fabric - - fabric-item-api-v1 1.3.0+691a79b5c8 via fabric - - fabric-item-groups-v0 0.3.3+3ac43d95c8 via fabric - - fabric-key-binding-api-v1 1.0.8+c8aba2f3c8 via fabric - - fabric-keybindings-v0 0.2.6+b4f4f6cdc8 via fabric - - fabric-language-kotlin 1.7.0+kotlin.1.6.0 via rpmtw_update_mod - - fabric-lifecycle-events-v1 1.4.10+c15ca335c8 via fabric - - fabric-loot-tables-v1 1.0.8+3ac43d95c8 via fabric - - fabric-mining-level-api-v1 1.0.3+3ac43d95c8 via fabric - - fabric-mining-levels-v0 0.1.7+b4f4f6cdc8 via fabric - - fabric-models-v0 0.3.3+3ac43d95c8 via fabric - - fabric-networking-api-v1 1.0.18+3ac43d95c8 via fabric - - fabric-networking-v0 0.3.5+b4f4f6cdc8 via fabric - - fabric-object-builder-api-v1 1.11.0+3b82842ec8 via fabric - - fabric-object-builders-v0 0.7.8+3ac43d95c8 via fabric - - fabric-particles-v1 0.2.9+526dc1acc8 via fabric - - fabric-registry-sync-v0 0.8.5+3ac43d95c8 via fabric - - fabric-renderer-api-v1 0.4.9+3ac43d95c8 via fabric - - fabric-renderer-indigo 0.4.12+3ac43d95c8 via fabric - - fabric-renderer-registries-v1 3.2.7+b4f4f6cdc8 via fabric - - fabric-rendering-data-attachment-v1 0.3.4+7242e9d7c8 via fabric - - fabric-rendering-fluids-v1 0.1.18+3ac43d95c8 via fabric - - fabric-rendering-v0 1.1.9+b4f4f6cdc8 via fabric - - fabric-rendering-v1 1.10.3+6b21378ac8 via fabric - - fabric-resource-loader-v0 0.4.11+3ac43d95c8 via fabric - - fabric-screen-api-v1 1.0.7+3ac43d95c8 via fabric - - fabric-screen-handler-api-v1 1.1.11+3ac43d95c8 via fabric - - fabric-structure-api-v1 2.0.8+295197a7c8 via fabric - - fabric-tag-extensions-v0 1.2.5+3ac43d95c8 via fabric - - fabric-textures-v0 1.0.9+3ac43d95c8 via fabric - - fabric-tool-attribute-api-v1 1.3.4+7de09f55c8 via fabric - - fabric-transfer-api-v1 1.5.5+b4f4f6cdc8 via fabric - - fabricloader 0.12.8 - - io_socket_engine_io-client 2.0.0 via rpmtw_update_mod - - io_socket_socket_io-client 2.0.1 via rpmtw_update_mod - - jankson 4.0.0+j1.2.0 via libgui - - java 17 - - libgui 5.0.0-beta.1+1.18-pre2 via rpmtw_update_mod - - libninepatch 1.1.0 via rpmtw_update_mod - - minecraft 1.18 - - net_lingala_zip4j_zip4j 2.9.1 via rpmtw_update_mod - - org_jetbrains_kotlin_kotlin-reflect 1.6.0 via fabric-language-kotlin - - org_jetbrains_kotlin_kotlin-stdlib 1.6.0 via fabric-language-kotlin - - org_jetbrains_kotlin_kotlin-stdlib-jdk7 1.6.0 via fabric-language-kotlin - - org_jetbrains_kotlin_kotlin-stdlib-jdk8 1.6.0 via fabric-language-kotlin - - org_jetbrains_kotlinx_kotlinx-coroutines-core-jvm 1.5.2 via fabric-language-kotlin - - org_jetbrains_kotlinx_kotlinx-coroutines-jdk8 1.5.2 via fabric-language-kotlin - - org_jetbrains_kotlinx_kotlinx-serialization-cbor-jvm 1.3.1 via fabric-language-kotlin - - org_jetbrains_kotlinx_kotlinx-serialization-core-jvm 1.3.1 via fabric-language-kotlin - - org_jetbrains_kotlinx_kotlinx-serialization-json-jvm 1.3.1 via fabric-language-kotlin - - org_json_json 20210307 via rpmtw_update_mod - - rpmtw_update_mod 1.2.7 -[21:15:05] [main/INFO]: SpongePowered MIXIN Subsystem Version=0.8.4 Source=file:/home/siongsng/RPMLauncher/data/libraries/net/fabricmc/sponge-mixin/0.10.7+mixin.0.8.4/sponge-mixin-0.10.7+mixin.0.8.4.jar Service=Knot/Fabric Env=CLIENT -[21:15:05] [main/INFO]: Compatibility level set to JAVA_16 -[21:15:05] [main/INFO]: Compatibility level set to JAVA_17 -[21:15:05] [main/WARN]: Error loading class: vazkii/patchouli/client/book/BookContentClasspathLoader (java.lang.ClassNotFoundException: vazkii/patchouli/client/book/BookContentClasspathLoader) -[21:15:05] [main/WARN]: @Mixin target vazkii.patchouli.client.book.BookContentClasspathLoader was not found RpmtwUpdateMod.mixins.json:MixinBookContents -[21:15:05] [main/WARN]: Error loading class: vazkii/patchouli/client/book/BookContentExternalLoader (java.lang.ClassNotFoundException: vazkii/patchouli/client/book/BookContentExternalLoader) -[21:15:05] [main/WARN]: @Mixin target vazkii.patchouli.client.book.BookContentExternalLoader was not found RpmtwUpdateMod.mixins.json:MixinContentExternalLoader -[21:15:08] [main/WARN]: @Inject(@At("INVOKE")) Shift.BY=3 on fabric-lifecycle-events-v1.mixins.json:client.WorldChunkMixin::handler$zdf000$onLoadBlockEntity exceeds the maximum allowed value: 0. Increase the value of maxShiftBy to suppress this warning. -[21:15:09] [Render thread/INFO]: 正在準備下載翻譯包... -[21:15:10] [Render thread/INFO]: 篩選耗時:0.026 秒。 -[21:15:10] [Render thread/INFO]: Environment: authHost='https://authserver.mojang.com', accountsHost='https://api.mojang.com', sessionHost='https://sessionserver.mojang.com', servicesHost='https://api.minecraftservices.com', name='PROD' -[21:15:11] [Render thread/INFO]: Setting user: SiongSng -[21:15:12] [Render thread/INFO]: [Indigo] Registering Indigo renderer! -[21:15:12] [Render thread/INFO]: Hello RPMTW world! -[21:15:12] [Render thread/INFO]: Backend library: LWJGL version 3.2.2 build 10 -[21:15:14] [Render thread/INFO]: Narrator library successfully loaded -[21:15:15] [Render thread/INFO]: Reloading ResourceManager: Default, Fabric Mods (Cloth Config v6, Fabric API, Fabric API Base, Fabric API Lookup API (v1), Fabric Biome API (v1), Fabric BlockRenderLayer Registration (v1), Fabric Command API (v1), Fabric Commands (v0), Fabric Containers (v0), Fabric Content Registries (v0), Fabric Crash Report Info (v1), Fabric Dimensions API (v1), Fabric Entity Events (v1), Fabric Events Interaction (v0), Fabric Events Lifecycle (v0), Fabric Game Rule API (v1), Fabric Item API (v1), Fabric Item Groups (v0), Fabric Key Binding API (v1), Fabric Key Bindings (v0), Fabric Language Kotlin, Fabric Lifecycle Events (v1), Fabric Loot Tables (v1), Fabric Mining Level API (v1), Fabric Mining Levels (v0), Fabric Models (v0), Fabric Networking API (v1), Fabric Networking (v0), Fabric Object Builder API (v1), Fabric Object Builders (v0), Fabric Particles (v1), Fabric Registry Sync (v0), Fabric Renderer API (v1), Fabric Renderer - Indigo, Fabric Renderer Registries (v1), Fabric Rendering Data Attachment (v1), Fabric Rendering Fluids (v1), Fabric Rendering (v0), Fabric Rendering (v1), Fabric Resource Loader (v0), Fabric Screen API (v1), Fabric Screen Handler API (v1), Fabric Structure API (v1), Fabric Tag Extensions (v0), Fabric Textures (v0), Fabric Tool Attribute API (v1), Fabric Transfer API (v1), Fabric Loader, Jankson, LibGui, RpmtwUpdateMod), RPMTW-1.18.zip -[21:15:18] [Render thread/INFO]: OpenAL initialized on device TU116 High Definition Audio Controller Digital Stereo (HDMI 2) -[21:15:18] [Render thread/INFO]: Sound engine started -[21:15:18] [Render thread/INFO]: Created: 1024x1024x4 minecraft:textures/atlas/blocks.png-atlas -[21:15:18] [Render thread/INFO]: Created: 256x128x4 minecraft:textures/atlas/signs.png-atlas -[21:15:18] [Render thread/INFO]: Created: 1024x512x4 minecraft:textures/atlas/banner_patterns.png-atlas -[21:15:18] [Render thread/INFO]: Created: 1024x512x4 minecraft:textures/atlas/shield_patterns.png-atlas -[21:15:18] [Render thread/INFO]: Created: 256x256x4 minecraft:textures/atlas/chest.png-atlas -[21:15:18] [Render thread/INFO]: Created: 512x256x4 minecraft:textures/atlas/beds.png-atlas -[21:15:18] [Render thread/INFO]: Created: 512x256x4 minecraft:textures/atlas/shulker_boxes.png-atlas -[21:15:20] [Render thread/INFO]: Created: 256x256x0 minecraft:textures/atlas/particles.png-atlas -[21:15:20] [Render thread/INFO]: Created: 256x256x0 minecraft:textures/atlas/paintings.png-atlas -[21:15:20] [Render thread/INFO]: Created: 256x128x0 minecraft:textures/atlas/mob_effects.png-atlas -[21:15:25] [Render thread/INFO]: Stopping! -[21:15:25] [Render thread/INFO]: 已中斷宇宙通訊的連線 \ No newline at end of file diff --git a/test/data/Fabric-Installer-Version.json b/test/data/Fabric-Installer-Version.json deleted file mode 100644 index 2235f29b5..000000000 --- a/test/data/Fabric-Installer-Version.json +++ /dev/null @@ -1,344 +0,0 @@ -[ - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.10.2/fabric-installer-0.10.2.jar", - "maven": "net.fabricmc:fabric-installer:0.10.2", - "version": "0.10.2", - "stable": true - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.10.1/fabric-installer-0.10.1.jar", - "maven": "net.fabricmc:fabric-installer:0.10.1", - "version": "0.10.1", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.10.0/fabric-installer-0.10.0.jar", - "maven": "net.fabricmc:fabric-installer:0.10.0", - "version": "0.10.0", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.9.1/fabric-installer-0.9.1.jar", - "maven": "net.fabricmc:fabric-installer:0.9.1", - "version": "0.9.1", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.9.0/fabric-installer-0.9.0.jar", - "maven": "net.fabricmc:fabric-installer:0.9.0", - "version": "0.9.0", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.8.3/fabric-installer-0.8.3.jar", - "maven": "net.fabricmc:fabric-installer:0.8.3", - "version": "0.8.3", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.8.2/fabric-installer-0.8.2.jar", - "maven": "net.fabricmc:fabric-installer:0.8.2", - "version": "0.8.2", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.8.1/fabric-installer-0.8.1.jar", - "maven": "net.fabricmc:fabric-installer:0.8.1", - "version": "0.8.1", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.8.0/fabric-installer-0.8.0.jar", - "maven": "net.fabricmc:fabric-installer:0.8.0", - "version": "0.8.0", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.7.4/fabric-installer-0.7.4.jar", - "maven": "net.fabricmc:fabric-installer:0.7.4", - "version": "0.7.4", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.7.3/fabric-installer-0.7.3.jar", - "maven": "net.fabricmc:fabric-installer:0.7.3", - "version": "0.7.3", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.7.2/fabric-installer-0.7.2.jar", - "maven": "net.fabricmc:fabric-installer:0.7.2", - "version": "0.7.2", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.7.1/fabric-installer-0.7.1.jar", - "maven": "net.fabricmc:fabric-installer:0.7.1", - "version": "0.7.1", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.51/fabric-installer-0.6.1.51.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.51", - "version": "0.6.1.51", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.50/fabric-installer-0.6.1.50.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.50", - "version": "0.6.1.50", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.49/fabric-installer-0.6.1.49.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.49", - "version": "0.6.1.49", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.48/fabric-installer-0.6.1.48.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.48", - "version": "0.6.1.48", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.47/fabric-installer-0.6.1.47.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.47", - "version": "0.6.1.47", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.46/fabric-installer-0.6.1.46.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.46", - "version": "0.6.1.46", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.45/fabric-installer-0.6.1.45.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.45", - "version": "0.6.1.45", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.1.44/fabric-installer-0.6.1.44.jar", - "maven": "net.fabricmc:fabric-installer:0.6.1.44", - "version": "0.6.1.44", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.0.43/fabric-installer-0.6.0.43.jar", - "maven": "net.fabricmc:fabric-installer:0.6.0.43", - "version": "0.6.0.43", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.0.42/fabric-installer-0.6.0.42.jar", - "maven": "net.fabricmc:fabric-installer:0.6.0.42", - "version": "0.6.0.42", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.6.0.41/fabric-installer-0.6.0.41.jar", - "maven": "net.fabricmc:fabric-installer:0.6.0.41", - "version": "0.6.0.41", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.2.40/fabric-installer-0.5.2.40.jar", - "maven": "net.fabricmc:fabric-installer:0.5.2.40", - "version": "0.5.2.40", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.2.39/fabric-installer-0.5.2.39.jar", - "maven": "net.fabricmc:fabric-installer:0.5.2.39", - "version": "0.5.2.39", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.2.38/fabric-installer-0.5.2.38.jar", - "maven": "net.fabricmc:fabric-installer:0.5.2.38", - "version": "0.5.2.38", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.1.37/fabric-installer-0.5.1.37.jar", - "maven": "net.fabricmc:fabric-installer:0.5.1.37", - "version": "0.5.1.37", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.1.36/fabric-installer-0.5.1.36.jar", - "maven": "net.fabricmc:fabric-installer:0.5.1.36", - "version": "0.5.1.36", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.0.35/fabric-installer-0.5.0.35.jar", - "maven": "net.fabricmc:fabric-installer:0.5.0.35", - "version": "0.5.0.35", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.0.34/fabric-installer-0.5.0.34.jar", - "maven": "net.fabricmc:fabric-installer:0.5.0.34", - "version": "0.5.0.34", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.0.33/fabric-installer-0.5.0.33.jar", - "maven": "net.fabricmc:fabric-installer:0.5.0.33", - "version": "0.5.0.33", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.0.32/fabric-installer-0.5.0.32.jar", - "maven": "net.fabricmc:fabric-installer:0.5.0.32", - "version": "0.5.0.32", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.0.31/fabric-installer-0.5.0.31.jar", - "maven": "net.fabricmc:fabric-installer:0.5.0.31", - "version": "0.5.0.31", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.0.30/fabric-installer-0.5.0.30.jar", - "maven": "net.fabricmc:fabric-installer:0.5.0.30", - "version": "0.5.0.30", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.5.0.29/fabric-installer-0.5.0.29.jar", - "maven": "net.fabricmc:fabric-installer:0.5.0.29", - "version": "0.5.0.29", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.2.28/fabric-installer-0.4.2.28.jar", - "maven": "net.fabricmc:fabric-installer:0.4.2.28", - "version": "0.4.2.28", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.2.27/fabric-installer-0.4.2.27.jar", - "maven": "net.fabricmc:fabric-installer:0.4.2.27", - "version": "0.4.2.27", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.2.26/fabric-installer-0.4.2.26.jar", - "maven": "net.fabricmc:fabric-installer:0.4.2.26", - "version": "0.4.2.26", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.2.25/fabric-installer-0.4.2.25.jar", - "maven": "net.fabricmc:fabric-installer:0.4.2.25", - "version": "0.4.2.25", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.2.24/fabric-installer-0.4.2.24.jar", - "maven": "net.fabricmc:fabric-installer:0.4.2.24", - "version": "0.4.2.24", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.2.23/fabric-installer-0.4.2.23.jar", - "maven": "net.fabricmc:fabric-installer:0.4.2.23", - "version": "0.4.2.23", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.1.22/fabric-installer-0.4.1.22.jar", - "maven": "net.fabricmc:fabric-installer:0.4.1.22", - "version": "0.4.1.22", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.0.21/fabric-installer-0.4.0.21.jar", - "maven": "net.fabricmc:fabric-installer:0.4.0.21", - "version": "0.4.0.21", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.0.20/fabric-installer-0.4.0.20.jar", - "maven": "net.fabricmc:fabric-installer:0.4.0.20", - "version": "0.4.0.20", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.0.19/fabric-installer-0.4.0.19.jar", - "maven": "net.fabricmc:fabric-installer:0.4.0.19", - "version": "0.4.0.19", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.4.0.18/fabric-installer-0.4.0.18.jar", - "maven": "net.fabricmc:fabric-installer:0.4.0.18", - "version": "0.4.0.18", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.3.0.17/fabric-installer-0.3.0.17.jar", - "maven": "net.fabricmc:fabric-installer:0.3.0.17", - "version": "0.3.0.17", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.3.0.16/fabric-installer-0.3.0.16.jar", - "maven": "net.fabricmc:fabric-installer:0.3.0.16", - "version": "0.3.0.16", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.3.0.15/fabric-installer-0.3.0.15.jar", - "maven": "net.fabricmc:fabric-installer:0.3.0.15", - "version": "0.3.0.15", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.2.4.14/fabric-installer-0.2.4.14.jar", - "maven": "net.fabricmc:fabric-installer:0.2.4.14", - "version": "0.2.4.14", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.2.3.12/fabric-installer-0.2.3.12.jar", - "maven": "net.fabricmc:fabric-installer:0.2.3.12", - "version": "0.2.3.12", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.2.2.11/fabric-installer-0.2.2.11.jar", - "maven": "net.fabricmc:fabric-installer:0.2.2.11", - "version": "0.2.2.11", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.2.2.10/fabric-installer-0.2.2.10.jar", - "maven": "net.fabricmc:fabric-installer:0.2.2.10", - "version": "0.2.2.10", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.2.1.9/fabric-installer-0.2.1.9.jar", - "maven": "net.fabricmc:fabric-installer:0.2.1.9", - "version": "0.2.1.9", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.2.1.8/fabric-installer-0.2.1.8.jar", - "maven": "net.fabricmc:fabric-installer:0.2.1.8", - "version": "0.2.1.8", - "stable": false - }, - { - "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.2.0.7/fabric-installer-0.2.0.7.jar", - "maven": "net.fabricmc:fabric-installer:0.2.0.7", - "version": "0.2.0.7", - "stable": false - } - ] \ No newline at end of file diff --git a/test/data/Forge-1.12.2-args.json b/test/data/Forge-1.12.2-args.json deleted file mode 100644 index 9f02b6c1b..000000000 --- a/test/data/Forge-1.12.2-args.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "game": [ - "--username", - "${auth_player_name}", - "--version", - "${version_name}", - "--gameDir", - "${game_directory}", - "--assetsDir", - "${assets_root}", - "--assetIndex", - "${assets_index_name}", - "--uuid", - "${auth_uuid}", - "--accessToken", - "${auth_access_token}", - "--userType", - "${user_type}", - "--tweakClass", - "net.minecraftforge.fml.common.launcher.FMLTweaker", - "--versionType", - "Forge" - ], - "mainClass": "net.minecraft.launchwrapper.Launch" -} \ No newline at end of file diff --git a/test/data/Minecraft-1.18-meta.json b/test/data/Minecraft-1.18-meta.json deleted file mode 100644 index 7849b3086..000000000 --- a/test/data/Minecraft-1.18-meta.json +++ /dev/null @@ -1,1580 +0,0 @@ -{ - "arguments": { - "game": [ - "--username", - "${auth_player_name}", - "--version", - "${version_name}", - "--gameDir", - "${game_directory}", - "--assetsDir", - "${assets_root}", - "--assetIndex", - "${assets_index_name}", - "--uuid", - "${auth_uuid}", - "--accessToken", - "${auth_access_token}", - "--clientId", - "${clientid}", - "--xuid", - "${auth_xuid}", - "--userType", - "${user_type}", - "--versionType", - "${version_type}", - { - "rules": [ - { - "action": "allow", - "features": { - "is_demo_user": true - } - } - ], - "value": "--demo" - }, - { - "rules": [ - { - "action": "allow", - "features": { - "has_custom_resolution": true - } - } - ], - "value": [ - "--width", - "${resolution_width}", - "--height", - "${resolution_height}" - ] - } - ], - "jvm": [ - { - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ], - "value": [ - "-XstartOnFirstThread" - ] - }, - { - "rules": [ - { - "action": "allow", - "os": { - "name": "windows" - } - } - ], - "value": "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump" - }, - { - "rules": [ - { - "action": "allow", - "os": { - "name": "windows", - "version": "^10\\." - } - } - ], - "value": [ - "-Dos.name=Windows 10", - "-Dos.version=10.0" - ] - }, - { - "rules": [ - { - "action": "allow", - "os": { - "arch": "x86" - } - } - ], - "value": "-Xss1M" - }, - "-Djava.library.path=${natives_directory}", - "-Dminecraft.launcher.brand=${launcher_name}", - "-Dminecraft.launcher.version=${launcher_version}", - "-cp", - "${classpath}" - ] - }, - "assetIndex": { - "id": "1.18", - "sha1": "816e3e9c187679b4622ebd1272e7c7c302d4c743", - "size": 348135, - "totalSize": 465764534, - "url": "https://launchermeta.mojang.com/v1/packages/816e3e9c187679b4622ebd1272e7c7c302d4c743/1.18.json" - }, - "assets": "1.18", - "complianceLevel": 1, - "downloads": { - "client": { - "sha1": "86dc60a6e7e9f1bbb877ce2c950de3d1c3cafb89", - "size": 20032140, - "url": "https://launcher.mojang.com/v1/objects/86dc60a6e7e9f1bbb877ce2c950de3d1c3cafb89/client.jar" - }, - "client_mappings": { - "sha1": "f50ffba6205ca0cff1fbfeb3709b4aae1b66d409", - "size": 6606082, - "url": "https://launcher.mojang.com/v1/objects/f50ffba6205ca0cff1fbfeb3709b4aae1b66d409/client.txt" - }, - "server": { - "sha1": "97b1c53df11cb8b973f4b522c8f4963b7e31495e", - "size": 46321640, - "url": "https://launcher.mojang.com/v1/objects/97b1c53df11cb8b973f4b522c8f4963b7e31495e/server.jar" - }, - "server_mappings": { - "sha1": "664893998f7e6d307c7e3f307e59d7f1220c6c87", - "size": 5112734, - "url": "https://launcher.mojang.com/v1/objects/664893998f7e6d307c7e3f307e59d7f1220c6c87/server.txt" - } - }, - "id": "1.18-pre6", - "javaVersion": { - "component": "java-runtime-beta", - "majorVersion": 17 - }, - "libraries": [ - { - "downloads": { - "artifact": { - "path": "com/mojang/blocklist/1.0.6/blocklist-1.0.6.jar", - "sha1": "7039c2c6209064ee4132cea7a0737b63b92c725e", - "size": 964, - "url": "https://libraries.minecraft.net/com/mojang/blocklist/1.0.6/blocklist-1.0.6.jar" - } - }, - "name": "com.mojang:blocklist:1.0.6" - }, - { - "downloads": { - "artifact": { - "path": "com/mojang/patchy/2.1.6/patchy-2.1.6.jar", - "sha1": "ce112c16c6275fdfff2d30208533ec906a191f71", - "size": 10090, - "url": "https://libraries.minecraft.net/com/mojang/patchy/2.1.6/patchy-2.1.6.jar" - } - }, - "name": "com.mojang:patchy:2.1.6" - }, - { - "downloads": { - "artifact": { - "path": "com/github/oshi/oshi-core/5.8.2/oshi-core-5.8.2.jar", - "sha1": "3a4c610d5991654009653e55b3204065441e2f0d", - "size": 879038, - "url": "https://libraries.minecraft.net/com/github/oshi/oshi-core/5.8.2/oshi-core-5.8.2.jar" - } - }, - "name": "com.github.oshi:oshi-core:5.8.2" - }, - { - "downloads": { - "artifact": { - "path": "net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar", - "sha1": "8f503e6d9b500ceff299052d6be75b38c7257758", - "size": 1729586, - "url": "https://libraries.minecraft.net/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar" - } - }, - "name": "net.java.dev.jna:jna:5.9.0" - }, - { - "downloads": { - "artifact": { - "path": "net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar", - "sha1": "c535a5bda553d7d7690356c825010da74b2671b5", - "size": 1343236, - "url": "https://libraries.minecraft.net/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar" - } - }, - "name": "net.java.dev.jna:jna-platform:5.9.0" - }, - { - "downloads": { - "artifact": { - "path": "org/slf4j/slf4j-api/1.8.0-beta4/slf4j-api-1.8.0-beta4.jar", - "sha1": "83b0359d847ee053d745be7ec0d8e9e8a44304b4", - "size": 44213, - "url": "https://libraries.minecraft.net/org/slf4j/slf4j-api/1.8.0-beta4/slf4j-api-1.8.0-beta4.jar" - } - }, - "name": "org.slf4j:slf4j-api:1.8.0-beta4" - }, - { - "downloads": { - "artifact": { - "path": "org/apache/logging/log4j/log4j-slf4j18-impl/2.14.1/log4j-slf4j18-impl-2.14.1.jar", - "sha1": "312b4d91b21160b9fab43600fa787f31e8cab930", - "size": 20688, - "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-slf4j18-impl/2.14.1/log4j-slf4j18-impl-2.14.1.jar" - } - }, - "name": "org.apache.logging.log4j:log4j-slf4j18-impl:2.14.1" - }, - { - "downloads": { - "artifact": { - "path": "com/ibm/icu/icu4j/69.1/icu4j-69.1.jar", - "sha1": "ff666ac55986650893aacb9e2e0003538e9799c0", - "size": 13356812, - "url": "https://libraries.minecraft.net/com/ibm/icu/icu4j/69.1/icu4j-69.1.jar" - } - }, - "name": "com.ibm.icu:icu4j:69.1" - }, - { - "downloads": { - "artifact": { - "path": "com/mojang/javabridge/1.2.24/javabridge-1.2.24.jar", - "sha1": "0c876796229b2ef5120f186eab5acc870699d3b9", - "size": 6053, - "url": "https://libraries.minecraft.net/com/mojang/javabridge/1.2.24/javabridge-1.2.24.jar" - } - }, - "name": "com.mojang:javabridge:1.2.24" - }, - { - "downloads": { - "artifact": { - "path": "net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar", - "sha1": "4fdac2fbe92dfad86aa6e9301736f6b4342a3f5c", - "size": 78146, - "url": "https://libraries.minecraft.net/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar" - } - }, - "name": "net.sf.jopt-simple:jopt-simple:5.0.4" - }, - { - "downloads": { - "artifact": { - "path": "io/netty/netty-all/4.1.68.Final/netty-all-4.1.68.Final.jar", - "sha1": "b8266a3c93c1c051109f71d3449e5dcd5d60b333", - "size": 4515045, - "url": "https://libraries.minecraft.net/io/netty/netty-all/4.1.68.Final/netty-all-4.1.68.Final.jar" - } - }, - "name": "io.netty:netty-all:4.1.68.Final" - }, - { - "downloads": { - "artifact": { - "path": "com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar", - "sha1": "1dcf1de382a0bf95a3d8b0849546c88bac1292c9", - "size": 4617, - "url": "https://libraries.minecraft.net/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" - } - }, - "name": "com.google.guava:failureaccess:1.0.1" - }, - { - "downloads": { - "artifact": { - "path": "com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar", - "sha1": "119ea2b2bc205b138974d351777b20f02b92704b", - "size": 2974216, - "url": "https://libraries.minecraft.net/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" - } - }, - "name": "com.google.guava:guava:31.0.1-jre" - }, - { - "downloads": { - "artifact": { - "path": "org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar", - "sha1": "c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e", - "size": 587402, - "url": "https://libraries.minecraft.net/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar" - } - }, - "name": "org.apache.commons:commons-lang3:3.12.0" - }, - { - "downloads": { - "artifact": { - "path": "commons-io/commons-io/2.11.0/commons-io-2.11.0.jar", - "sha1": "a2503f302b11ebde7ebc3df41daebe0e4eea3689", - "size": 327135, - "url": "https://libraries.minecraft.net/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar" - } - }, - "name": "commons-io:commons-io:2.11.0" - }, - { - "downloads": { - "artifact": { - "path": "commons-codec/commons-codec/1.15/commons-codec-1.15.jar", - "sha1": "49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d", - "size": 353793, - "url": "https://libraries.minecraft.net/commons-codec/commons-codec/1.15/commons-codec-1.15.jar" - } - }, - "name": "commons-codec:commons-codec:1.15" - }, - { - "downloads": { - "artifact": { - "path": "com/mojang/brigadier/1.0.18/brigadier-1.0.18.jar", - "sha1": "c1ef1234282716483c92183f49bef47b1a89bfa9", - "size": 77116, - "url": "https://libraries.minecraft.net/com/mojang/brigadier/1.0.18/brigadier-1.0.18.jar" - } - }, - "name": "com.mojang:brigadier:1.0.18" - }, - { - "downloads": { - "artifact": { - "path": "com/mojang/datafixerupper/4.0.26/datafixerupper-4.0.26.jar", - "sha1": "ebd6690f33871ccee9b6132c6480668ee2e35020", - "size": 673183, - "url": "https://libraries.minecraft.net/com/mojang/datafixerupper/4.0.26/datafixerupper-4.0.26.jar" - } - }, - "name": "com.mojang:datafixerupper:4.0.26" - }, - { - "downloads": { - "artifact": { - "path": "com/google/code/gson/gson/2.8.8/gson-2.8.8.jar", - "sha1": "431fc3cbc0ff81abdbfde070062741089c3ba874", - "size": 242047, - "url": "https://libraries.minecraft.net/com/google/code/gson/gson/2.8.8/gson-2.8.8.jar" - } - }, - "name": "com.google.code.gson:gson:2.8.8" - }, - { - "downloads": { - "artifact": { - "path": "com/mojang/authlib/3.2.38/authlib-3.2.38.jar", - "sha1": "7bba19e34be22fded9ceb409ca1e8fb7525daf55", - "size": 102196, - "url": "https://libraries.minecraft.net/com/mojang/authlib/3.2.38/authlib-3.2.38.jar" - } - }, - "name": "com.mojang:authlib:3.2.38" - }, - { - "downloads": { - "artifact": { - "path": "org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar", - "sha1": "4ec95b60d4e86b5c95a0e919cb172a0af98011ef", - "size": 1018316, - "url": "https://libraries.minecraft.net/org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar" - } - }, - "name": "org.apache.commons:commons-compress:1.21" - }, - { - "downloads": { - "artifact": { - "path": "org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar", - "sha1": "e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada", - "size": 780321, - "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar" - } - }, - "name": "org.apache.httpcomponents:httpclient:4.5.13" - }, - { - "downloads": { - "artifact": { - "path": "commons-logging/commons-logging/1.2/commons-logging-1.2.jar", - "sha1": "4bfc12adfe4842bf07b657f0369c4cb522955686", - "size": 61829, - "url": "https://libraries.minecraft.net/commons-logging/commons-logging/1.2/commons-logging-1.2.jar" - } - }, - "name": "commons-logging:commons-logging:1.2" - }, - { - "downloads": { - "artifact": { - "path": "org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.jar", - "sha1": "9dd1a631c082d92ecd4bd8fd4cf55026c720a8c1", - "size": 328436, - "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.jar" - } - }, - "name": "org.apache.httpcomponents:httpcore:4.4.14" - }, - { - "downloads": { - "artifact": { - "path": "it/unimi/dsi/fastutil/8.5.6/fastutil-8.5.6.jar", - "sha1": "76f95700418a68fbc4ac050525261f05dc681ca1", - "size": 23565248, - "url": "https://libraries.minecraft.net/it/unimi/dsi/fastutil/8.5.6/fastutil-8.5.6.jar" - } - }, - "name": "it.unimi.dsi:fastutil:8.5.6" - }, - { - "downloads": { - "artifact": { - "path": "org/apache/logging/log4j/log4j-api/2.14.1/log4j-api-2.14.1.jar", - "sha1": "cd8858fbbde69f46bce8db1152c18a43328aae78", - "size": 300365, - "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-api/2.14.1/log4j-api-2.14.1.jar" - } - }, - "name": "org.apache.logging.log4j:log4j-api:2.14.1" - }, - { - "downloads": { - "artifact": { - "path": "org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar", - "sha1": "9141212b8507ab50a45525b545b39d224614528b", - "size": 1745700, - "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar" - } - }, - "name": "org.apache.logging.log4j:log4j-core:2.14.1" - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar", - "sha1": "2bb514e444994c6fece99a21f76e0c90438e377f", - "size": 317748, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" - } - }, - "name": "org.lwjgl:lwjgl:3.2.1", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar", - "sha1": "8ad6294407e15780b43e84929c40e4c5e997972e", - "size": 321900, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" - } - }, - "name": "org.lwjgl:lwjgl:3.2.2", - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar", - "sha1": "7a0c583fcbec32b15784f846df536e1837d83666", - "size": 38616, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" - } - }, - "name": "org.lwjgl:lwjgl-jemalloc:3.2.1", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar", - "sha1": "ee8e57a79300f78294576d87c4a587f8c99402e2", - "size": 34848, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" - } - }, - "name": "org.lwjgl:lwjgl-jemalloc:3.2.2", - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar", - "sha1": "dc7ff2dabb40a141ee9bf2e326d9b1b19f3278fb", - "size": 80103, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" - } - }, - "name": "org.lwjgl:lwjgl-openal:3.2.1", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar", - "sha1": "2b772a102b0a11ee5f2109a5b136f4dc7c630827", - "size": 80012, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" - } - }, - "name": "org.lwjgl:lwjgl-openal:3.2.2", - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar", - "sha1": "57008c2374c5bc434b18adfef3f3653ee450ee18", - "size": 931322, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" - } - }, - "name": "org.lwjgl:lwjgl-opengl:3.2.1", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar", - "sha1": "6ac5bb88b44c43ea195a570aab059f63da004cd8", - "size": 929780, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" - } - }, - "name": "org.lwjgl:lwjgl-opengl:3.2.2", - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar", - "sha1": "027abb7f64894b61cad163791acd8113f0b21296", - "size": 116708, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" - } - }, - "name": "org.lwjgl:lwjgl-glfw:3.2.1", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar", - "sha1": "d3ad4df38e400b8afba1de63f84338809399df5b", - "size": 108907, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" - } - }, - "name": "org.lwjgl:lwjgl-glfw:3.2.2", - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar", - "sha1": "31f5eb5fce3791d58ec898bc5c1867d76d781ba1", - "size": 105765, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" - } - }, - "name": "org.lwjgl:lwjgl-stb:3.2.1", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar", - "sha1": "3b8e6ebc5851dd3d17e37e5cadce2eff2a429f0f", - "size": 104469, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" - } - }, - "name": "org.lwjgl:lwjgl-stb:3.2.2", - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1.jar", - "sha1": "259f1dbddb921e27e01b32458d6f584eb8bba13a", - "size": 7088, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1.jar" - } - }, - "name": "org.lwjgl:lwjgl-tinyfd:3.2.1", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar", - "sha1": "fcbe606c8f8da6f8f9a05e2c540eb1ee8632b0e9", - "size": 7092, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" - } - }, - "name": "org.lwjgl:lwjgl-tinyfd:3.2.2", - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar", - "sha1": "2bb514e444994c6fece99a21f76e0c90438e377f", - "size": 317748, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-javadoc.jar", - "sha1": "1f6b7050737559b775d797c0ea56612b8e373fd6", - "size": 1287174, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar", - "sha1": "9bdd47cd63ce102cec837a396c8ded597cb75a66", - "size": 87484, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-macos.jar", - "sha1": "5a4c271d150906858d475603dcb9479453c60555", - "size": 39835, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-windows.jar", - "sha1": "e799d06b8969db0610e68776e0eff4b6191098bd", - "size": 255871, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-sources.jar", - "sha1": "106f90ac41449004a969309488aa6e3a2f7d6731", - "size": 255671, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl:3.2.1", - "natives": { - "osx": "natives-macos" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar", - "sha1": "8ad6294407e15780b43e84929c40e4c5e997972e", - "size": 321900, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" - }, - "classifiers": { - "natives-linux": { - "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-linux.jar", - "sha1": "ae7976827ca2a3741f6b9a843a89bacd637af350", - "size": 124776, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-macos.jar", - "sha1": "bbfb75693bdb714c0c69c2c9f9be73d259b43b62", - "size": 48462, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-windows.jar", - "sha1": "05359f3aa50d36352815fc662ea73e1c00d22170", - "size": 279593, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-windows.jar" - } - } - }, - "name": "org.lwjgl:lwjgl:3.2.2", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - }, - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar", - "sha1": "7a0c583fcbec32b15784f846df536e1837d83666", - "size": 38616, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-javadoc.jar", - "sha1": "04f6897be1e2d68bff5ec5e91a2b96e32f084c09", - "size": 461041, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-linux.jar", - "sha1": "5536616b558cea2fea6330ca682fd7c733db9c43", - "size": 156057, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-macos.jar", - "sha1": "439ab9d0264167a949cc7bcce673704322baaf50", - "size": 117001, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-windows.jar", - "sha1": "3c869b3d7638c800b7039cd859d064658643ad6e", - "size": 218136, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-sources.jar", - "sha1": "4450dca46228c02c51bb9bbda70e7cfc3154296d", - "size": 31279, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-jemalloc:3.2.1", - "natives": { - "osx": "natives-macos" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar", - "sha1": "ee8e57a79300f78294576d87c4a587f8c99402e2", - "size": 34848, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" - }, - "classifiers": { - "natives-linux": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-linux.jar", - "sha1": "268c08a150347e04e44ba56e359d62c9b78669df", - "size": 156173, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-macos.jar", - "sha1": "805f5a10465375ba034b27b72331912fd2846690", - "size": 117127, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-windows.jar", - "sha1": "338b25b99da3ba5f441f6492f2ce2a9c608860ed", - "size": 220623, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-windows.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-jemalloc:3.2.2", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - }, - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar", - "sha1": "dc7ff2dabb40a141ee9bf2e326d9b1b19f3278fb", - "size": 80103, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-javadoc.jar", - "sha1": "95752f443686da1b3443e397dc83e730e1907a1e", - "size": 617869, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-linux.jar", - "sha1": "bcd4be67863dd908f696f628c3ca9f6eb9ae5152", - "size": 590716, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-macos.jar", - "sha1": "9357ebfc82a0d6f64e17093dd963219367cd6fa2", - "size": 528004, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-windows.jar", - "sha1": "92fb931e65c637cea209ad5c3ffebd1b325ed41d", - "size": 1310083, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-sources.jar", - "sha1": "8fe3d6e6353685164b1eb3a22980aaa1115d4a32", - "size": 78379, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-openal:3.2.1", - "natives": { - "osx": "natives-macos" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar", - "sha1": "2b772a102b0a11ee5f2109a5b136f4dc7c630827", - "size": 80012, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" - }, - "classifiers": { - "natives-linux": { - "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-linux.jar", - "sha1": "0364f9f5c3947393083ab5f37a571f5603aadd0b", - "size": 590997, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-macos.jar", - "sha1": "a97b6345d5a9ddf889e262bd7ad8eed43b1bb063", - "size": 528006, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-windows.jar", - "sha1": "ec20a7d42a2438528fca87e60b1705f1e2339ddb", - "size": 1310102, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-windows.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-openal:3.2.2", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - }, - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar", - "sha1": "57008c2374c5bc434b18adfef3f3653ee450ee18", - "size": 931322, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-javadoc.jar", - "sha1": "e25fc8cbcbee68182a6b7f13ad71b1f0961005ad", - "size": 4307561, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-linux.jar", - "sha1": "c43bb08ed7dcf1a6d344803e464148b3b14dd274", - "size": 77401, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-macos.jar", - "sha1": "dca9ad9e59a87172144d531e08ef7f6988073db0", - "size": 38998, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-windows.jar", - "sha1": "80954961b31084d7b4f2f041d6b5a799a774c880", - "size": 170804, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-sources.jar", - "sha1": "47930ffbef53c0f45c7e35c01b1c6ad5b2205809", - "size": 1251582, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-opengl:3.2.1", - "natives": { - "osx": "natives-macos" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar", - "sha1": "6ac5bb88b44c43ea195a570aab059f63da004cd8", - "size": 929780, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" - }, - "classifiers": { - "natives-linux": { - "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-linux.jar", - "sha1": "338d33387919cb3f4cdba143c2b738a71ccfda60", - "size": 77392, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-macos.jar", - "sha1": "cf4f43e69ee70d8ebfbb6ba93dec9016339e4fdc", - "size": 38989, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-windows.jar", - "sha1": "d8dcdc91066cae2d2d8279cb4a9f9f05d9525826", - "size": 170798, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-windows.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-opengl:3.2.2", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - }, - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar", - "sha1": "027abb7f64894b61cad163791acd8113f0b21296", - "size": 116708, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-javadoc.jar", - "sha1": "81482a14b617e4fb0c7de69b3e06ef2e28ef894f", - "size": 690774, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-linux.jar", - "sha1": "5a2fb27f9e12a34ecabf6f6a7606c61849e347ee", - "size": 157431, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-macos.jar", - "sha1": "72fe6dab6110a5a1cd4833f11840eef7b2eadce5", - "size": 64724, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-windows.jar", - "sha1": "00def7c58ad2e1cb258d6d73be181ffab8ef8bd5", - "size": 265304, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-sources.jar", - "sha1": "4c56ae817da75996b19601c87d7e759b846c3902", - "size": 101885, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-glfw:3.2.1", - "natives": { - "osx": "natives-macos" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar", - "sha1": "d3ad4df38e400b8afba1de63f84338809399df5b", - "size": 108907, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" - }, - "classifiers": { - "natives-linux": { - "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-linux.jar", - "sha1": "0957733f26a6661d4883da0335f7ef46d3bbbd7d", - "size": 159198, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-macos.jar", - "sha1": "98f745038d17ac3192fcd01dc44126b03ec1570d", - "size": 67311, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-windows.jar", - "sha1": "dc6826d636bf796b33a49038c354210e661bfc17", - "size": 266648, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-windows.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-glfw:3.2.2", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - }, - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar", - "sha1": "31f5eb5fce3791d58ec898bc5c1867d76d781ba1", - "size": 105765, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-javadoc.jar", - "sha1": "524d79537f840d6cfe50e030d24413933f0d464b", - "size": 684972, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-linux.jar", - "sha1": "66e01b8036258619332cb452b970ca0a52db1a87", - "size": 197208, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-macos.jar", - "sha1": "1f5615c952451c30afafba4a6e3ba4e1cd9e7f5c", - "size": 192364, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-windows.jar", - "sha1": "d100bfd2b0d03223a043cfcb64a2dfd2bb7f4c61", - "size": 454473, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-sources.jar", - "sha1": "50ac43d4c6ea5846f354f9576134c0f9264345c2", - "size": 96479, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-stb:3.2.1", - "natives": { - "osx": "natives-macos" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar", - "sha1": "fcbe606c8f8da6f8f9a05e2c540eb1ee8632b0e9", - "size": 7092, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-javadoc.jar", - "sha1": "ba657a222ee267b75fa81ae5ab29ae29b50f725f", - "size": 368913, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-linux.jar", - "sha1": "39e35b161c130635d9c8918ce04e887a30c5b687", - "size": 38804, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-macos.jar", - "sha1": "46d0798228b8a28e857a2a0f02310fd6ba2a4eab", - "size": 42136, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-windows.jar", - "sha1": "e9115958773644e863332a6a06488d26f9e1fc9f", - "size": 208314, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-sources.jar", - "sha1": "2fe76dcf2ca02ae0e64ac7c69eb251c09df0e922", - "size": 5034, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-tinyfd:3.2.2", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - }, - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1.jar", - "sha1": "259f1dbddb921e27e01b32458d6f584eb8bba13a", - "size": 7088, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1.jar" - }, - "classifiers": { - "javadoc": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-javadoc.jar", - "sha1": "0a85d995178cdab6b94d9a172dd9e7d2a0d70cfb", - "size": 368913, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-javadoc.jar" - }, - "natives-linux": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-natives-linux.jar", - "sha1": "4ad49108397322596d7b85c2c687e5de6ee52157", - "size": 38192, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-natives-macos.jar", - "sha1": "759c2fd9cc5c6ce0b5b7af77ac8200483b7fb660", - "size": 41962, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-natives-windows.jar", - "sha1": "85750d2ca022852e15f58c0b94b3d1d4e7f0ba52", - "size": 207577, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-natives-windows.jar" - }, - "sources": { - "path": "org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-sources.jar", - "sha1": "c375699fd794c4c87d935e0f9a84e7d80d0de77e", - "size": 5034, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-tinyfd/3.2.1/lwjgl-tinyfd-3.2.1-sources.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-tinyfd:3.2.1", - "natives": { - "osx": "natives-macos" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar", - "sha1": "3b8e6ebc5851dd3d17e37e5cadce2eff2a429f0f", - "size": 104469, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" - }, - "classifiers": { - "natives-linux": { - "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-linux.jar", - "sha1": "172c52e586fecf43f759bc4f70a778c01f6fdcc1", - "size": 203476, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-linux.jar" - }, - "natives-macos": { - "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-macos.jar", - "sha1": "ee059b129b09fdecbd8595273926ae930bf5a5d7", - "size": 196796, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-macos.jar" - }, - "natives-windows": { - "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-windows.jar", - "sha1": "811f705cbb29e8ae8d60bdf8fdd38c0c123ad3ef", - "size": 465810, - "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-windows.jar" - } - } - }, - "name": "org.lwjgl:lwjgl-stb:3.2.2", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - }, - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar", - "sha1": "f378f889797edd7df8d32272c06ca80a1b6b0f58", - "size": 13164, - "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar" - } - }, - "name": "com.mojang:text2speech:1.11.3" - }, - { - "downloads": { - "artifact": { - "path": "com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar", - "sha1": "f378f889797edd7df8d32272c06ca80a1b6b0f58", - "size": 13164, - "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar" - }, - "classifiers": { - "natives-linux": { - "path": "com/mojang/text2speech/1.11.3/text2speech-1.11.3-natives-linux.jar", - "sha1": "ac641755a2a841d1fca9e660194f42523ee5cfe0", - "size": 7833, - "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.11.3/text2speech-1.11.3-natives-linux.jar" - }, - "natives-windows": { - "path": "com/mojang/text2speech/1.11.3/text2speech-1.11.3-natives-windows.jar", - "sha1": "c0b242c0091be5acbf303263c7eeeaedd70544c7", - "size": 81379, - "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.11.3/text2speech-1.11.3-natives-windows.jar" - }, - "sources": { - "path": "com/mojang/text2speech/1.11.3/text2speech-1.11.3-sources.jar", - "sha1": "772a37dd77417571e6f119a8d306f0c14c2ee410", - "size": 5332, - "url": "https://libraries.minecraft.net/com/mojang/text2speech/1.11.3/text2speech-1.11.3-sources.jar" - } - } - }, - "extract": { - "exclude": [ - "META-INF/" - ] - }, - "name": "com.mojang:text2speech:1.11.3", - "natives": { - "linux": "natives-linux", - "windows": "natives-windows" - } - }, - { - "downloads": { - "artifact": { - "path": "ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0.jar", - "sha1": "6ef160c3133a78de015830860197602ca1c855d3", - "size": 40502, - "url": "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0.jar" - }, - "classifiers": { - "javadoc": { - "path": "ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0-javadoc.jar", - "sha1": "fb0092f22cb4fe8e631452f577b7a238778abf2a", - "size": 174060, - "url": "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0-javadoc.jar" - }, - "natives-osx": { - "path": "ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0-natives-osx.jar", - "sha1": "08befab4894d55875f33c3d300f4f71e6e828f64", - "size": 5629, - "url": "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0-natives-osx.jar" - }, - "sources": { - "path": "ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0-sources.jar", - "sha1": "865837a198189aee737019561ece842827f24278", - "size": 43283, - "url": "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0-sources.jar" - } - } - }, - "extract": { - "exclude": [ - "META-INF/" - ] - }, - "name": "ca.weblite:java-objc-bridge:1.0.0", - "natives": { - "osx": "natives-osx" - }, - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - }, - { - "downloads": { - "artifact": { - "path": "ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0.jar", - "sha1": "6ef160c3133a78de015830860197602ca1c855d3", - "size": 40502, - "url": "https://libraries.minecraft.net/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0.jar" - } - }, - "name": "ca.weblite:java-objc-bridge:1.0.0", - "rules": [ - { - "action": "allow", - "os": { - "name": "osx" - } - } - ] - } - ], - "logging": { - "client": { - "argument": "-Dlog4j.configurationFile=${path}", - "file": { - "id": "client-1.12.xml", - "sha1": "ef4f57b922df243d0cef096efe808c72db042149", - "size": 877, - "url": "https://launcher.mojang.com/v1/objects/ef4f57b922df243d0cef096efe808c72db042149/client-1.12.xml" - }, - "type": "log4j2-xml" - } - }, - "mainClass": "net.minecraft.client.main.Main", - "minimumLauncherVersion": 21, - "releaseTime": "2021-11-22T17:09:05+00:00", - "time": "2021-11-22T17:09:05+00:00", - "type": "snapshot" -} \ No newline at end of file diff --git a/test/data/Minecraft-Version-Manifest-V2.json b/test/data/Minecraft-Version-Manifest-V2.json deleted file mode 100644 index 52a9d2e0e..000000000 --- a/test/data/Minecraft-Version-Manifest-V2.json +++ /dev/null @@ -1,5516 +0,0 @@ -{ - "latest": { - "release": "1.18.1", - "snapshot": "1.18.1" - }, - "versions": [ - { - "id": "1.18.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/b0bdc637e4c4cbf0501500cbaad5a757b04848ed/1.18.1.json", - "time": "2021-12-10T08:26:34+00:00", - "releaseTime": "2021-12-10T08:23:00+00:00", - "sha1": "b0bdc637e4c4cbf0501500cbaad5a757b04848ed", - "complianceLevel": 1 - }, - { - "id": "1.18.1-rc3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e93a50674f603440dfc24a96c5e2c0f379231e57/1.18.1-rc3.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-12-10T03:36:38+00:00", - "sha1": "e93a50674f603440dfc24a96c5e2c0f379231e57", - "complianceLevel": 1 - }, - { - "id": "1.18.1-rc2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a22e46bd382874572e3aef9ea4e0fec81e1a8629/1.18.1-rc2.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-12-08T12:29:36+00:00", - "sha1": "a22e46bd382874572e3aef9ea4e0fec81e1a8629", - "complianceLevel": 1 - }, - { - "id": "1.18.1-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5861d51c313c523f91f5b88915ece4203cf9bbc5/1.18.1-rc1.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-12-07T15:52:47+00:00", - "sha1": "5861d51c313c523f91f5b88915ece4203cf9bbc5", - "complianceLevel": 1 - }, - { - "id": "1.18.1-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/757c95dd306af389b9e31435b3e17aa360662627/1.18.1-pre1.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-12-03T13:45:38+00:00", - "sha1": "757c95dd306af389b9e31435b3e17aa360662627", - "complianceLevel": 1 - }, - { - "id": "1.18", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/4220b6c4c91d80e17d441bcc166e5ca3f9a77304/1.18.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-30T09:16:29+00:00", - "sha1": "4220b6c4c91d80e17d441bcc166e5ca3f9a77304", - "complianceLevel": 1 - }, - { - "id": "1.18-rc4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c256fe7acf00470ab54f3b4ca396fb1ade52d6ad/1.18-rc4.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-29T13:43:42+00:00", - "sha1": "c256fe7acf00470ab54f3b4ca396fb1ade52d6ad", - "complianceLevel": 1 - }, - { - "id": "1.18-rc3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/661cbcdc94ddf44ca2e072b1f5f13441f70b53f0/1.18-rc3.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-26T15:51:56+00:00", - "sha1": "661cbcdc94ddf44ca2e072b1f5f13441f70b53f0", - "complianceLevel": 1 - }, - { - "id": "1.18-rc2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/21e151f7a8a1925e9a18e55736804d8ce095b1df/1.18-rc2.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-26T10:02:04+00:00", - "sha1": "21e151f7a8a1925e9a18e55736804d8ce095b1df", - "complianceLevel": 1 - }, - { - "id": "1.18-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1252eb140bd1b41ee071db8e885ccbfc1237b487/1.18-rc1.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-25T14:28:49+00:00", - "sha1": "1252eb140bd1b41ee071db8e885ccbfc1237b487", - "complianceLevel": 1 - }, - { - "id": "1.18-pre8", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e40cfe9aa0a401fec87f9092a707c0b97c7ae56d/1.18-pre8.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-24T14:57:32+00:00", - "sha1": "e40cfe9aa0a401fec87f9092a707c0b97c7ae56d", - "complianceLevel": 1 - }, - { - "id": "1.18-pre7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1d4ba1dbebf3655056ce2a7859fdc4edb928573b/1.18-pre7.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-23T16:37:41+00:00", - "sha1": "1d4ba1dbebf3655056ce2a7859fdc4edb928573b", - "complianceLevel": 1 - }, - { - "id": "1.18-pre6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2bcc2ddc9ecc8fb524c97ce7fd2f2e04b11694b4/1.18-pre6.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-22T17:09:05+00:00", - "sha1": "2bcc2ddc9ecc8fb524c97ce7fd2f2e04b11694b4", - "complianceLevel": 1 - }, - { - "id": "1.18-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/26a29a99efb58eba40f16572e0e5b9e7e8c0b983/1.18-pre5.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-19T15:47:09+00:00", - "sha1": "26a29a99efb58eba40f16572e0e5b9e7e8c0b983", - "complianceLevel": 1 - }, - { - "id": "1.18-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a87d4691e75e273fab963e529e9eb4ff78e949b2/1.18-pre4.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-17T18:07:56+00:00", - "sha1": "a87d4691e75e273fab963e529e9eb4ff78e949b2", - "complianceLevel": 1 - }, - { - "id": "1.18-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8abac5ea86779c6aff6321ff23dd18aeb011945f/1.18-pre3.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-17T16:04:25+00:00", - "sha1": "8abac5ea86779c6aff6321ff23dd18aeb011945f", - "complianceLevel": 1 - }, - { - "id": "1.18-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d9048191dd8c25cd3d6d10152651c90f68788a29/1.18-pre2.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-16T17:04:48+00:00", - "sha1": "d9048191dd8c25cd3d6d10152651c90f68788a29", - "complianceLevel": 1 - }, - { - "id": "1.18-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0749ee515dea389c4348b6c388ecd206afe9382d/1.18-pre1.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-11T16:14:06+00:00", - "sha1": "0749ee515dea389c4348b6c388ecd206afe9382d", - "complianceLevel": 1 - }, - { - "id": "21w44a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2269fdf7d942d50dcd12f5b244288c91ded5bff3/21w44a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-11-03T16:14:34+00:00", - "sha1": "2269fdf7d942d50dcd12f5b244288c91ded5bff3", - "complianceLevel": 1 - }, - { - "id": "21w43a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4c316f9183fb1f4e5a981e0f0a318fe27b6a364c/21w43a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-10-27T14:38:55+00:00", - "sha1": "4c316f9183fb1f4e5a981e0f0a318fe27b6a364c", - "complianceLevel": 1 - }, - { - "id": "21w42a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/45ec56f42e737431e32a6eb0d59c80133f608c34/21w42a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-10-20T12:41:25+00:00", - "sha1": "45ec56f42e737431e32a6eb0d59c80133f608c34", - "complianceLevel": 1 - }, - { - "id": "21w41a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2eded0c8b2794aa7966150dfa5eccf2adb0ee88a/21w41a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-10-13T15:23:23+00:00", - "sha1": "2eded0c8b2794aa7966150dfa5eccf2adb0ee88a", - "complianceLevel": 1 - }, - { - "id": "21w40a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/56bac73a83fdd38c4f42c49d9eb6f6f8a5931e73/21w40a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-10-07T11:17:50+00:00", - "sha1": "56bac73a83fdd38c4f42c49d9eb6f6f8a5931e73", - "complianceLevel": 1 - }, - { - "id": "21w39a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e85ee59e2f9d1bee72a2462a462f51ef0a8e2f0a/21w39a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-09-29T16:27:05+00:00", - "sha1": "e85ee59e2f9d1bee72a2462a462f51ef0a8e2f0a", - "complianceLevel": 1 - }, - { - "id": "21w38a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9d716ebd9c2ad42d4a5992e06f13b1de32be66d1/21w38a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-09-23T14:36:06+00:00", - "sha1": "9d716ebd9c2ad42d4a5992e06f13b1de32be66d1", - "complianceLevel": 1 - }, - { - "id": "21w37a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b1cd844ac9306cb3447fb7930511fb2e18d92cfe/21w37a.json", - "time": "2021-12-10T08:14:51+00:00", - "releaseTime": "2021-09-15T16:04:30+00:00", - "sha1": "b1cd844ac9306cb3447fb7930511fb2e18d92cfe", - "complianceLevel": 1 - }, - { - "id": "1.17.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/0b1d2828e58cf8eb1aac14ee588f5160bd06e77f/1.17.1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-07-06T12:01:34+00:00", - "sha1": "0b1d2828e58cf8eb1aac14ee588f5160bd06e77f", - "complianceLevel": 1 - }, - { - "id": "1.17.1-rc2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/494152b7d3a75ff74eed4ea709461f58cebd5e0e/1.17.1-rc2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-07-05T12:58:01+00:00", - "sha1": "494152b7d3a75ff74eed4ea709461f58cebd5e0e", - "complianceLevel": 1 - }, - { - "id": "1.17.1-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3c45df2f3bffafb69c47b59ae4b7eb27e18b3403/1.17.1-rc1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-07-01T15:23:37+00:00", - "sha1": "3c45df2f3bffafb69c47b59ae4b7eb27e18b3403", - "complianceLevel": 1 - }, - { - "id": "1.17.1-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/44c1e8a2c2cb6e31e74389728055080a178d8211/1.17.1-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-30T15:43:16+00:00", - "sha1": "44c1e8a2c2cb6e31e74389728055080a178d8211", - "complianceLevel": 1 - }, - { - "id": "1.17.1-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f4d348da321440275351632c3a214c2a346b6535/1.17.1-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-29T15:14:12+00:00", - "sha1": "f4d348da321440275351632c3a214c2a346b6535", - "complianceLevel": 1 - }, - { - "id": "1.17.1-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/eb9110666b1acf688ec045cfe08fab746481003c/1.17.1-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-18T12:24:40+00:00", - "sha1": "eb9110666b1acf688ec045cfe08fab746481003c", - "complianceLevel": 1 - }, - { - "id": "1.17", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/bb6684a5a4f4ecfa8540a4c3aec0d1eab8c574c1/1.17.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-08T11:00:40+00:00", - "sha1": "bb6684a5a4f4ecfa8540a4c3aec0d1eab8c574c1", - "complianceLevel": 1 - }, - { - "id": "1.17-rc2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a8738e73ae56391c911cf385169fc3fc2523426a/1.17-rc2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-07T11:46:28+00:00", - "sha1": "a8738e73ae56391c911cf385169fc3fc2523426a", - "complianceLevel": 1 - }, - { - "id": "1.17-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0ddb07701b1a582addd77d0ffebcf55cc80d2718/1.17-rc1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-04T13:24:48+00:00", - "sha1": "0ddb07701b1a582addd77d0ffebcf55cc80d2718", - "complianceLevel": 1 - }, - { - "id": "1.17-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3c1cef1ab781605736c3616e19e05831b2ace674/1.17-pre5.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-03T17:01:28+00:00", - "sha1": "3c1cef1ab781605736c3616e19e05831b2ace674", - "complianceLevel": 1 - }, - { - "id": "1.17-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a9e9398811380eb9bfa6f8650e3ebcdc06cccf62/1.17-pre4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-02T16:15:43+00:00", - "sha1": "a9e9398811380eb9bfa6f8650e3ebcdc06cccf62", - "complianceLevel": 1 - }, - { - "id": "1.17-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/848ba35d367aab89fe8b6e745ad14fa63298b07a/1.17-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-06-01T15:43:46+00:00", - "sha1": "848ba35d367aab89fe8b6e745ad14fa63298b07a", - "complianceLevel": 1 - }, - { - "id": "1.17-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/014acc696235553d02289468a0979934d38a19d0/1.17-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-05-31T15:54:05+00:00", - "sha1": "014acc696235553d02289468a0979934d38a19d0", - "complianceLevel": 1 - }, - { - "id": "1.17-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/42cfdff6bc0309049e346ce77986264365129f94/1.17-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-05-27T09:39:21+00:00", - "sha1": "42cfdff6bc0309049e346ce77986264365129f94", - "complianceLevel": 1 - }, - { - "id": "21w20a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8e231ced9cb57ef0d63211f40a7c998f4fe84390/21w20a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-05-19T15:22:02+00:00", - "sha1": "8e231ced9cb57ef0d63211f40a7c998f4fe84390", - "complianceLevel": 1 - }, - { - "id": "21w19a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/179c77aead1636521432beca2a778cae0cb4a3e0/21w19a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-05-12T11:19:15+00:00", - "sha1": "179c77aead1636521432beca2a778cae0cb4a3e0", - "complianceLevel": 1 - }, - { - "id": "21w18a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0bc7f8206a23faa7447c9e8ea715a7bc1c69ba7b/21w18a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-05-05T15:24:35+00:00", - "sha1": "0bc7f8206a23faa7447c9e8ea715a7bc1c69ba7b", - "complianceLevel": 1 - }, - { - "id": "21w17a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/64b767dbefb87f744e72bd01fabc78dc983cc465/21w17a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-04-28T13:54:05+00:00", - "sha1": "64b767dbefb87f744e72bd01fabc78dc983cc465", - "complianceLevel": 1 - }, - { - "id": "21w16a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/70f20568e2743265faec03e8f939152865e21322/21w16a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-04-21T16:41:14+00:00", - "sha1": "70f20568e2743265faec03e8f939152865e21322", - "complianceLevel": 1 - }, - { - "id": "21w15a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0f2092c7b1b68f4d21714ba6d2fc2ffc1d7cdf00/21w15a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-04-14T13:41:34+00:00", - "sha1": "0f2092c7b1b68f4d21714ba6d2fc2ffc1d7cdf00", - "complianceLevel": 1 - }, - { - "id": "21w14a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/365d27af2c01a0a4b4fc9752075b58b9531f438a/21w14a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-04-07T14:04:09+00:00", - "sha1": "365d27af2c01a0a4b4fc9752075b58b9531f438a", - "complianceLevel": 1 - }, - { - "id": "21w13a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9dccad0291c43dc95c7358ac42ca95a8b43e4ef7/21w13a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-03-31T16:17:46+00:00", - "sha1": "9dccad0291c43dc95c7358ac42ca95a8b43e4ef7", - "complianceLevel": 1 - }, - { - "id": "21w11a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/31dd553a885d1185c8b86f4fc7a4ce4a59b067b0/21w11a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-03-17T15:05:50+00:00", - "sha1": "31dd553a885d1185c8b86f4fc7a4ce4a59b067b0", - "complianceLevel": 1 - }, - { - "id": "21w10a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9d48506d57718e2a399cda727912581939f574fc/21w10a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-03-10T15:24:38+00:00", - "sha1": "9d48506d57718e2a399cda727912581939f574fc", - "complianceLevel": 1 - }, - { - "id": "21w08b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/86af044ab26d3d0ea12c89a4fd7bb43b3fc1de44/21w08b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-02-25T11:46:34+00:00", - "sha1": "86af044ab26d3d0ea12c89a4fd7bb43b3fc1de44", - "complianceLevel": 1 - }, - { - "id": "21w08a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e2d09d0147dc73d9664fc008366bb42c4b79db53/21w08a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-02-24T14:38:51+00:00", - "sha1": "e2d09d0147dc73d9664fc008366bb42c4b79db53", - "complianceLevel": 1 - }, - { - "id": "21w07a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/04c7207218db1db47074818a12120007cdcf585c/21w07a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-02-17T16:35:40+00:00", - "sha1": "04c7207218db1db47074818a12120007cdcf585c", - "complianceLevel": 1 - }, - { - "id": "21w06a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/092a3c19e782ffe67ee7754fd90815dbca022305/21w06a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-02-10T17:13:54+00:00", - "sha1": "092a3c19e782ffe67ee7754fd90815dbca022305", - "complianceLevel": 1 - }, - { - "id": "21w05b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1c896f4d33f6dcd5ec0f2443978ad23db4f1799e/21w05b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-02-04T15:09:29+00:00", - "sha1": "1c896f4d33f6dcd5ec0f2443978ad23db4f1799e", - "complianceLevel": 1 - }, - { - "id": "21w05a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cb2bd252eb8289178295a1dffb91ec2284b0d57b/21w05a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-02-03T15:56:54+00:00", - "sha1": "cb2bd252eb8289178295a1dffb91ec2284b0d57b", - "complianceLevel": 1 - }, - { - "id": "21w03a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c79b36194d26122f2b4e123dd6095d75d7fb1c15/21w03a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-01-20T14:56:29+00:00", - "sha1": "c79b36194d26122f2b4e123dd6095d75d7fb1c15", - "complianceLevel": 1 - }, - { - "id": "1.16.5", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/e170c64cf4a18013d24f26e319b350676acfc945/1.16.5.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-01-14T16:05:32+00:00", - "sha1": "e170c64cf4a18013d24f26e319b350676acfc945", - "complianceLevel": 1 - }, - { - "id": "1.16.5-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a851bbc0be7a8c43a67b1f17e2f57eed14cb2f62/1.16.5-rc1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2021-01-13T15:58:55+00:00", - "sha1": "a851bbc0be7a8c43a67b1f17e2f57eed14cb2f62", - "complianceLevel": 1 - }, - { - "id": "20w51a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3ea2ff02293533ec676e97d80700104cae62da8d/20w51a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-12-16T16:27:57+00:00", - "sha1": "3ea2ff02293533ec676e97d80700104cae62da8d", - "complianceLevel": 1 - }, - { - "id": "20w49a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/73aa00a8426373dfc5c3b4c4badcdce6e7ac8b64/20w49a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-12-02T16:47:20+00:00", - "sha1": "73aa00a8426373dfc5c3b4c4badcdce6e7ac8b64", - "complianceLevel": 1 - }, - { - "id": "20w48a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ae546810d18da4ce73510ed90db4d47873262220/20w48a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-11-25T15:42:24+00:00", - "sha1": "ae546810d18da4ce73510ed90db4d47873262220", - "complianceLevel": 1 - }, - { - "id": "20w46a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/44ed6ee5b77aced4fa708f475e728f7983ab3f28/20w46a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-11-11T15:30:32+00:00", - "sha1": "44ed6ee5b77aced4fa708f475e728f7983ab3f28", - "complianceLevel": 1 - }, - { - "id": "20w45a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/731a97004430e91f25d21e7ae4c8212d94f51092/20w45a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-11-04T16:42:00+00:00", - "sha1": "731a97004430e91f25d21e7ae4c8212d94f51092", - "complianceLevel": 1 - }, - { - "id": "1.16.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/0bd63b06f80350f3d941069091a69b79dbb17feb/1.16.4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-10-29T15:49:37+00:00", - "sha1": "0bd63b06f80350f3d941069091a69b79dbb17feb", - "complianceLevel": 1 - }, - { - "id": "1.16.4-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/85680fef8b2fdccc93d6f3d7be5df3ca5169c4b0/1.16.4-rc1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-10-27T16:31:08+00:00", - "sha1": "85680fef8b2fdccc93d6f3d7be5df3ca5169c4b0", - "complianceLevel": 1 - }, - { - "id": "1.16.4-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b743460e017d255c3ac2d123401a977f39c128cc/1.16.4-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-10-22T15:32:17+00:00", - "sha1": "b743460e017d255c3ac2d123401a977f39c128cc", - "complianceLevel": 0 - }, - { - "id": "1.16.4-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9b9af22a6f9a6eba05f16870bc4ec535904dace9/1.16.4-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-10-13T14:36:07+00:00", - "sha1": "9b9af22a6f9a6eba05f16870bc4ec535904dace9", - "complianceLevel": 0 - }, - { - "id": "1.16.3", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/4278e8d3e34b8b89940387315f6a52918766fe49/1.16.3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-09-10T13:42:37+00:00", - "sha1": "4278e8d3e34b8b89940387315f6a52918766fe49", - "complianceLevel": 0 - }, - { - "id": "1.16.3-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/083da90dc260bbe5814c0340f940a3e8267269fd/1.16.3-rc1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-09-07T12:34:06+00:00", - "sha1": "083da90dc260bbe5814c0340f940a3e8267269fd", - "complianceLevel": 0 - }, - { - "id": "1.16.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/8fa0bb1f4f37cb60909daf25ead3d2bd0ed8ac41/1.16.2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-08-11T10:13:46+00:00", - "sha1": "8fa0bb1f4f37cb60909daf25ead3d2bd0ed8ac41", - "complianceLevel": 0 - }, - { - "id": "1.16.2-rc2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/461c78a1d5ef67489b83e33085a7289f96bbd99c/1.16.2-rc2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-08-10T11:43:36+00:00", - "sha1": "461c78a1d5ef67489b83e33085a7289f96bbd99c", - "complianceLevel": 0 - }, - { - "id": "1.16.2-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/22d4bf3f80811814ded11d22a9791c984d15f259/1.16.2-rc1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-08-07T14:35:39+00:00", - "sha1": "22d4bf3f80811814ded11d22a9791c984d15f259", - "complianceLevel": 0 - }, - { - "id": "1.16.2-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ef2827ca977bab3dfc069ed75ac297b74336db9f/1.16.2-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-08-06T16:44:52+00:00", - "sha1": "ef2827ca977bab3dfc069ed75ac297b74336db9f", - "complianceLevel": 0 - }, - { - "id": "1.16.2-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e15a0cad2215fbb2e79ff858621addbbbc428e8d/1.16.2-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-08-05T15:30:50+00:00", - "sha1": "e15a0cad2215fbb2e79ff858621addbbbc428e8d", - "complianceLevel": 0 - }, - { - "id": "1.16.2-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c894bda317eba6031d847ff7a79e149cebe93d85/1.16.2-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-07-29T13:19:05+00:00", - "sha1": "c894bda317eba6031d847ff7a79e149cebe93d85", - "complianceLevel": 0 - }, - { - "id": "20w30a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fce0e735acd683a4a03655dbea53743a721a5efc/20w30a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-07-22T15:05:15+00:00", - "sha1": "fce0e735acd683a4a03655dbea53743a721a5efc", - "complianceLevel": 0 - }, - { - "id": "20w29a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/36f205dac4835939b05b927b395a69f8006a4767/20w29a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-07-15T14:13:47+00:00", - "sha1": "36f205dac4835939b05b927b395a69f8006a4767", - "complianceLevel": 0 - }, - { - "id": "20w28a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7e83a48d6a6cc063745915e360b53762f93515d9/20w28a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-07-08T15:10:40+00:00", - "sha1": "7e83a48d6a6cc063745915e360b53762f93515d9", - "complianceLevel": 0 - }, - { - "id": "20w27a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fd680c04e4b97bd4f6ebe602d60e987581782388/20w27a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-07-01T15:07:35+00:00", - "sha1": "fd680c04e4b97bd4f6ebe602d60e987581782388", - "complianceLevel": 0 - }, - { - "id": "1.16.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/f35b0345b44ba53c34b3b3d832ab394571fb3465/1.16.1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-24T10:31:40+00:00", - "sha1": "f35b0345b44ba53c34b3b3d832ab394571fb3465", - "complianceLevel": 0 - }, - { - "id": "1.16", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/d16202c2e71ebd426703d330a5b9f28f226e6cb5/1.16.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-23T16:20:52+00:00", - "sha1": "d16202c2e71ebd426703d330a5b9f28f226e6cb5", - "complianceLevel": 0 - }, - { - "id": "1.16-rc1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d186aa94d20d12794b8f11c53da9f7a14be0325d/1.16-rc1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-18T12:49:28+00:00", - "sha1": "d186aa94d20d12794b8f11c53da9f7a14be0325d", - "complianceLevel": 0 - }, - { - "id": "1.16-pre8", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ed11182463925c364a5a97ffa86d85ef963cb816/1.16-pre8.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-17T14:45:23+00:00", - "sha1": "ed11182463925c364a5a97ffa86d85ef963cb816", - "complianceLevel": 0 - }, - { - "id": "1.16-pre7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/32125f22ac6d89c3cf8e99b6ba4857785bb2b42c/1.16-pre7.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-16T15:31:35+00:00", - "sha1": "32125f22ac6d89c3cf8e99b6ba4857785bb2b42c", - "complianceLevel": 0 - }, - { - "id": "1.16-pre6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4fd1c5430b5cc928018b0c5c456bc07ca272368a/1.16-pre6.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-15T16:57:57+00:00", - "sha1": "4fd1c5430b5cc928018b0c5c456bc07ca272368a", - "complianceLevel": 0 - }, - { - "id": "1.16-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d5c0b698f70821a74d3642355ea92cddddeb2405/1.16-pre5.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-12T14:33:59+00:00", - "sha1": "d5c0b698f70821a74d3642355ea92cddddeb2405", - "complianceLevel": 0 - }, - { - "id": "1.16-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/511a347d2202962b47a7de8cd5422e1c27edcb08/1.16-pre4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-11T15:45:55+00:00", - "sha1": "511a347d2202962b47a7de8cd5422e1c27edcb08", - "complianceLevel": 0 - }, - { - "id": "1.16-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/442546a6fb8a4c38f56c9f7a184125e37c498e54/1.16-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-10T14:57:43+00:00", - "sha1": "442546a6fb8a4c38f56c9f7a184125e37c498e54", - "complianceLevel": 0 - }, - { - "id": "1.16-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/488605c17a1a88ee63bccebd3848e0f198383efc/1.16-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-05T10:47:59+00:00", - "sha1": "488605c17a1a88ee63bccebd3848e0f198383efc", - "complianceLevel": 0 - }, - { - "id": "1.16-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ccb28a55ef39b70ad5a061147de9ee0703140661/1.16-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-06-04T18:17:51+00:00", - "sha1": "ccb28a55ef39b70ad5a061147de9ee0703140661", - "complianceLevel": 0 - }, - { - "id": "20w22a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e06665e0e07ffc3c517395ae36b8f9f02205f699/20w22a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-05-29T11:25:02+00:00", - "sha1": "e06665e0e07ffc3c517395ae36b8f9f02205f699", - "complianceLevel": 0 - }, - { - "id": "20w21a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ea655312f5138890e464f220ef4d3f51e1852976/20w21a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-05-20T12:07:18+00:00", - "sha1": "ea655312f5138890e464f220ef4d3f51e1852976", - "complianceLevel": 0 - }, - { - "id": "20w20b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bdf490d7e5b79ac70279b84efdc5f902a2dd0725/20w20b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-05-14T08:16:26+00:00", - "sha1": "bdf490d7e5b79ac70279b84efdc5f902a2dd0725", - "complianceLevel": 0 - }, - { - "id": "20w20a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/846f621f5785d8d6f6ea66a4f11fa432b2d96bde/20w20a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-05-13T15:11:43+00:00", - "sha1": "846f621f5785d8d6f6ea66a4f11fa432b2d96bde", - "complianceLevel": 0 - }, - { - "id": "20w19a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2686f94adf89316cc2b997d925ebf36573aca662/20w19a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-05-06T16:23:24+00:00", - "sha1": "2686f94adf89316cc2b997d925ebf36573aca662", - "complianceLevel": 0 - }, - { - "id": "20w18a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c7612f73cd3127b0db783998267f9c17aa334f1c/20w18a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-04-29T15:16:34+00:00", - "sha1": "c7612f73cd3127b0db783998267f9c17aa334f1c", - "complianceLevel": 0 - }, - { - "id": "20w17a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2fc88af698de1b441627b8c718699b3db7af7167/20w17a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-04-22T13:47:50+00:00", - "sha1": "2fc88af698de1b441627b8c718699b3db7af7167", - "complianceLevel": 0 - }, - { - "id": "20w16a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3530597acebc01fb9345396a37c351627a2b5bf5/20w16a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-04-15T14:13:01+00:00", - "sha1": "3530597acebc01fb9345396a37c351627a2b5bf5", - "complianceLevel": 0 - }, - { - "id": "20w15a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cfaef10849d0398c2aa6e5816fdd3f8433c4c54d/20w15a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-04-08T12:29:24+00:00", - "sha1": "cfaef10849d0398c2aa6e5816fdd3f8433c4c54d", - "complianceLevel": 0 - }, - { - "id": "20w14a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0f5bb66dcd44e62fa0f394b6fc602d7886aa1f9c/20w14a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-04-02T14:28:06+00:00", - "sha1": "0f5bb66dcd44e62fa0f394b6fc602d7886aa1f9c", - "complianceLevel": 0 - }, - { - "id": "20w14infinite", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fc7300d5692da1906da7e2b35a516df85c29758a/20w14infinite.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-04-01T12:47:08+00:00", - "sha1": "fc7300d5692da1906da7e2b35a516df85c29758a", - "complianceLevel": 0 - }, - { - "id": "20w13b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d1b2f506a5da3f1751cdd615cd68cdbd08992755/20w13b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-03-26T13:00:34+00:00", - "sha1": "d1b2f506a5da3f1751cdd615cd68cdbd08992755", - "complianceLevel": 0 - }, - { - "id": "20w13a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1954cc0b523cfc9dfda2609da8b1823422f04b55/20w13a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-03-25T17:05:33+00:00", - "sha1": "1954cc0b523cfc9dfda2609da8b1823422f04b55", - "complianceLevel": 0 - }, - { - "id": "20w12a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/24e51bfe623486b492e1662bf916f987447c7fc0/20w12a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-03-18T16:42:06+00:00", - "sha1": "24e51bfe623486b492e1662bf916f987447c7fc0", - "complianceLevel": 0 - }, - { - "id": "20w11a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3ab55c9f5f06b0847279111da8e3ff446e92fe9a/20w11a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-03-11T16:28:27+00:00", - "sha1": "3ab55c9f5f06b0847279111da8e3ff446e92fe9a", - "complianceLevel": 0 - }, - { - "id": "20w10a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1c60065e325e47faf2902d829892affd2c2c2f43/20w10a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-03-04T16:21:41+00:00", - "sha1": "1c60065e325e47faf2902d829892affd2c2c2f43", - "complianceLevel": 0 - }, - { - "id": "20w09a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/296233525630c6180d31de8f8beb4b17ef485c4e/20w09a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-02-26T16:43:08+00:00", - "sha1": "296233525630c6180d31de8f8beb4b17ef485c4e", - "complianceLevel": 0 - }, - { - "id": "20w08a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/47bd5a3b888712334222670a7ef0db2b15d07507/20w08a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-02-19T13:30:09+00:00", - "sha1": "47bd5a3b888712334222670a7ef0db2b15d07507", - "complianceLevel": 0 - }, - { - "id": "20w07a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/971091dd31c7d056fb0c34da1f56115357b92d64/20w07a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-02-14T13:20:49+00:00", - "sha1": "971091dd31c7d056fb0c34da1f56115357b92d64", - "complianceLevel": 0 - }, - { - "id": "20w06a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a90a304e229611052bf5e8b7969811895d2363ec/20w06a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-02-05T16:05:22+00:00", - "sha1": "a90a304e229611052bf5e8b7969811895d2363ec", - "complianceLevel": 0 - }, - { - "id": "1.15.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/9166cb9b4aebc20dd497bdab4fbf00ace2f5a052/1.15.2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-01-17T10:03:52+00:00", - "sha1": "9166cb9b4aebc20dd497bdab4fbf00ace2f5a052", - "complianceLevel": 0 - }, - { - "id": "1.15.2-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a0d450b3e8aef2ce64547a7b43388f91fca7dcdb/1.15.2-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-01-16T12:35:57+00:00", - "sha1": "a0d450b3e8aef2ce64547a7b43388f91fca7dcdb", - "complianceLevel": 0 - }, - { - "id": "1.15.2-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cd814f996f6c6c0feb779193110ffb01ff46182c/1.15.2-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2020-01-14T16:19:31+00:00", - "sha1": "cd814f996f6c6c0feb779193110ffb01ff46182c", - "complianceLevel": 0 - }, - { - "id": "1.15.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/b77dd303ad3179a7eee7f5de0bf088526093023e/1.15.1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-12-16T10:29:47+00:00", - "sha1": "b77dd303ad3179a7eee7f5de0bf088526093023e", - "complianceLevel": 0 - }, - { - "id": "1.15.1-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fb5a6de12fa23e235cd20c64226edc74ac5872a6/1.15.1-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-12-12T14:02:30+00:00", - "sha1": "fb5a6de12fa23e235cd20c64226edc74ac5872a6", - "complianceLevel": 0 - }, - { - "id": "1.15", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/1dad71401651c94c7171665fea535f9c6ba52f3f/1.15.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-12-09T13:13:38+00:00", - "sha1": "1dad71401651c94c7171665fea535f9c6ba52f3f", - "complianceLevel": 0 - }, - { - "id": "1.15-pre7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/011f917f195984b059a814637ad61ed06fcb811c/1.15-pre7.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-12-09T12:14:11+00:00", - "sha1": "011f917f195984b059a814637ad61ed06fcb811c", - "complianceLevel": 0 - }, - { - "id": "1.15-pre6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4ca7685007e63b65a059b7842bdad9d399d1a793/1.15-pre6.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-12-06T12:04:30+00:00", - "sha1": "4ca7685007e63b65a059b7842bdad9d399d1a793", - "complianceLevel": 0 - }, - { - "id": "1.15-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6671e747697d323e2281ef23027e93bb6efdff66/1.15-pre5.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-12-05T13:20:00+00:00", - "sha1": "6671e747697d323e2281ef23027e93bb6efdff66", - "complianceLevel": 0 - }, - { - "id": "1.15-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/eef5497322508e5e5a64323831e09730891e3356/1.15-pre4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-12-03T12:24:24+00:00", - "sha1": "eef5497322508e5e5a64323831e09730891e3356", - "complianceLevel": 0 - }, - { - "id": "1.15-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8d976248bd81bae8c505cd283d32fe40cb3e3f88/1.15-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-11-28T17:17:50+00:00", - "sha1": "8d976248bd81bae8c505cd283d32fe40cb3e3f88", - "complianceLevel": 0 - }, - { - "id": "1.15-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fca16cdf1a95ddd2e3429d392e105ab764463d32/1.15-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-11-25T18:09:38+00:00", - "sha1": "fca16cdf1a95ddd2e3429d392e105ab764463d32", - "complianceLevel": 0 - }, - { - "id": "1.15-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ff10a13b1e949c107718ab00c57f8d28b6bd6469/1.15-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-11-21T17:01:17+00:00", - "sha1": "ff10a13b1e949c107718ab00c57f8d28b6bd6469", - "complianceLevel": 0 - }, - { - "id": "19w46b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/752ca21d7c97769060a9014e3c6e87a1f282cfdc/19w46b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-11-14T13:29:24+00:00", - "sha1": "752ca21d7c97769060a9014e3c6e87a1f282cfdc", - "complianceLevel": 0 - }, - { - "id": "19w46a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4eceb0cf1134a32fd07317c3133433cb3c6ffdc6/19w46a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-11-13T16:37:46+00:00", - "sha1": "4eceb0cf1134a32fd07317c3133433cb3c6ffdc6", - "complianceLevel": 0 - }, - { - "id": "19w45b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/36471285304b62f13ae2357e3415da851e7e6451/19w45b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-11-08T12:42:44+00:00", - "sha1": "36471285304b62f13ae2357e3415da851e7e6451", - "complianceLevel": 0 - }, - { - "id": "19w45a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0522a25703b1f1f4fd20cc9c4b7dd3f31d5e20ec/19w45a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-11-07T16:19:20+00:00", - "sha1": "0522a25703b1f1f4fd20cc9c4b7dd3f31d5e20ec", - "complianceLevel": 0 - }, - { - "id": "19w44a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/509a9ec030e6918e864ecec78c9f5e17e7939be7/19w44a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-10-30T15:31:44+00:00", - "sha1": "509a9ec030e6918e864ecec78c9f5e17e7939be7", - "complianceLevel": 0 - }, - { - "id": "19w42a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/505d64ec64e85c143364b6f556b79aaeb3d6b5b9/19w42a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-10-16T15:30:39+00:00", - "sha1": "505d64ec64e85c143364b6f556b79aaeb3d6b5b9", - "complianceLevel": 0 - }, - { - "id": "19w41a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f35731ffd73f5be68f92bdee41f003d6a873b10c/19w41a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-10-09T15:21:35+00:00", - "sha1": "f35731ffd73f5be68f92bdee41f003d6a873b10c", - "complianceLevel": 0 - }, - { - "id": "19w40a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ba7479f8c70d9b853352a25673d040da84ba43b0/19w40a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-10-02T13:40:26+00:00", - "sha1": "ba7479f8c70d9b853352a25673d040da84ba43b0", - "complianceLevel": 0 - }, - { - "id": "19w39a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7bf656114aa9e2541900cbb8cce9a9c92adca41e/19w39a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-09-27T10:13:33+00:00", - "sha1": "7bf656114aa9e2541900cbb8cce9a9c92adca41e", - "complianceLevel": 0 - }, - { - "id": "19w38b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/544ca8bd5c8a92efb2a7d4701dbe9f5d47250802/19w38b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-09-18T14:59:13+00:00", - "sha1": "544ca8bd5c8a92efb2a7d4701dbe9f5d47250802", - "complianceLevel": 0 - }, - { - "id": "19w38a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/becef2be7f7b9a49e53019f91c06c71633cb9b94/19w38a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-09-18T10:03:22+00:00", - "sha1": "becef2be7f7b9a49e53019f91c06c71633cb9b94", - "complianceLevel": 0 - }, - { - "id": "19w37a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/808c2362c7e8c9cf45b5bae535cbf259286f435e/19w37a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-09-11T11:46:44+00:00", - "sha1": "808c2362c7e8c9cf45b5bae535cbf259286f435e", - "complianceLevel": 0 - }, - { - "id": "19w36a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ddf7c1fafa23f947cd8d05e089ae149b7c204eb9/19w36a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-09-04T11:19:34+00:00", - "sha1": "ddf7c1fafa23f947cd8d05e089ae149b7c204eb9", - "complianceLevel": 0 - }, - { - "id": "19w35a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4c90ba12de0ee35960d1fc114f711c7aa054d311/19w35a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-08-28T15:01:44+00:00", - "sha1": "4c90ba12de0ee35960d1fc114f711c7aa054d311", - "complianceLevel": 0 - }, - { - "id": "19w34a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fb5a0d36b6af1da6df46e3d41d2de8cb287b2d81/19w34a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-08-22T12:06:21+00:00", - "sha1": "fb5a0d36b6af1da6df46e3d41d2de8cb287b2d81", - "complianceLevel": 0 - }, - { - "id": "1.14.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/ddce2c9334b7c3f111cca1f4244351eebe5977be/1.14.4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-19T09:25:47+00:00", - "sha1": "ddce2c9334b7c3f111cca1f4244351eebe5977be", - "complianceLevel": 0 - }, - { - "id": "1.14.4-pre7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/70563174d8f6b2503d2e36034dfead914f280e5f/1.14.4-pre7.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-18T11:32:36+00:00", - "sha1": "70563174d8f6b2503d2e36034dfead914f280e5f", - "complianceLevel": 0 - }, - { - "id": "1.14.4-pre6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4b69fc0fedf211de83cbc1e0a2d0d9ffa4d73bf7/1.14.4-pre6.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-15T12:39:49+00:00", - "sha1": "4b69fc0fedf211de83cbc1e0a2d0d9ffa4d73bf7", - "complianceLevel": 0 - }, - { - "id": "1.14.4-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/de726668a6a33edf6245d78e6b59afbca1d55ec3/1.14.4-pre5.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-11T10:52:33+00:00", - "sha1": "de726668a6a33edf6245d78e6b59afbca1d55ec3", - "complianceLevel": 0 - }, - { - "id": "1.14.4-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/295fa57f23b0be11b6cb7664b8bccb0a536a6f0a/1.14.4-pre4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-10T12:53:29+00:00", - "sha1": "295fa57f23b0be11b6cb7664b8bccb0a536a6f0a", - "complianceLevel": 0 - }, - { - "id": "1.14.4-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c8f236dc847c514303a38ad5f797f9830c9b3617/1.14.4-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-08T11:21:42+00:00", - "sha1": "c8f236dc847c514303a38ad5f797f9830c9b3617", - "complianceLevel": 0 - }, - { - "id": "1.14.4-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5a89e3a05c84b0f929a43a2ec0d21ad9ebc2f9be/1.14.4-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-04T14:41:05+00:00", - "sha1": "5a89e3a05c84b0f929a43a2ec0d21ad9ebc2f9be", - "complianceLevel": 0 - }, - { - "id": "1.14.4-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/559bfce8a1a85d3b54dbbca6960524696fb4a9c2/1.14.4-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-07-03T13:01:01+00:00", - "sha1": "559bfce8a1a85d3b54dbbca6960524696fb4a9c2", - "complianceLevel": 0 - }, - { - "id": "1.14.3", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/d23e2ebef86c3c0cdb77debe28f9cdd21a8bcca8/1.14.3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-06-24T12:52:52+00:00", - "sha1": "d23e2ebef86c3c0cdb77debe28f9cdd21a8bcca8", - "complianceLevel": 0 - }, - { - "id": "1.14.3-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f51a633fac81e1b3fb27a2aabed5498dc2309bd9/1.14.3-pre4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-06-19T11:44:29+00:00", - "sha1": "f51a633fac81e1b3fb27a2aabed5498dc2309bd9", - "complianceLevel": 0 - }, - { - "id": "1.14.3-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3d7f9d32fb64e8b716172b11a0a5cdb364ef8477/1.14.3-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-06-14T08:03:33+00:00", - "sha1": "3d7f9d32fb64e8b716172b11a0a5cdb364ef8477", - "complianceLevel": 0 - }, - { - "id": "1.14.3-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/65675a87f3679e4f9501549bd92c5c3a122088a7/1.14.3-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-06-07T09:11:29+00:00", - "sha1": "65675a87f3679e4f9501549bd92c5c3a122088a7", - "complianceLevel": 0 - }, - { - "id": "1.14.3-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6244f7d09025ee9545e260fc7aa854fc04459429/1.14.3-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-06-03T14:34:20+00:00", - "sha1": "6244f7d09025ee9545e260fc7aa854fc04459429", - "complianceLevel": 0 - }, - { - "id": "1.14.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/2470772708c1505f17ded499f8c49cabfbf6695b/1.14.2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-27T11:48:25+00:00", - "sha1": "2470772708c1505f17ded499f8c49cabfbf6695b", - "complianceLevel": 0 - }, - { - "id": "1.14.2 Pre-Release 4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b2404b413cec5880d8516daa57251c3206fdb60a/1.14.2%20Pre-Release%204.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-27T07:21:11+00:00", - "sha1": "b2404b413cec5880d8516daa57251c3206fdb60a", - "complianceLevel": 0 - }, - { - "id": "1.14.2 Pre-Release 3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/150179dd955b478bd2ab2540b2adce08a4a821a7/1.14.2%20Pre-Release%203.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-22T13:12:51+00:00", - "sha1": "150179dd955b478bd2ab2540b2adce08a4a821a7", - "complianceLevel": 0 - }, - { - "id": "1.14.2 Pre-Release 2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a2fb1bd0dc87d478f64b15b5c113491d96a7ca0d/1.14.2%20Pre-Release%202.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-17T12:21:03+00:00", - "sha1": "a2fb1bd0dc87d478f64b15b5c113491d96a7ca0d", - "complianceLevel": 0 - }, - { - "id": "1.14.2 Pre-Release 1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/aa963d59f695118f7cf4941baa9a5a862bc0c319/1.14.2%20Pre-Release%201.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-16T15:40:25+00:00", - "sha1": "aa963d59f695118f7cf4941baa9a5a862bc0c319", - "complianceLevel": 0 - }, - { - "id": "1.14.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/a5c3a8199343f4f38ed710f59b09eed07226389d/1.14.1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-13T11:10:12+00:00", - "sha1": "a5c3a8199343f4f38ed710f59b09eed07226389d", - "complianceLevel": 0 - }, - { - "id": "1.14.1 Pre-Release 2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6dbd74609fcf04c8fac36ba4ea97197dc1c469a5/1.14.1%20Pre-Release%202.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-09T14:01:04+00:00", - "sha1": "6dbd74609fcf04c8fac36ba4ea97197dc1c469a5", - "complianceLevel": 0 - }, - { - "id": "1.14.1 Pre-Release 1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5cea4f7f999716d6820113bac1423747e2aeaead/1.14.1%20Pre-Release%201.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-05-07T14:44:42+00:00", - "sha1": "5cea4f7f999716d6820113bac1423747e2aeaead", - "complianceLevel": 0 - }, - { - "id": "1.14", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/51e423527d9452c66ca2a9baed183e24836bc469/1.14.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-23T14:52:44+00:00", - "sha1": "51e423527d9452c66ca2a9baed183e24836bc469", - "complianceLevel": 0 - }, - { - "id": "1.14 Pre-Release 5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4e8a06816edfc2ad7caaeb56d0e316bb239d1b50/1.14%20Pre-Release%205.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-18T11:05:19+00:00", - "sha1": "4e8a06816edfc2ad7caaeb56d0e316bb239d1b50", - "complianceLevel": 0 - }, - { - "id": "1.14 Pre-Release 4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e7c41208869e5f90f1831175498278c7879fc78d/1.14%20Pre-Release%204.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-17T15:31:12+00:00", - "sha1": "e7c41208869e5f90f1831175498278c7879fc78d", - "complianceLevel": 0 - }, - { - "id": "1.14 Pre-Release 3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cc36fcfc92f6ad9f8602b29e96c139e3b655045e/1.14%20Pre-Release%203.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-16T13:57:10+00:00", - "sha1": "cc36fcfc92f6ad9f8602b29e96c139e3b655045e", - "complianceLevel": 0 - }, - { - "id": "1.14 Pre-Release 2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4818a107a745213ba1d0405d77d3fad068ebd77e/1.14%20Pre-Release%202.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-12T11:38:53+00:00", - "sha1": "4818a107a745213ba1d0405d77d3fad068ebd77e", - "complianceLevel": 0 - }, - { - "id": "1.14 Pre-Release 1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/31cc332c857ca780cfefaf483d7207e0193cc015/1.14%20Pre-Release%201.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-10T14:24:16+00:00", - "sha1": "31cc332c857ca780cfefaf483d7207e0193cc015", - "complianceLevel": 0 - }, - { - "id": "19w14b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f21a8218b851b7fc9786a27c913e9d5531f140c5/19w14b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-05T10:33:58+00:00", - "sha1": "f21a8218b851b7fc9786a27c913e9d5531f140c5", - "complianceLevel": 0 - }, - { - "id": "19w14a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/679d9c2f0d36497cc151ffe80336ccd744994ee2/19w14a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-03T13:45:00+00:00", - "sha1": "679d9c2f0d36497cc151ffe80336ccd744994ee2", - "complianceLevel": 0 - }, - { - "id": "3D Shareware v1.34", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0dcd6cd40ecaf3a92df2956662658beaa6570bb4/3D%20Shareware%20v1.34.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-04-01T11:18:08+00:00", - "sha1": "0dcd6cd40ecaf3a92df2956662658beaa6570bb4", - "complianceLevel": 0 - }, - { - "id": "19w13b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/aa179615f15a1e64fca1b29bc5b3d1189ce2ea64/19w13b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-03-29T16:53:22+00:00", - "sha1": "aa179615f15a1e64fca1b29bc5b3d1189ce2ea64", - "complianceLevel": 0 - }, - { - "id": "19w13a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/59a9073d427f78418e866f488f167b202205a3fb/19w13a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-03-27T15:15:31+00:00", - "sha1": "59a9073d427f78418e866f488f167b202205a3fb", - "complianceLevel": 0 - }, - { - "id": "19w12b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cae2698ee99912dc85c9a736d831fd227efd43a4/19w12b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-03-21T15:20:01+00:00", - "sha1": "cae2698ee99912dc85c9a736d831fd227efd43a4", - "complianceLevel": 0 - }, - { - "id": "19w12a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6a02abaa072b8a6ed70028cc86422e259c485ca2/19w12a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-03-20T16:47:34+00:00", - "sha1": "6a02abaa072b8a6ed70028cc86422e259c485ca2", - "complianceLevel": 0 - }, - { - "id": "19w11b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1d257ff03d25d5134bb97867df162f19a200fb5a/19w11b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-03-14T14:26:23+00:00", - "sha1": "1d257ff03d25d5134bb97867df162f19a200fb5a", - "complianceLevel": 0 - }, - { - "id": "19w11a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f024f3f0c9ec2db39265f2a000d5a5b6c06e3cb6/19w11a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-03-13T13:59:29+00:00", - "sha1": "f024f3f0c9ec2db39265f2a000d5a5b6c06e3cb6", - "complianceLevel": 0 - }, - { - "id": "19w09a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/80fbab381b291eb7b8084242719c1a3e4be4359a/19w09a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-02-27T14:44:30+00:00", - "sha1": "80fbab381b291eb7b8084242719c1a3e4be4359a", - "complianceLevel": 0 - }, - { - "id": "19w08b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/df233cad676cf4f22babe7a9520ee35f8e3fa682/19w08b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-02-21T13:38:09+00:00", - "sha1": "df233cad676cf4f22babe7a9520ee35f8e3fa682", - "complianceLevel": 0 - }, - { - "id": "19w08a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cbccd6e63b4aa9ade72c065e2bdf841f1cdc8de9/19w08a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-02-20T14:56:58+00:00", - "sha1": "cbccd6e63b4aa9ade72c065e2bdf841f1cdc8de9", - "complianceLevel": 0 - }, - { - "id": "19w07a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6a11008b298a302dcfe8837cf6234b57c0131430/19w07a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-02-13T16:12:08+00:00", - "sha1": "6a11008b298a302dcfe8837cf6234b57c0131430", - "complianceLevel": 0 - }, - { - "id": "19w06a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/39b62f76fdb94262b68ebc63528d81575d53bf09/19w06a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-02-06T16:24:13+00:00", - "sha1": "39b62f76fdb94262b68ebc63528d81575d53bf09", - "complianceLevel": 0 - }, - { - "id": "19w05a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/58ae1f9302d8594323e91e1873a67aa51f231373/19w05a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-01-30T15:16:49+00:00", - "sha1": "58ae1f9302d8594323e91e1873a67aa51f231373", - "complianceLevel": 0 - }, - { - "id": "19w04b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/455150dfefc0ddfacadc72e35a33d391bb09dac2/19w04b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-01-25T12:20:15+00:00", - "sha1": "455150dfefc0ddfacadc72e35a33d391bb09dac2", - "complianceLevel": 0 - }, - { - "id": "19w04a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b04886bc4a872b66198af72824085b2b26ca3a84/19w04a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-01-24T15:31:52+00:00", - "sha1": "b04886bc4a872b66198af72824085b2b26ca3a84", - "complianceLevel": 0 - }, - { - "id": "19w03c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4a79d8dc2b2105b8ea530a19581a70fefe40d2c3/19w03c.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-01-18T11:27:13+00:00", - "sha1": "4a79d8dc2b2105b8ea530a19581a70fefe40d2c3", - "complianceLevel": 0 - }, - { - "id": "19w03b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7c57d2889b35c4d7021caea1537035b24469e287/19w03b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-01-17T16:43:27+00:00", - "sha1": "7c57d2889b35c4d7021caea1537035b24469e287", - "complianceLevel": 0 - }, - { - "id": "19w03a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2c4fac2d0d6d1e8671e70eaf07a400af7cf31a83/19w03a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-01-16T16:45:02+00:00", - "sha1": "2c4fac2d0d6d1e8671e70eaf07a400af7cf31a83", - "complianceLevel": 0 - }, - { - "id": "19w02a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8b8b0cc1995babd8f12a03b31810a85924a39b37/19w02a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2019-01-09T15:52:07+00:00", - "sha1": "8b8b0cc1995babd8f12a03b31810a85924a39b37", - "complianceLevel": 0 - }, - { - "id": "18w50a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/dd51bf7c49fab2635fdf42a3f473fc5e9e752767/18w50a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-12-12T14:58:13+00:00", - "sha1": "dd51bf7c49fab2635fdf42a3f473fc5e9e752767", - "complianceLevel": 0 - }, - { - "id": "18w49a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b53ab0a9b094c761f61ab5b6f15c3f3006d0b434/18w49a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-12-05T12:24:30+00:00", - "sha1": "b53ab0a9b094c761f61ab5b6f15c3f3006d0b434", - "complianceLevel": 0 - }, - { - "id": "18w48b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/efb357fa3fa36f494bef9aeee5ba7f115ed93b5b/18w48b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-11-30T10:37:31+00:00", - "sha1": "efb357fa3fa36f494bef9aeee5ba7f115ed93b5b", - "complianceLevel": 0 - }, - { - "id": "18w48a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/331900b9cb5c5021522212ffb2d99533a8946e4f/18w48a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-11-29T13:11:38+00:00", - "sha1": "331900b9cb5c5021522212ffb2d99533a8946e4f", - "complianceLevel": 0 - }, - { - "id": "18w47b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a6614940328c1090ec4e46f124e7523ee7669b71/18w47b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-11-23T10:46:41+00:00", - "sha1": "a6614940328c1090ec4e46f124e7523ee7669b71", - "complianceLevel": 0 - }, - { - "id": "18w47a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d4f1839347872bc99a9083c837dbf1542fda4c3d/18w47a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-11-21T15:45:22+00:00", - "sha1": "d4f1839347872bc99a9083c837dbf1542fda4c3d", - "complianceLevel": 0 - }, - { - "id": "18w46a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/62aadb6705cba3fbf8b7422159eb7094b1052538/18w46a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-11-15T13:43:14+00:00", - "sha1": "62aadb6705cba3fbf8b7422159eb7094b1052538", - "complianceLevel": 0 - }, - { - "id": "18w45a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/013b86912dfa399dc1fb5efa19b0298818aed32e/18w45a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-11-07T14:40:06+00:00", - "sha1": "013b86912dfa399dc1fb5efa19b0298818aed32e", - "complianceLevel": 0 - }, - { - "id": "18w44a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/729ad955e6577769c01b0a3c66f12f49ca0f6638/18w44a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-10-31T15:29:16+00:00", - "sha1": "729ad955e6577769c01b0a3c66f12f49ca0f6638", - "complianceLevel": 0 - }, - { - "id": "18w43c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ac786a8ec42768f36bc13141441813720487b503/18w43c.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-10-26T08:40:46+00:00", - "sha1": "ac786a8ec42768f36bc13141441813720487b503", - "complianceLevel": 0 - }, - { - "id": "18w43b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/582ea83fd2770c8aa6891b8312b8a5a84073d0dd/18w43b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-10-24T15:02:30+00:00", - "sha1": "582ea83fd2770c8aa6891b8312b8a5a84073d0dd", - "complianceLevel": 0 - }, - { - "id": "18w43a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/27dde2120eefa07688f6f19de55700915f12102a/18w43a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-10-24T10:52:16+00:00", - "sha1": "27dde2120eefa07688f6f19de55700915f12102a", - "complianceLevel": 0 - }, - { - "id": "1.13.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/30c27b896ab17e7bfebe488943944bcc213c63c4/1.13.2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-10-22T11:41:07+00:00", - "sha1": "30c27b896ab17e7bfebe488943944bcc213c63c4", - "complianceLevel": 0 - }, - { - "id": "1.13.2-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a54569dcdb0337eef026f594dfa29ceb8695e795/1.13.2-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-10-18T14:46:12+00:00", - "sha1": "a54569dcdb0337eef026f594dfa29ceb8695e795", - "complianceLevel": 0 - }, - { - "id": "1.13.2-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a71d297aa508c62336f9a4a61972d8ba9ee20a18/1.13.2-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-10-16T13:40:58+00:00", - "sha1": "a71d297aa508c62336f9a4a61972d8ba9ee20a18", - "complianceLevel": 0 - }, - { - "id": "1.13.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/89c6be1d7ac323633e85d50f2be81b175fa5aaa5/1.13.1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-08-22T14:03:42+00:00", - "sha1": "89c6be1d7ac323633e85d50f2be81b175fa5aaa5", - "complianceLevel": 0 - }, - { - "id": "1.13.1-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1a0cf4d5576afd039ac8a351afdf4caf79b82d0f/1.13.1-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-08-20T13:52:09+00:00", - "sha1": "1a0cf4d5576afd039ac8a351afdf4caf79b82d0f", - "complianceLevel": 0 - }, - { - "id": "1.13.1-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cf4562f5ef01224a323854965adaf9b6cb340413/1.13.1-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-08-16T13:08:44+00:00", - "sha1": "cf4562f5ef01224a323854965adaf9b6cb340413", - "complianceLevel": 0 - }, - { - "id": "18w33a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4f4d199dcf66316aaf362a9ada72b9470172781c/18w33a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-08-15T14:28:56+00:00", - "sha1": "4f4d199dcf66316aaf362a9ada72b9470172781c", - "complianceLevel": 0 - }, - { - "id": "18w32a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/db92593dbf1ef5679e3a7d56f1a3f3dff968ed9c/18w32a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-08-08T13:16:57+00:00", - "sha1": "db92593dbf1ef5679e3a7d56f1a3f3dff968ed9c", - "complianceLevel": 0 - }, - { - "id": "18w31a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/db8320ce0a6ff6b3828503a33935e27b5941a6fa/18w31a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-08-01T12:54:44+00:00", - "sha1": "db8320ce0a6ff6b3828503a33935e27b5941a6fa", - "complianceLevel": 0 - }, - { - "id": "18w30b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/06e14f24c63f5a6122ec4750a9edced81fcdbcee/18w30b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-26T16:06:57+00:00", - "sha1": "06e14f24c63f5a6122ec4750a9edced81fcdbcee", - "complianceLevel": 0 - }, - { - "id": "18w30a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/341a20b9fcbae8acce4e8f856a57ad758c5027cd/18w30a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-25T14:29:31+00:00", - "sha1": "341a20b9fcbae8acce4e8f856a57ad758c5027cd", - "complianceLevel": 0 - }, - { - "id": "1.13", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/0097ad482f1f7e26c8b6c6d7464cd78cce3037f6/1.13.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-18T15:11:46+00:00", - "sha1": "0097ad482f1f7e26c8b6c6d7464cd78cce3037f6", - "complianceLevel": 0 - }, - { - "id": "1.13-pre10", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e0e320607322f0445728c5c59385c0f8137542e3/1.13-pre10.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-17T14:48:06+00:00", - "sha1": "e0e320607322f0445728c5c59385c0f8137542e3", - "complianceLevel": 0 - }, - { - "id": "1.13-pre9", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/202d6b18a9c1542ce993626f7cfb96d6e793c87c/1.13-pre9.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-16T14:17:42+00:00", - "sha1": "202d6b18a9c1542ce993626f7cfb96d6e793c87c", - "complianceLevel": 0 - }, - { - "id": "1.13-pre8", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6116dc4446cfe71d278681d268fade2574211ff3/1.13-pre8.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-13T11:45:00+00:00", - "sha1": "6116dc4446cfe71d278681d268fade2574211ff3", - "complianceLevel": 0 - }, - { - "id": "1.13-pre7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bb75e277efa2c472cb739f008e372c2e671a7544/1.13-pre7.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-10T14:21:42+00:00", - "sha1": "bb75e277efa2c472cb739f008e372c2e671a7544", - "complianceLevel": 0 - }, - { - "id": "1.13-pre6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d432a761e6fb669dfef73f70fa3c7cd1f2187d1b/1.13-pre6.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-07-04T12:36:00+00:00", - "sha1": "d432a761e6fb669dfef73f70fa3c7cd1f2187d1b", - "complianceLevel": 0 - }, - { - "id": "1.13-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e55379ae9ee4fea24f01ae6a0115a4d7f63cf516/1.13-pre5.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-06-28T13:58:53+00:00", - "sha1": "e55379ae9ee4fea24f01ae6a0115a4d7f63cf516", - "complianceLevel": 0 - }, - { - "id": "1.13-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3aae6e949ea89920d3dd63631e0490239420abc1/1.13-pre4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-06-26T13:00:55+00:00", - "sha1": "3aae6e949ea89920d3dd63631e0490239420abc1", - "complianceLevel": 0 - }, - { - "id": "1.13-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6edd02d9f9f731161adb874704acdd836159959d/1.13-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-06-21T12:57:11+00:00", - "sha1": "6edd02d9f9f731161adb874704acdd836159959d", - "complianceLevel": 0 - }, - { - "id": "1.13-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ac5de81861381a1c0974822d365f01bf173760ca/1.13-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-06-15T09:20:00+00:00", - "sha1": "ac5de81861381a1c0974822d365f01bf173760ca", - "complianceLevel": 0 - }, - { - "id": "1.13-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3e67f37ee92a845743d2c9bd2157e93990936ba0/1.13-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-06-04T15:17:34+00:00", - "sha1": "3e67f37ee92a845743d2c9bd2157e93990936ba0", - "complianceLevel": 0 - }, - { - "id": "18w22c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/93e65647ee53bb7dcd45f1dd8f36ee5dbd465f50/18w22c.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-31T13:53:15+00:00", - "sha1": "93e65647ee53bb7dcd45f1dd8f36ee5dbd465f50", - "complianceLevel": 0 - }, - { - "id": "18w22b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/02ee100fca2a5b1fa1113eff7a96d9a5cbb0f709/18w22b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-30T13:48:58+00:00", - "sha1": "02ee100fca2a5b1fa1113eff7a96d9a5cbb0f709", - "complianceLevel": 0 - }, - { - "id": "18w22a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/45b9795e75181b5cc4b3bade1ca0b393667ed944/18w22a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-29T13:23:55+00:00", - "sha1": "45b9795e75181b5cc4b3bade1ca0b393667ed944", - "complianceLevel": 0 - }, - { - "id": "18w21b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9f66ce47412a5edeaf61789a8ff8a6676cf16efa/18w21b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-25T10:09:09+00:00", - "sha1": "9f66ce47412a5edeaf61789a8ff8a6676cf16efa", - "complianceLevel": 0 - }, - { - "id": "18w21a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ccc197fb3d6e1133ec18471f50cabdcb953531f0/18w21a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-23T13:11:49+00:00", - "sha1": "ccc197fb3d6e1133ec18471f50cabdcb953531f0", - "complianceLevel": 0 - }, - { - "id": "18w20c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/85139e2e9fc5811ff3bc0ba0880fb9dbbae6e814/18w20c.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-17T14:06:56+00:00", - "sha1": "85139e2e9fc5811ff3bc0ba0880fb9dbbae6e814", - "complianceLevel": 0 - }, - { - "id": "18w20b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d11121f294451cdff97a3068795b8130a04eb854/18w20b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-16T14:35:35+00:00", - "sha1": "d11121f294451cdff97a3068795b8130a04eb854", - "complianceLevel": 0 - }, - { - "id": "18w20a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/110c46651542646cb02e4c2b1fa9684e2b9aada4/18w20a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-15T14:02:25+00:00", - "sha1": "110c46651542646cb02e4c2b1fa9684e2b9aada4", - "complianceLevel": 0 - }, - { - "id": "18w19b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3c71f6e7dae5eec50f06c83be22f0e2c31568d3d/18w19b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-09T10:00:51+00:00", - "sha1": "3c71f6e7dae5eec50f06c83be22f0e2c31568d3d", - "complianceLevel": 0 - }, - { - "id": "18w19a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f684373251ab70803638248c7458344e7c556750/18w19a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-05-08T13:05:19+00:00", - "sha1": "f684373251ab70803638248c7458344e7c556750", - "complianceLevel": 0 - }, - { - "id": "18w16a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2abb9a0e7c59550376aa25b88a1f81b29fdec7c6/18w16a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-04-19T14:46:35+00:00", - "sha1": "2abb9a0e7c59550376aa25b88a1f81b29fdec7c6", - "complianceLevel": 0 - }, - { - "id": "18w15a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/894422d8f57bbf1c4ee6655ab19880bd112b4200/18w15a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-04-11T14:54:22+00:00", - "sha1": "894422d8f57bbf1c4ee6655ab19880bd112b4200", - "complianceLevel": 0 - }, - { - "id": "18w14b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/820129bc81b4e5316ba682b16bb8c593898da365/18w14b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-04-05T14:44:02+00:00", - "sha1": "820129bc81b4e5316ba682b16bb8c593898da365", - "complianceLevel": 0 - }, - { - "id": "18w14a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2b64daa3a1a6f3a5ab53159085fdfc880cb0826f/18w14a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-04-04T14:36:14+00:00", - "sha1": "2b64daa3a1a6f3a5ab53159085fdfc880cb0826f", - "complianceLevel": 0 - }, - { - "id": "18w11a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a23f8d5bf577269443a9bdbb0e96d7d9f4d2f6dc/18w11a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-03-13T15:10:59+00:00", - "sha1": "a23f8d5bf577269443a9bdbb0e96d7d9f4d2f6dc", - "complianceLevel": 0 - }, - { - "id": "18w10d", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0e4b7afdc5959d9b99c313a2bf6b92e56ff66f99/18w10d.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-03-09T15:19:12+00:00", - "sha1": "0e4b7afdc5959d9b99c313a2bf6b92e56ff66f99", - "complianceLevel": 0 - }, - { - "id": "18w10c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f8294165c2e8c880efb849a59663c529f7e5bce0/18w10c.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-03-08T15:29:23+00:00", - "sha1": "f8294165c2e8c880efb849a59663c529f7e5bce0", - "complianceLevel": 0 - }, - { - "id": "18w10b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bbb9d8370f9896335c4eaf050223ef30b1f7bf90/18w10b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-03-07T15:56:01+00:00", - "sha1": "bbb9d8370f9896335c4eaf050223ef30b1f7bf90", - "complianceLevel": 0 - }, - { - "id": "18w10a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/41a778663a1c3c1d9979c3ada2d628678efde965/18w10a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-03-06T15:54:24+00:00", - "sha1": "41a778663a1c3c1d9979c3ada2d628678efde965", - "complianceLevel": 0 - }, - { - "id": "18w09a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f862f6d1e92dc1a888b2f76f347be0ffb4977c71/18w09a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-03-01T14:15:10+00:00", - "sha1": "f862f6d1e92dc1a888b2f76f347be0ffb4977c71", - "complianceLevel": 0 - }, - { - "id": "18w08b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b3ac89da14cd7c6fc48f24f0d0dd7180d8a92778/18w08b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-02-22T15:44:49+00:00", - "sha1": "b3ac89da14cd7c6fc48f24f0d0dd7180d8a92778", - "complianceLevel": 0 - }, - { - "id": "18w08a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f07f85f61d53ee26477a662d947686b33a25e200/18w08a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-02-21T14:59:00+00:00", - "sha1": "f07f85f61d53ee26477a662d947686b33a25e200", - "complianceLevel": 0 - }, - { - "id": "18w07c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5fdfa4e973c39faf9d509a4b1e8910139caa21b3/18w07c.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-02-16T13:23:32+00:00", - "sha1": "5fdfa4e973c39faf9d509a4b1e8910139caa21b3", - "complianceLevel": 0 - }, - { - "id": "18w07b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5f4efa813c095fad0806063b1f5e146a4244d45b/18w07b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-02-15T14:28:42+00:00", - "sha1": "5f4efa813c095fad0806063b1f5e146a4244d45b", - "complianceLevel": 0 - }, - { - "id": "18w07a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f1bfa17608ff9d545131b7e5f3cf681364b508fc/18w07a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-02-14T17:34:13+00:00", - "sha1": "f1bfa17608ff9d545131b7e5f3cf681364b508fc", - "complianceLevel": 0 - }, - { - "id": "18w06a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/765f1d42ff3c39800afe39e39b3f43d783e6b609/18w06a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-02-09T12:09:55+00:00", - "sha1": "765f1d42ff3c39800afe39e39b3f43d783e6b609", - "complianceLevel": 0 - }, - { - "id": "18w05a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/910ce9ce7de29324e8500ee56b620b1005c4e87b/18w05a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-01-31T13:32:09+00:00", - "sha1": "910ce9ce7de29324e8500ee56b620b1005c4e87b", - "complianceLevel": 0 - }, - { - "id": "18w03b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/383299163cf01756c7a0ccffb7ed24cc6da5f110/18w03b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-01-17T15:09:14+00:00", - "sha1": "383299163cf01756c7a0ccffb7ed24cc6da5f110", - "complianceLevel": 0 - }, - { - "id": "18w03a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/222afb740bf48beca5603d3b9931379a87016ee5/18w03a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-01-17T14:25:24+00:00", - "sha1": "222afb740bf48beca5603d3b9931379a87016ee5", - "complianceLevel": 0 - }, - { - "id": "18w02a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2e33dc8b0e08eb1e196caf57f150ffb5642b7877/18w02a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-01-10T11:54:55+00:00", - "sha1": "2e33dc8b0e08eb1e196caf57f150ffb5642b7877", - "complianceLevel": 0 - }, - { - "id": "18w01a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/29deae5e4e41c572edc5f7734690b4551d39ed4f/18w01a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2018-01-03T13:29:30+00:00", - "sha1": "29deae5e4e41c572edc5f7734690b4551d39ed4f", - "complianceLevel": 0 - }, - { - "id": "17w50a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fa20448c10de025a71faa8d64c80bd627b544018/17w50a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-12-11T15:28:08+00:00", - "sha1": "fa20448c10de025a71faa8d64c80bd627b544018", - "complianceLevel": 0 - }, - { - "id": "17w49b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e68224a25991e838e2f0b6179f96379caae04602/17w49b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-12-07T15:29:54+00:00", - "sha1": "e68224a25991e838e2f0b6179f96379caae04602", - "complianceLevel": 0 - }, - { - "id": "17w49a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6ba277f076778609c8dda3bdde38ad057d62b80d/17w49a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-12-06T14:24:30+00:00", - "sha1": "6ba277f076778609c8dda3bdde38ad057d62b80d", - "complianceLevel": 0 - }, - { - "id": "17w48a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/45d03978f26c4cf9e0256d9e0555b1dcadb29589/17w48a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-11-27T15:36:33+00:00", - "sha1": "45d03978f26c4cf9e0256d9e0555b1dcadb29589", - "complianceLevel": 0 - }, - { - "id": "17w47b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d0183aa774daeaa677e8b6bfe760a64a1fe49dc9/17w47b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-11-23T15:30:12+00:00", - "sha1": "d0183aa774daeaa677e8b6bfe760a64a1fe49dc9", - "complianceLevel": 0 - }, - { - "id": "17w47a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d6022a91fd35507e3a9e3f35ae8d4e0da4e87afc/17w47a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-11-22T12:40:05+00:00", - "sha1": "d6022a91fd35507e3a9e3f35ae8d4e0da4e87afc", - "complianceLevel": 0 - }, - { - "id": "17w46a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4d9c65d4d9c4bc2252e8249217056048b197796a/17w46a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-11-15T15:21:55+00:00", - "sha1": "4d9c65d4d9c4bc2252e8249217056048b197796a", - "complianceLevel": 0 - }, - { - "id": "17w45b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6fae4c7be13dfef33ac5c789c55cc58e403f5cd8/17w45b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-11-10T10:07:02+00:00", - "sha1": "6fae4c7be13dfef33ac5c789c55cc58e403f5cd8", - "complianceLevel": 0 - }, - { - "id": "17w45a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/77917c87df1b7edeab2bd3d72966d7d842ff5dfa/17w45a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-11-08T15:48:00+00:00", - "sha1": "77917c87df1b7edeab2bd3d72966d7d842ff5dfa", - "complianceLevel": 0 - }, - { - "id": "17w43b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cd37421b9c1118ea6f5cb060bbe225b0e38f2194/17w43b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-10-26T13:36:22+00:00", - "sha1": "cd37421b9c1118ea6f5cb060bbe225b0e38f2194", - "complianceLevel": 0 - }, - { - "id": "17w43a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/09e1fe2758f58543710ebe7444faa53cdfe1629d/17w43a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-10-25T14:43:50+00:00", - "sha1": "09e1fe2758f58543710ebe7444faa53cdfe1629d", - "complianceLevel": 0 - }, - { - "id": "1.12.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/06bd59c127005282cf02cb8894c3c76aefea1f8d/1.12.2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-09-18T08:39:46+00:00", - "sha1": "06bd59c127005282cf02cb8894c3c76aefea1f8d", - "complianceLevel": 0 - }, - { - "id": "1.12.2-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7dfef9f320f25deb226461fda35d1b03c1479c41/1.12.2-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-09-15T08:21:17+00:00", - "sha1": "7dfef9f320f25deb226461fda35d1b03c1479c41", - "complianceLevel": 0 - }, - { - "id": "1.12.2-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/489c890f0a79317c02a844ec145e56b7ee64cd90/1.12.2-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-09-13T13:33:31+00:00", - "sha1": "489c890f0a79317c02a844ec145e56b7ee64cd90", - "complianceLevel": 0 - }, - { - "id": "1.12.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/96696dec1c94a588d76b65f06af258f3ee49d044/1.12.1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-08-03T12:40:39+00:00", - "sha1": "96696dec1c94a588d76b65f06af258f3ee49d044", - "complianceLevel": 0 - }, - { - "id": "1.12.1-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/090a0aa3b67d6d967249ce32137b13ee41261575/1.12.1-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-08-02T10:53:55+00:00", - "sha1": "090a0aa3b67d6d967249ce32137b13ee41261575", - "complianceLevel": 0 - }, - { - "id": "17w31a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e6f9cc1b1105caa653e6d05df13dd3ba8300fe38/17w31a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-08-01T09:41:23+00:00", - "sha1": "e6f9cc1b1105caa653e6d05df13dd3ba8300fe38", - "complianceLevel": 0 - }, - { - "id": "1.12", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/c7a12c19e55afed3764014d8a2944eac351ab091/1.12.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-06-02T13:50:27+00:00", - "sha1": "c7a12c19e55afed3764014d8a2944eac351ab091", - "complianceLevel": 0 - }, - { - "id": "1.12-pre7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6f0af52e0ee56a48369648848141103891f04f5d/1.12-pre7.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-31T10:56:41+00:00", - "sha1": "6f0af52e0ee56a48369648848141103891f04f5d", - "complianceLevel": 0 - }, - { - "id": "1.12-pre6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/46940531864e5737b67637411171357ea5d9527e/1.12-pre6.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-29T11:45:12+00:00", - "sha1": "46940531864e5737b67637411171357ea5d9527e", - "complianceLevel": 0 - }, - { - "id": "1.12-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bbb0a548c8e3e49382e2f6dbc5f3a01866c0799e/1.12-pre5.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-19T07:43:28+00:00", - "sha1": "bbb0a548c8e3e49382e2f6dbc5f3a01866c0799e", - "complianceLevel": 0 - }, - { - "id": "1.12-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/56e9e60d9b323c2033e8df66c8ed602d14e4d4ba/1.12-pre4.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-18T12:28:16+00:00", - "sha1": "56e9e60d9b323c2033e8df66c8ed602d14e4d4ba", - "complianceLevel": 0 - }, - { - "id": "1.12-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ac8788de647cc22ab9d6e7459cad7029c7e61d6f/1.12-pre3.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-17T14:09:18+00:00", - "sha1": "ac8788de647cc22ab9d6e7459cad7029c7e61d6f", - "complianceLevel": 0 - }, - { - "id": "1.12-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c4330b1dd9c7fd54028aa98195a51b5031e818af/1.12-pre2.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-11T12:11:12+00:00", - "sha1": "c4330b1dd9c7fd54028aa98195a51b5031e818af", - "complianceLevel": 0 - }, - { - "id": "1.12-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9f236da41a1fb9d9d21ad6d78268faaf02fb1792/1.12-pre1.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-10T11:37:17+00:00", - "sha1": "9f236da41a1fb9d9d21ad6d78268faaf02fb1792", - "complianceLevel": 0 - }, - { - "id": "17w18b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2162f0d5efe41602b9d1104d4ef9e67d929cffe5/17w18b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-04T13:40:22+00:00", - "sha1": "2162f0d5efe41602b9d1104d4ef9e67d929cffe5", - "complianceLevel": 0 - }, - { - "id": "17w18a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ef26c5de4bd440980e506217708a5e2684b67448/17w18a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-05-03T14:50:23+00:00", - "sha1": "ef26c5de4bd440980e506217708a5e2684b67448", - "complianceLevel": 0 - }, - { - "id": "17w17b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/151d9ad44e4d0dca2023239ce8c8401beea445e2/17w17b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-04-27T13:24:23+00:00", - "sha1": "151d9ad44e4d0dca2023239ce8c8401beea445e2", - "complianceLevel": 0 - }, - { - "id": "17w17a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/887bce9c499d83365911b0d5ec9c4d7881f0e7ad/17w17a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-04-26T13:48:23+00:00", - "sha1": "887bce9c499d83365911b0d5ec9c4d7881f0e7ad", - "complianceLevel": 0 - }, - { - "id": "17w16b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/12a87411646f4245334660553e2e17bb1dcd48ac/17w16b.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-04-21T12:02:59+00:00", - "sha1": "12a87411646f4245334660553e2e17bb1dcd48ac", - "complianceLevel": 0 - }, - { - "id": "17w16a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2d40d7374a953b8a2f44ec9719463bcc17f8fc22/17w16a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-04-20T13:58:35+00:00", - "sha1": "2d40d7374a953b8a2f44ec9719463bcc17f8fc22", - "complianceLevel": 0 - }, - { - "id": "17w15a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7509ece9be5ca3951048077a4d55a6f1c15c1bf6/17w15a.json", - "time": "2021-12-10T02:52:28+00:00", - "releaseTime": "2017-04-12T09:30:50+00:00", - "sha1": "7509ece9be5ca3951048077a4d55a6f1c15c1bf6", - "complianceLevel": 0 - }, - { - "id": "17w14a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ce10db2b058981b6c7cc69ddfba97c63a6a5758d/17w14a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2017-04-05T13:58:01+00:00", - "sha1": "ce10db2b058981b6c7cc69ddfba97c63a6a5758d", - "complianceLevel": 0 - }, - { - "id": "17w13b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1eb898370308e40a006cc5d82599d680e252c6ad/17w13b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2017-03-31T11:06:35+00:00", - "sha1": "1eb898370308e40a006cc5d82599d680e252c6ad", - "complianceLevel": 0 - }, - { - "id": "17w13a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/362f937de1845aab4cbfb4253495b21a8b1ad3d8/17w13a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2017-03-30T09:32:19+00:00", - "sha1": "362f937de1845aab4cbfb4253495b21a8b1ad3d8", - "complianceLevel": 0 - }, - { - "id": "17w06a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/13fe7becc21a0dc89f88dca5a788229db3e51df2/17w06a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2017-02-08T13:16:29+00:00", - "sha1": "13fe7becc21a0dc89f88dca5a788229db3e51df2", - "complianceLevel": 0 - }, - { - "id": "1.11.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/2e91a19cc67af19abcaa8cf9c72ef1439a8073c8/1.11.2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-12-21T09:29:12+00:00", - "sha1": "2e91a19cc67af19abcaa8cf9c72ef1439a8073c8", - "complianceLevel": 0 - }, - { - "id": "1.11.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/37c85eb442747195bd585cd3259aeeafca75f507/1.11.1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-12-20T14:05:34+00:00", - "sha1": "37c85eb442747195bd585cd3259aeeafca75f507", - "complianceLevel": 0 - }, - { - "id": "16w50a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d3ed8113f2c9f9e3871b9c5432e1f0105cb9564c/16w50a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-12-15T14:38:52+00:00", - "sha1": "d3ed8113f2c9f9e3871b9c5432e1f0105cb9564c", - "complianceLevel": 0 - }, - { - "id": "1.11", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/f36787cfa57a3d644f31817b77f5726c5dbcd7a1/1.11.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-11-14T14:34:40+00:00", - "sha1": "f36787cfa57a3d644f31817b77f5726c5dbcd7a1", - "complianceLevel": 0 - }, - { - "id": "1.11-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fa9a5e29092092e7396f15d38155c1c91eaac136/1.11-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-11-08T13:42:50+00:00", - "sha1": "fa9a5e29092092e7396f15d38155c1c91eaac136", - "complianceLevel": 0 - }, - { - "id": "16w44a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/35921f14431bd634a3da689ce17fb9117fedaf0a/16w44a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-11-03T14:17:11+00:00", - "sha1": "35921f14431bd634a3da689ce17fb9117fedaf0a", - "complianceLevel": 0 - }, - { - "id": "16w43a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/23edb7270d50c79ab093bd03b1576e47f66cf017/16w43a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-10-27T09:00:51+00:00", - "sha1": "23edb7270d50c79ab093bd03b1576e47f66cf017", - "complianceLevel": 0 - }, - { - "id": "16w42a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/613903c89f94a7a9b3fef08d51fbcd1a4dfd8f6f/16w42a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-10-19T11:17:47+00:00", - "sha1": "613903c89f94a7a9b3fef08d51fbcd1a4dfd8f6f", - "complianceLevel": 0 - }, - { - "id": "16w41a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ec6e2b5300a0e55e99852cf4c15d25f3d4adf0b7/16w41a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-10-13T14:28:35+00:00", - "sha1": "ec6e2b5300a0e55e99852cf4c15d25f3d4adf0b7", - "complianceLevel": 0 - }, - { - "id": "16w40a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b1368cd69ad319151b92e615689c8ffdf102ba2c/16w40a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-10-06T13:57:59+00:00", - "sha1": "b1368cd69ad319151b92e615689c8ffdf102ba2c", - "complianceLevel": 0 - }, - { - "id": "16w39c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8c98eee9c9a29947410a70456fa9faf00a7e8301/16w39c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-09-30T14:11:48+00:00", - "sha1": "8c98eee9c9a29947410a70456fa9faf00a7e8301", - "complianceLevel": 0 - }, - { - "id": "16w39b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/28f25000fa32cd0013ef0723b7d8baad3b63d303/16w39b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-09-29T14:39:39+00:00", - "sha1": "28f25000fa32cd0013ef0723b7d8baad3b63d303", - "complianceLevel": 0 - }, - { - "id": "16w39a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/66aa47fc2fe8e7073a3af1fad1cf3d5cf0d9a28d/16w39a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-09-28T13:32:06+00:00", - "sha1": "66aa47fc2fe8e7073a3af1fad1cf3d5cf0d9a28d", - "complianceLevel": 0 - }, - { - "id": "16w38a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/612a453382963e4c9db323d6392e6657bcdc0f4d/16w38a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-09-20T12:40:49+00:00", - "sha1": "612a453382963e4c9db323d6392e6657bcdc0f4d", - "complianceLevel": 0 - }, - { - "id": "16w36a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/01b0ca977357ad1179c893fc80d716b654181f0e/16w36a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-09-08T14:55:10+00:00", - "sha1": "01b0ca977357ad1179c893fc80d716b654181f0e", - "complianceLevel": 0 - }, - { - "id": "16w35a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fd5639868bb9f589c9d93debc3accd95a6015a90/16w35a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-09-01T13:13:38+00:00", - "sha1": "fd5639868bb9f589c9d93debc3accd95a6015a90", - "complianceLevel": 0 - }, - { - "id": "16w33a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/296c1e4033c0ba4140c179f52f43dba863d7b27a/16w33a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-08-17T12:48:57+00:00", - "sha1": "296c1e4033c0ba4140c179f52f43dba863d7b27a", - "complianceLevel": 0 - }, - { - "id": "16w32b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ae0858304483dd566c29d97332ba7d0b5b2bc528/16w32b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-08-11T14:34:29+00:00", - "sha1": "ae0858304483dd566c29d97332ba7d0b5b2bc528", - "complianceLevel": 0 - }, - { - "id": "16w32a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bbb9e8407ad98b4b5f29605e14f9290c5589acdc/16w32a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-08-10T12:30:10+00:00", - "sha1": "bbb9e8407ad98b4b5f29605e14f9290c5589acdc", - "complianceLevel": 0 - }, - { - "id": "1.10.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/5c069793ce59869c400cee9be9d0831babd9dfc0/1.10.2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-06-23T09:17:32+00:00", - "sha1": "5c069793ce59869c400cee9be9d0831babd9dfc0", - "complianceLevel": 0 - }, - { - "id": "1.10.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/bbe15594a6942f5a2b649c63d4554a9e84e80330/1.10.1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-06-22T10:13:22+00:00", - "sha1": "bbe15594a6942f5a2b649c63d4554a9e84e80330", - "complianceLevel": 0 - }, - { - "id": "1.10", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/a80b1fa6fb3d505528ee874a7c82070374904918/1.10.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-06-08T13:06:18+00:00", - "sha1": "a80b1fa6fb3d505528ee874a7c82070374904918", - "complianceLevel": 0 - }, - { - "id": "1.10-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/73572f7c3206eaa8e7a3e1e226d4543035e62154/1.10-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-06-07T14:56:34+00:00", - "sha1": "73572f7c3206eaa8e7a3e1e226d4543035e62154", - "complianceLevel": 0 - }, - { - "id": "1.10-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/89de4986c0c3501824c7893d692944a4051e1444/1.10-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-06-02T14:45:16+00:00", - "sha1": "89de4986c0c3501824c7893d692944a4051e1444", - "complianceLevel": 0 - }, - { - "id": "16w21b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9c01cd6514c70637561093658ed733789fa998c2/16w21b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-05-26T12:47:22+00:00", - "sha1": "9c01cd6514c70637561093658ed733789fa998c2", - "complianceLevel": 0 - }, - { - "id": "16w21a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/dab2c5d591b8a59f1fc63a9814f17cb9075c187d/16w21a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-05-25T13:12:09+00:00", - "sha1": "dab2c5d591b8a59f1fc63a9814f17cb9075c187d", - "complianceLevel": 0 - }, - { - "id": "16w20a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c92953bfe7ffaa782f32309a7daef3e131d37f64/16w20a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-05-18T12:45:14+00:00", - "sha1": "c92953bfe7ffaa782f32309a7daef3e131d37f64", - "complianceLevel": 0 - }, - { - "id": "1.9.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/779a2060f7601510c6d4b4f920b063f2aee1df84/1.9.4.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-05-10T10:17:16+00:00", - "sha1": "779a2060f7601510c6d4b4f920b063f2aee1df84", - "complianceLevel": 0 - }, - { - "id": "1.9.3", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/27a81d44fd3d589c488cbb0793ff12f586798b56/1.9.3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-05-10T08:33:35+00:00", - "sha1": "27a81d44fd3d589c488cbb0793ff12f586798b56", - "complianceLevel": 0 - }, - { - "id": "1.9.3-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/3c8fef41f0b35f2e67be884e1d0dec67f61475c6/1.9.3-pre3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-05-03T09:28:11+00:00", - "sha1": "3c8fef41f0b35f2e67be884e1d0dec67f61475c6", - "complianceLevel": 0 - }, - { - "id": "1.9.3-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/57ae4bc880f032aab0020a67268f2ba59dd3ae93/1.9.3-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-04-27T13:33:20+00:00", - "sha1": "57ae4bc880f032aab0020a67268f2ba59dd3ae93", - "complianceLevel": 0 - }, - { - "id": "1.9.3-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/467f38c87bdf02063fbce6a589aef1ac53b6241c/1.9.3-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-04-21T12:41:42+00:00", - "sha1": "467f38c87bdf02063fbce6a589aef1ac53b6241c", - "complianceLevel": 0 - }, - { - "id": "16w15b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c730dd078d0408880485e3a75ff7077538a823d7/16w15b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-04-13T13:56:41+00:00", - "sha1": "c730dd078d0408880485e3a75ff7077538a823d7", - "complianceLevel": 0 - }, - { - "id": "16w15a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f3e8b17530d8d54ab4fbc3c90c29c6c52fd315de/16w15a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-04-11T14:38:28+00:00", - "sha1": "f3e8b17530d8d54ab4fbc3c90c29c6c52fd315de", - "complianceLevel": 0 - }, - { - "id": "16w14a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4117f6b598b99c9de3e59bc468c72fb96d81025e/16w14a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-04-07T12:47:51+00:00", - "sha1": "4117f6b598b99c9de3e59bc468c72fb96d81025e", - "complianceLevel": 0 - }, - { - "id": "1.RV-Pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a31f94898730f706ca1b0f4b6d2a58d66aedc38e/1.RV-Pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-03-31T16:18:53+00:00", - "sha1": "a31f94898730f706ca1b0f4b6d2a58d66aedc38e", - "complianceLevel": 0 - }, - { - "id": "1.9.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/c2b550445e04b325fcabe03b9d363dbb4b16b63c/1.9.2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-03-30T15:23:55+00:00", - "sha1": "c2b550445e04b325fcabe03b9d363dbb4b16b63c", - "complianceLevel": 0 - }, - { - "id": "1.9.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/ad467e82f12e0fdd1b00689589a08de31b942ae9/1.9.1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-03-30T13:43:07+00:00", - "sha1": "ad467e82f12e0fdd1b00689589a08de31b942ae9", - "complianceLevel": 0 - }, - { - "id": "1.9.1-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e00783c5b1336d9d8d95df4f700e2d4a86f617a5/1.9.1-pre3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-03-11T09:20:36+00:00", - "sha1": "e00783c5b1336d9d8d95df4f700e2d4a86f617a5", - "complianceLevel": 0 - }, - { - "id": "1.9.1-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b5c428bbadf6bfcc1c8dedab6d1750ceaefab33e/1.9.1-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-03-10T15:06:03+00:00", - "sha1": "b5c428bbadf6bfcc1c8dedab6d1750ceaefab33e", - "complianceLevel": 0 - }, - { - "id": "1.9.1-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2a26d22170dcc3137db71c264b5ced3557727846/1.9.1-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-03-09T16:27:29+00:00", - "sha1": "2a26d22170dcc3137db71c264b5ced3557727846", - "complianceLevel": 0 - }, - { - "id": "1.9", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/2722a6c87a7f369e26e432e85ddf159fcb75dacf/1.9.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-29T13:49:54+00:00", - "sha1": "2722a6c87a7f369e26e432e85ddf159fcb75dacf", - "complianceLevel": 0 - }, - { - "id": "1.9-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/31e4d124e9834d70d2b0cfbd65977b80e9feef29/1.9-pre4.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-26T15:21:11+00:00", - "sha1": "31e4d124e9834d70d2b0cfbd65977b80e9feef29", - "complianceLevel": 0 - }, - { - "id": "1.9-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/83140fe2ff7cb15a7e480af949d264534d65620f/1.9-pre3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-24T15:52:36+00:00", - "sha1": "83140fe2ff7cb15a7e480af949d264534d65620f", - "complianceLevel": 0 - }, - { - "id": "1.9-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0c61c1ede0b9d2e72e83fa2f6e3ec3f305cfddd9/1.9-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-18T17:41:00+00:00", - "sha1": "0c61c1ede0b9d2e72e83fa2f6e3ec3f305cfddd9", - "complianceLevel": 0 - }, - { - "id": "1.9-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d957338a5000d8256b32372d60714aa7835a293b/1.9-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-17T15:23:19+00:00", - "sha1": "d957338a5000d8256b32372d60714aa7835a293b", - "complianceLevel": 0 - }, - { - "id": "16w07b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d88c47330f5fb1f1fc575fc11debaab69ef7a2be/16w07b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-16T15:22:39+00:00", - "sha1": "d88c47330f5fb1f1fc575fc11debaab69ef7a2be", - "complianceLevel": 0 - }, - { - "id": "16w07a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/88f9139c2ff8ff20e143008be69c774726101d1f/16w07a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-15T15:48:46+00:00", - "sha1": "88f9139c2ff8ff20e143008be69c774726101d1f", - "complianceLevel": 0 - }, - { - "id": "16w06a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d570691e9e87541213171926d9b0b7bebec5b985/16w06a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-10T15:06:41+00:00", - "sha1": "d570691e9e87541213171926d9b0b7bebec5b985", - "complianceLevel": 0 - }, - { - "id": "16w05b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a9ac788fa14d0a98a4cdc95595832755e43da1f8/16w05b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-04T15:28:02+00:00", - "sha1": "a9ac788fa14d0a98a4cdc95595832755e43da1f8", - "complianceLevel": 0 - }, - { - "id": "16w05a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d03e51366b34b6a08336f727becd065002fa9ddd/16w05a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-02-03T15:48:38+00:00", - "sha1": "d03e51366b34b6a08336f727becd065002fa9ddd", - "complianceLevel": 0 - }, - { - "id": "16w04a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e6443ca82d6be999557d01bea7775d6f1f2494c0/16w04a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-01-28T15:37:24+00:00", - "sha1": "e6443ca82d6be999557d01bea7775d6f1f2494c0", - "complianceLevel": 0 - }, - { - "id": "16w03a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/955d7d06c800352dd8da03fcd2ae08a7c12d2002/16w03a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-01-20T14:29:24+00:00", - "sha1": "955d7d06c800352dd8da03fcd2ae08a7c12d2002", - "complianceLevel": 0 - }, - { - "id": "16w02a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9f0f356bc9f78823115642e7851ebb8a4d7baf39/16w02a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2016-01-13T15:15:16+00:00", - "sha1": "9f0f356bc9f78823115642e7851ebb8a4d7baf39", - "complianceLevel": 0 - }, - { - "id": "15w51b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/abbc1b8a8a957890c9cbe55699b7ab6dc22a6e73/15w51b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-12-17T15:30:41+00:00", - "sha1": "abbc1b8a8a957890c9cbe55699b7ab6dc22a6e73", - "complianceLevel": 0 - }, - { - "id": "15w51a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c8fa9d6f867f6f977060e757577b3d958bf9b52f/15w51a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-12-17T14:02:37+00:00", - "sha1": "c8fa9d6f867f6f977060e757577b3d958bf9b52f", - "complianceLevel": 0 - }, - { - "id": "15w50a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a150fade7039dc92038e86002ea9eacfae8667fd/15w50a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-12-09T15:35:57+00:00", - "sha1": "a150fade7039dc92038e86002ea9eacfae8667fd", - "complianceLevel": 0 - }, - { - "id": "15w49b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7b0c0a65c997e82f52bcf46c340972250f935560/15w49b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-12-03T15:23:22+00:00", - "sha1": "7b0c0a65c997e82f52bcf46c340972250f935560", - "complianceLevel": 0 - }, - { - "id": "1.8.9", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/fc9dafafbc3a0d2eab921aa0ba5f5de13cf4b353/1.8.9.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-12-03T09:24:39+00:00", - "sha1": "fc9dafafbc3a0d2eab921aa0ba5f5de13cf4b353", - "complianceLevel": 0 - }, - { - "id": "15w49a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/271775d694f3acd2183e4e9fb24c2e84065773d6/15w49a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-12-02T15:09:37+00:00", - "sha1": "271775d694f3acd2183e4e9fb24c2e84065773d6", - "complianceLevel": 0 - }, - { - "id": "15w47c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bb30adee8f5fd55526c216d6d919ca63e4ffadc4/15w47c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-11-20T12:46:56+00:00", - "sha1": "bb30adee8f5fd55526c216d6d919ca63e4ffadc4", - "complianceLevel": 0 - }, - { - "id": "15w47b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/81aa44ffdc14051896f5e2144acf89de175c78f4/15w47b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-11-19T14:48:03+00:00", - "sha1": "81aa44ffdc14051896f5e2144acf89de175c78f4", - "complianceLevel": 0 - }, - { - "id": "15w47a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f23aed832f150b1db1d43bdbb7f0680c37c0fe24/15w47a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-11-18T15:53:41+00:00", - "sha1": "f23aed832f150b1db1d43bdbb7f0680c37c0fe24", - "complianceLevel": 0 - }, - { - "id": "15w46a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f83d1ad81fa19ac10c15de85a42df7c41e0ffd0d/15w46a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-11-12T12:11:47+00:00", - "sha1": "f83d1ad81fa19ac10c15de85a42df7c41e0ffd0d", - "complianceLevel": 0 - }, - { - "id": "15w45a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8bffbae480b5640007aaa8bf6e954d95c6346d04/15w45a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-11-05T13:04:07+00:00", - "sha1": "8bffbae480b5640007aaa8bf6e954d95c6346d04", - "complianceLevel": 0 - }, - { - "id": "15w44b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c97b52f91522ba413993a00b1d0e5d9987e488ba/15w44b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-30T11:23:17+00:00", - "sha1": "c97b52f91522ba413993a00b1d0e5d9987e488ba", - "complianceLevel": 0 - }, - { - "id": "15w44a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/080312449775e7037a4bc8a2f1c9b440395383fa/15w44a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-28T15:09:36+00:00", - "sha1": "080312449775e7037a4bc8a2f1c9b440395383fa", - "complianceLevel": 0 - }, - { - "id": "15w43c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/040acb343865ed4dd5a0c26045fe61f623ac7c31/15w43c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-23T15:35:55+00:00", - "sha1": "040acb343865ed4dd5a0c26045fe61f623ac7c31", - "complianceLevel": 0 - }, - { - "id": "15w43b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d7c8138c279954799fefa2e6dfb2fcb044407e0c/15w43b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-22T14:11:58+00:00", - "sha1": "d7c8138c279954799fefa2e6dfb2fcb044407e0c", - "complianceLevel": 0 - }, - { - "id": "15w43a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/23413606da234f5b12aa2462b185539e7817fcb4/15w43a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-21T15:28:52+00:00", - "sha1": "23413606da234f5b12aa2462b185539e7817fcb4", - "complianceLevel": 0 - }, - { - "id": "15w42a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/550b54161a6c3e9216b3a2e44aeabd6d831aca26/15w42a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-14T13:25:14+00:00", - "sha1": "550b54161a6c3e9216b3a2e44aeabd6d831aca26", - "complianceLevel": 0 - }, - { - "id": "15w41b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/020d9450acefa9274165e451e3967affa64ef7b3/15w41b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-07T14:07:26+00:00", - "sha1": "020d9450acefa9274165e451e3967affa64ef7b3", - "complianceLevel": 0 - }, - { - "id": "15w41a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a95b36e51d0777baa66980def5f7bf5abb39b281/15w41a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-10-07T13:19:53+00:00", - "sha1": "a95b36e51d0777baa66980def5f7bf5abb39b281", - "complianceLevel": 0 - }, - { - "id": "15w40b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a632e3c148c2e0891367ac42df10c2641ac06163/15w40b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-30T14:13:54+00:00", - "sha1": "a632e3c148c2e0891367ac42df10c2641ac06163", - "complianceLevel": 0 - }, - { - "id": "15w40a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8c26e7980478ce24d5460d6c187bb6fb9835d079/15w40a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-30T13:13:54+00:00", - "sha1": "8c26e7980478ce24d5460d6c187bb6fb9835d079", - "complianceLevel": 0 - }, - { - "id": "15w39c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ada5057569948d21fd6f1aab5177be9c9fb3c9d6/15w39c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-23T13:13:54+00:00", - "sha1": "ada5057569948d21fd6f1aab5177be9c9fb3c9d6", - "complianceLevel": 0 - }, - { - "id": "15w39b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/169db102ace620f453fe7d65619fe800e44ace95/15w39b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-21T15:09:52+00:00", - "sha1": "169db102ace620f453fe7d65619fe800e44ace95", - "complianceLevel": 0 - }, - { - "id": "15w39a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d11f5cfd415e1f76c571994ec26a4d5fd6c196fd/15w39a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-21T13:16:32+00:00", - "sha1": "d11f5cfd415e1f76c571994ec26a4d5fd6c196fd", - "complianceLevel": 0 - }, - { - "id": "15w38b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/50ed421ce2f888d4b9679a1ca74a881777ab92ea/15w38b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-17T14:22:31+00:00", - "sha1": "50ed421ce2f888d4b9679a1ca74a881777ab92ea", - "complianceLevel": 0 - }, - { - "id": "15w38a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/507f00f4dbdb106aaa984d7d487c69ebf872a61e/15w38a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-16T14:22:31+00:00", - "sha1": "507f00f4dbdb106aaa984d7d487c69ebf872a61e", - "complianceLevel": 0 - }, - { - "id": "15w37a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/831264446c3d301e1d62517e82c3365227806413/15w37a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-10T14:22:31+00:00", - "sha1": "831264446c3d301e1d62517e82c3365227806413", - "complianceLevel": 0 - }, - { - "id": "15w36d", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/70cb969a189d1a5f930ef53bd7034ae4652fa468/15w36d.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-04T14:22:31+00:00", - "sha1": "70cb969a189d1a5f930ef53bd7034ae4652fa468", - "complianceLevel": 0 - }, - { - "id": "15w36c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/654c15f25a7e67a4140d0298caf600d16015be5f/15w36c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-02T16:07:22+00:00", - "sha1": "654c15f25a7e67a4140d0298caf600d16015be5f", - "complianceLevel": 0 - }, - { - "id": "15w36b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/86ee90c6e18f617412b3930ec3b6483e2ed6392a/15w36b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-02T15:36:25+00:00", - "sha1": "86ee90c6e18f617412b3930ec3b6483e2ed6392a", - "complianceLevel": 0 - }, - { - "id": "15w36a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f99d50c862cdc97088ff1601caa97872a844d994/15w36a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-09-02T14:46:40+00:00", - "sha1": "f99d50c862cdc97088ff1601caa97872a844d994", - "complianceLevel": 0 - }, - { - "id": "15w35e", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/05f9d7da977d0167e12a6425d84af0c4b111f09c/15w35e.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-28T18:14:02+00:00", - "sha1": "05f9d7da977d0167e12a6425d84af0c4b111f09c", - "complianceLevel": 0 - }, - { - "id": "15w35d", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4a00fdad2bc6ac20985b24e3864309955279b98b/15w35d.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-28T16:25:35+00:00", - "sha1": "4a00fdad2bc6ac20985b24e3864309955279b98b", - "complianceLevel": 0 - }, - { - "id": "15w35c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d52c32f733384d1ec0a4fd500b1302bde1a17c87/15w35c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-28T11:21:00+00:00", - "sha1": "d52c32f733384d1ec0a4fd500b1302bde1a17c87", - "complianceLevel": 0 - }, - { - "id": "15w35b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/901a71d9a3d3b8ffcda89169cd1207a840cd1b11/15w35b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-24T15:39:18+00:00", - "sha1": "901a71d9a3d3b8ffcda89169cd1207a840cd1b11", - "complianceLevel": 0 - }, - { - "id": "15w35a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a289341b7a0f58dfe4be83c84b6763a716302eb2/15w35a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-24T14:19:31+00:00", - "sha1": "a289341b7a0f58dfe4be83c84b6763a716302eb2", - "complianceLevel": 0 - }, - { - "id": "15w34d", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/30b0c5fead8b34d19ab8a46dd1571fd2d9025552/15w34d.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-21T15:27:55+00:00", - "sha1": "30b0c5fead8b34d19ab8a46dd1571fd2d9025552", - "complianceLevel": 0 - }, - { - "id": "15w34c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/daf43530b81021f66cc19c3fa7e7f5eeff12d341/15w34c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-21T12:45:20+00:00", - "sha1": "daf43530b81021f66cc19c3fa7e7f5eeff12d341", - "complianceLevel": 0 - }, - { - "id": "15w34b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bb3b4749e1af1b64fc45a5e85d4c7419ab6a7053/15w34b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-20T14:00:03+00:00", - "sha1": "bb3b4749e1af1b64fc45a5e85d4c7419ab6a7053", - "complianceLevel": 0 - }, - { - "id": "15w34a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9eae09fdc6d56306c4c249ae186136d11984ffbf/15w34a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-19T12:56:01+00:00", - "sha1": "9eae09fdc6d56306c4c249ae186136d11984ffbf", - "complianceLevel": 0 - }, - { - "id": "15w33c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/73c46f454697c82bece1819dbab06a56db56e80d/15w33c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-14T13:10:46+00:00", - "sha1": "73c46f454697c82bece1819dbab06a56db56e80d", - "complianceLevel": 0 - }, - { - "id": "15w33b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/028c9db8ecf9495ba36bf6cf5040a66022e508b8/15w33b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-12T15:29:11+00:00", - "sha1": "028c9db8ecf9495ba36bf6cf5040a66022e508b8", - "complianceLevel": 0 - }, - { - "id": "15w33a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/12db610aa2ab7af8360fc812147c8326f6b580ed/15w33a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-12T14:05:07+00:00", - "sha1": "12db610aa2ab7af8360fc812147c8326f6b580ed", - "complianceLevel": 0 - }, - { - "id": "15w32c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8d1604588dc3a4b100e79adfde9acced18791afa/15w32c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-07T14:08:17+00:00", - "sha1": "8d1604588dc3a4b100e79adfde9acced18791afa", - "complianceLevel": 0 - }, - { - "id": "15w32b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e65e2da8b10dc375e57051116b4a33c906f94b86/15w32b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-06T13:51:47+00:00", - "sha1": "e65e2da8b10dc375e57051116b4a33c906f94b86", - "complianceLevel": 0 - }, - { - "id": "15w32a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d4b45a6114009ac8550de1a6cfd97d91b8b0e0d3/15w32a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-08-05T12:22:42+00:00", - "sha1": "d4b45a6114009ac8550de1a6cfd97d91b8b0e0d3", - "complianceLevel": 0 - }, - { - "id": "15w31c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9a6bbc272f529a8b293a28231845daf438bc2318/15w31c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-07-31T13:45:08+00:00", - "sha1": "9a6bbc272f529a8b293a28231845daf438bc2318", - "complianceLevel": 0 - }, - { - "id": "15w31b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2709e40a1d0db0f28fbc9b2c4e2dc5aa07435eb0/15w31b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-07-30T13:38:32+00:00", - "sha1": "2709e40a1d0db0f28fbc9b2c4e2dc5aa07435eb0", - "complianceLevel": 0 - }, - { - "id": "15w31a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/dcfbeae6d71d0a94ce92a174deca99615fba0851/15w31a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-07-29T13:24:33+00:00", - "sha1": "dcfbeae6d71d0a94ce92a174deca99615fba0851", - "complianceLevel": 0 - }, - { - "id": "1.8.8", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/f88609a2c0c7770c6bbf09e0cc829464b0830297/1.8.8.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-07-27T10:31:28+00:00", - "sha1": "f88609a2c0c7770c6bbf09e0cc829464b0830297", - "complianceLevel": 0 - }, - { - "id": "1.8.7", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/c7592ab67b0f7350b8071c8ae5721293c6537615/1.8.7.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-06-05T10:10:44+00:00", - "sha1": "c7592ab67b0f7350b8071c8ae5721293c6537615", - "complianceLevel": 0 - }, - { - "id": "1.8.6", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/862485a9646dae94ed25a713af93add8efb289a8/1.8.6.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-05-25T10:31:19+00:00", - "sha1": "862485a9646dae94ed25a713af93add8efb289a8", - "complianceLevel": 0 - }, - { - "id": "1.8.5", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/8df7157f2b901f6199b2923fad7e7647845580ea/1.8.5.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-05-22T11:15:28+00:00", - "sha1": "8df7157f2b901f6199b2923fad7e7647845580ea", - "complianceLevel": 0 - }, - { - "id": "1.8.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/b4d395f4df8b35ff46e082db713a9ae56b47c3ff/1.8.4.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-04-17T11:37:50+00:00", - "sha1": "b4d395f4df8b35ff46e082db713a9ae56b47c3ff", - "complianceLevel": 0 - }, - { - "id": "15w14a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6208081ddf1639ff7648802f70149e854695a94a/15w14a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-04-01T07:08:00+00:00", - "sha1": "6208081ddf1639ff7648802f70149e854695a94a", - "complianceLevel": 0 - }, - { - "id": "1.8.3", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/4fcc0efac9dd10288a51e158d23eeba23522c6a0/1.8.3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-02-20T14:00:09+00:00", - "sha1": "4fcc0efac9dd10288a51e158d23eeba23522c6a0", - "complianceLevel": 0 - }, - { - "id": "1.8.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/2c3d77cf1bec4c774fd4be10aa18d304ca6075da/1.8.2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-02-19T15:47:29+00:00", - "sha1": "2c3d77cf1bec4c774fd4be10aa18d304ca6075da", - "complianceLevel": 0 - }, - { - "id": "1.8.2-pre7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ba540cfc389da3943a55739d0f68d4723d69a137/1.8.2-pre7.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-02-16T13:01:35+00:00", - "sha1": "ba540cfc389da3943a55739d0f68d4723d69a137", - "complianceLevel": 0 - }, - { - "id": "1.8.2-pre6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cb4dfbd1086d062fbc52b5a14955ff88f7383ffc/1.8.2-pre6.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-01-30T11:58:24+00:00", - "sha1": "cb4dfbd1086d062fbc52b5a14955ff88f7383ffc", - "complianceLevel": 0 - }, - { - "id": "1.8.2-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a253ffa000f6e89bfaee1bad8a231b2d38e91d50/1.8.2-pre5.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-01-26T15:03:24+00:00", - "sha1": "a253ffa000f6e89bfaee1bad8a231b2d38e91d50", - "complianceLevel": 0 - }, - { - "id": "1.8.2-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/383ccb01179268e1ce2614f6cd4d44174f66b97f/1.8.2-pre4.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-01-16T14:19:59+00:00", - "sha1": "383ccb01179268e1ce2614f6cd4d44174f66b97f", - "complianceLevel": 0 - }, - { - "id": "1.8.2-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e20fb920f0ec55503f467271a43e8cacaed6f1b9/1.8.2-pre3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-01-15T16:44:33+00:00", - "sha1": "e20fb920f0ec55503f467271a43e8cacaed6f1b9", - "complianceLevel": 0 - }, - { - "id": "1.8.2-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d7c3bfa36a04e1b85ecadecd64d51aa3f6c0d86d/1.8.2-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2015-01-15T15:07:31+00:00", - "sha1": "d7c3bfa36a04e1b85ecadecd64d51aa3f6c0d86d", - "complianceLevel": 0 - }, - { - "id": "1.8.2-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bb3cc87a6308d99357e5f8160ac822fe498dbc9e/1.8.2-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-12-18T11:29:41+00:00", - "sha1": "bb3cc87a6308d99357e5f8160ac822fe498dbc9e", - "complianceLevel": 0 - }, - { - "id": "1.8.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/703ebfbc95b7b85abf5968fdd92ec83df468e976/1.8.1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-11-24T14:13:31+00:00", - "sha1": "703ebfbc95b7b85abf5968fdd92ec83df468e976", - "complianceLevel": 0 - }, - { - "id": "1.8.1-pre5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c916ef606f1271764ff077bcedd0350f4b2c8f89/1.8.1-pre5.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-11-19T14:30:48+00:00", - "sha1": "c916ef606f1271764ff077bcedd0350f4b2c8f89", - "complianceLevel": 0 - }, - { - "id": "1.8.1-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/11105d999abd55b0deec3394fd62ccffc2efc5c7/1.8.1-pre4.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-11-06T14:10:50+00:00", - "sha1": "11105d999abd55b0deec3394fd62ccffc2efc5c7", - "complianceLevel": 0 - }, - { - "id": "1.8.1-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bfd6e5d51073bc23aaab56a1147ea3e6635b3c0d/1.8.1-pre3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-10-23T12:59:42+00:00", - "sha1": "bfd6e5d51073bc23aaab56a1147ea3e6635b3c0d", - "complianceLevel": 0 - }, - { - "id": "1.8.1-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/61dc172eb7d5985ebc2dc70f7731029922e5671a/1.8.1-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-10-16T14:19:27+00:00", - "sha1": "61dc172eb7d5985ebc2dc70f7731029922e5671a", - "complianceLevel": 0 - }, - { - "id": "1.8.1-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/459b6243d0252f662ac06b7553f0cf24f7db8f91/1.8.1-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-10-15T13:25:11+00:00", - "sha1": "459b6243d0252f662ac06b7553f0cf24f7db8f91", - "complianceLevel": 0 - }, - { - "id": "1.8", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/1136e2f8986d487b9584f229a8705a8d4bb75c3a/1.8.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-09-02T08:24:35+00:00", - "sha1": "1136e2f8986d487b9584f229a8705a8d4bb75c3a", - "complianceLevel": 0 - }, - { - "id": "1.8-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8836cea9b75834782a8af8b4ed4843d5668189db/1.8-pre3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-28T09:40:54+00:00", - "sha1": "8836cea9b75834782a8af8b4ed4843d5668189db", - "complianceLevel": 0 - }, - { - "id": "1.8-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/171c64a92cd998d9a9eb4a8cfecb89b18d617d6f/1.8-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-25T14:52:18+00:00", - "sha1": "171c64a92cd998d9a9eb4a8cfecb89b18d617d6f", - "complianceLevel": 0 - }, - { - "id": "1.8-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b11f29099de7c38c2733584a8f9929967c06f2bb/1.8-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-21T13:56:26+00:00", - "sha1": "b11f29099de7c38c2733584a8f9929967c06f2bb", - "complianceLevel": 0 - }, - { - "id": "14w34d", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a58af7a958e4daf4f941156fab5d576675b6edf4/14w34d.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-20T12:46:59+00:00", - "sha1": "a58af7a958e4daf4f941156fab5d576675b6edf4", - "complianceLevel": 0 - }, - { - "id": "14w34c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/636b526ca9dc64f0d0376803ad2f9ce027959225/14w34c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-19T15:31:24+00:00", - "sha1": "636b526ca9dc64f0d0376803ad2f9ce027959225", - "complianceLevel": 0 - }, - { - "id": "14w34b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c42ba54ca5f15b47007a24668c1adeaa5d6a48b4/14w34b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-18T15:14:28+00:00", - "sha1": "c42ba54ca5f15b47007a24668c1adeaa5d6a48b4", - "complianceLevel": 0 - }, - { - "id": "14w34a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c07f827acb32b88e90323e8fd1aca9c0410386bb/14w34a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-18T14:14:11+00:00", - "sha1": "c07f827acb32b88e90323e8fd1aca9c0410386bb", - "complianceLevel": 0 - }, - { - "id": "14w33c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8fcb009c6b7e080b41a8eab972fb90cc617c8d53/14w33c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-15T18:00:26+00:00", - "sha1": "8fcb009c6b7e080b41a8eab972fb90cc617c8d53", - "complianceLevel": 0 - }, - { - "id": "14w33b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/abdc0f069f104657efef3ec9cafd9c6b5f7cca2d/14w33b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-15T16:23:51+00:00", - "sha1": "abdc0f069f104657efef3ec9cafd9c6b5f7cca2d", - "complianceLevel": 0 - }, - { - "id": "14w33a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/340a500b4c8399378520eeb063cea8d0fefb5f4f/14w33a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-13T15:08:14+00:00", - "sha1": "340a500b4c8399378520eeb063cea8d0fefb5f4f", - "complianceLevel": 0 - }, - { - "id": "14w32d", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e4c795cb522c6c1f42fb90be9d78985835390253/14w32d.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-08T15:13:41+00:00", - "sha1": "e4c795cb522c6c1f42fb90be9d78985835390253", - "complianceLevel": 0 - }, - { - "id": "14w32c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0801d009ca4ca301b3a6cc57940a425edbc0d0a4/14w32c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-08T14:11:20+00:00", - "sha1": "0801d009ca4ca301b3a6cc57940a425edbc0d0a4", - "complianceLevel": 0 - }, - { - "id": "14w32b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/09bf53ea1e0d3658aeb62c7cdff38ffe7b5849c9/14w32b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-07T14:45:17+00:00", - "sha1": "09bf53ea1e0d3658aeb62c7cdff38ffe7b5849c9", - "complianceLevel": 0 - }, - { - "id": "14w32a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/47b01f58a2d725661ce82a5ff9c9cc2c93ea3d47/14w32a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-08-06T14:01:16+00:00", - "sha1": "47b01f58a2d725661ce82a5ff9c9cc2c93ea3d47", - "complianceLevel": 0 - }, - { - "id": "14w31a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/304414699beb66ed54f56fe0c7e353237612c769/14w31a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-30T15:38:05+00:00", - "sha1": "304414699beb66ed54f56fe0c7e353237612c769", - "complianceLevel": 0 - }, - { - "id": "14w30c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/21dd7d4b209d95f0687300f6fc1fb39cd285ba3f/14w30c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-24T14:39:09+00:00", - "sha1": "21dd7d4b209d95f0687300f6fc1fb39cd285ba3f", - "complianceLevel": 0 - }, - { - "id": "14w30b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e441fe300f0360ea7e4f18146cd9787b3c3650be/14w30b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-23T15:03:03+00:00", - "sha1": "e441fe300f0360ea7e4f18146cd9787b3c3650be", - "complianceLevel": 0 - }, - { - "id": "14w30a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d562497014cb7c4247608365880c783764420d8d/14w30a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-23T13:15:42+00:00", - "sha1": "d562497014cb7c4247608365880c783764420d8d", - "complianceLevel": 0 - }, - { - "id": "14w29b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d93e0fa1cd44ed1718f6cb896f24fa37dae6c386/14w29b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-16T17:27:40+00:00", - "sha1": "d93e0fa1cd44ed1718f6cb896f24fa37dae6c386", - "complianceLevel": 0 - }, - { - "id": "14w29a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fdb0e5f34d4577a18e9e0348f5f68a01390a965c/14w29a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-16T15:18:17+00:00", - "sha1": "fdb0e5f34d4577a18e9e0348f5f68a01390a965c", - "complianceLevel": 0 - }, - { - "id": "14w28b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/93b4c60b4096cd2e3da7f8ce9190150b21250f31/14w28b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-10T14:28:48+00:00", - "sha1": "93b4c60b4096cd2e3da7f8ce9190150b21250f31", - "complianceLevel": 0 - }, - { - "id": "14w28a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6249d2412102eb631f43a0f9577c46c940e69fc2/14w28a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-07-09T15:42:36+00:00", - "sha1": "6249d2412102eb631f43a0f9577c46c940e69fc2", - "complianceLevel": 0 - }, - { - "id": "14w27b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/60f220693f30a7c92b53631e6f3a48e2d5e3ce13/14w27b.json", - "time": "2021-07-07T13:10:12+00:00", - "releaseTime": "2014-07-02T18:34:56+00:00", - "sha1": "60f220693f30a7c92b53631e6f3a48e2d5e3ce13", - "complianceLevel": 0 - }, - { - "id": "14w27a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d4ed459f98feab3d0e787235bebe0aa70c27eda3/14w27a.json", - "time": "2021-07-07T13:10:12+00:00", - "releaseTime": "2014-07-02T16:07:20+00:00", - "sha1": "d4ed459f98feab3d0e787235bebe0aa70c27eda3", - "complianceLevel": 0 - }, - { - "id": "14w26c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4c9f6bae7109c6614ea418deb95703d4c06c7682/14w26c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-06-26T15:05:03+00:00", - "sha1": "4c9f6bae7109c6614ea418deb95703d4c06c7682", - "complianceLevel": 0 - }, - { - "id": "14w26b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c3c9c557e76dcfccbee92fb21d3d06a186dcff30/14w26b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-06-25T15:08:39+00:00", - "sha1": "c3c9c557e76dcfccbee92fb21d3d06a186dcff30", - "complianceLevel": 0 - }, - { - "id": "14w26a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f6902d7dece278ad77f7e5c8b95571a3e181cefd/14w26a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-06-25T13:59:27+00:00", - "sha1": "f6902d7dece278ad77f7e5c8b95571a3e181cefd", - "complianceLevel": 0 - }, - { - "id": "14w25b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/71622db0275444d20d7c4d0902729c3cd9117879/14w25b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-06-19T12:29:48+00:00", - "sha1": "71622db0275444d20d7c4d0902729c3cd9117879", - "complianceLevel": 0 - }, - { - "id": "14w25a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/1091d1a8d7538a3dcfe0291ee65f3465e92ce25a/14w25a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-06-18T15:52:28+00:00", - "sha1": "1091d1a8d7538a3dcfe0291ee65f3465e92ce25a", - "complianceLevel": 0 - }, - { - "id": "14w21b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d2fe96006449741d4366f1c2d63ba4f3ed260069/14w21b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-22T15:17:55+00:00", - "sha1": "d2fe96006449741d4366f1c2d63ba4f3ed260069", - "complianceLevel": 0 - }, - { - "id": "14w21a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/de841b2efcb29e494b3dd70405f5b1cab5882ee4/14w21a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-22T14:44:33+00:00", - "sha1": "de841b2efcb29e494b3dd70405f5b1cab5882ee4", - "complianceLevel": 0 - }, - { - "id": "14w20b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a0e4f81b3a5c3e8cacc0759824ab99ffeafb6bbb/14w20b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-15T16:47:21+00:00", - "sha1": "a0e4f81b3a5c3e8cacc0759824ab99ffeafb6bbb", - "complianceLevel": 0 - }, - { - "id": "14w20a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ef86e8fb75c0fffd057330382fcd32ff76b602b2/14w20a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-15T14:01:20+00:00", - "sha1": "ef86e8fb75c0fffd057330382fcd32ff76b602b2", - "complianceLevel": 0 - }, - { - "id": "1.7.10", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/599a64f3185f1294e9ea88bdef92d928f26b9512/1.7.10.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-14T17:29:23+00:00", - "sha1": "599a64f3185f1294e9ea88bdef92d928f26b9512", - "complianceLevel": 0 - }, - { - "id": "1.7.10-pre4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7518c1f21d48a8ad9820cc675eebbb59fcb31ca1/1.7.10-pre4.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-14T16:29:23+00:00", - "sha1": "7518c1f21d48a8ad9820cc675eebbb59fcb31ca1", - "complianceLevel": 0 - }, - { - "id": "1.7.10-pre3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b53621ddd0810f5ec5ad5921171d69e71edb269d/1.7.10-pre3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-14T15:29:23+00:00", - "sha1": "b53621ddd0810f5ec5ad5921171d69e71edb269d", - "complianceLevel": 0 - }, - { - "id": "1.7.10-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c947a48376895042c0f9e919d406595a4a1c5350/1.7.10-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-14T14:29:23+00:00", - "sha1": "c947a48376895042c0f9e919d406595a4a1c5350", - "complianceLevel": 0 - }, - { - "id": "1.7.10-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d5cd0a9e216bd75a5180a4c75a4ae6394a3ffca6/1.7.10-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-14T13:29:23+00:00", - "sha1": "d5cd0a9e216bd75a5180a4c75a4ae6394a3ffca6", - "complianceLevel": 0 - }, - { - "id": "14w19a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5a20bf22a654153b1a7cd9f176e2e79edb338378/14w19a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-08T14:24:19+00:00", - "sha1": "5a20bf22a654153b1a7cd9f176e2e79edb338378", - "complianceLevel": 0 - }, - { - "id": "14w18b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/170ed828660f559514911e2722891a84d4beb399/14w18b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-05-02T11:38:17+00:00", - "sha1": "170ed828660f559514911e2722891a84d4beb399", - "complianceLevel": 0 - }, - { - "id": "14w18a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5fbdbee2851db59f6a6ce6f2cb37cec6ff6371d6/14w18a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-04-30T10:25:35+00:00", - "sha1": "5fbdbee2851db59f6a6ce6f2cb37cec6ff6371d6", - "complianceLevel": 0 - }, - { - "id": "14w17a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4426b5164816c7cfe358380ab6b8c74d9751044a/14w17a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-04-24T15:44:49+00:00", - "sha1": "4426b5164816c7cfe358380ab6b8c74d9751044a", - "complianceLevel": 0 - }, - { - "id": "14w11b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d79299aca3800241fb95b9e4e6fa3ccde4a9bcf3/14w11b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-04-14T14:36:19+00:00", - "sha1": "d79299aca3800241fb95b9e4e6fa3ccde4a9bcf3", - "complianceLevel": 0 - }, - { - "id": "1.7.9", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/ec16524ee44b545b3e6e51b11235e4ccab3fd8bd/1.7.9.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-04-14T13:29:23+00:00", - "sha1": "ec16524ee44b545b3e6e51b11235e4ccab3fd8bd", - "complianceLevel": 0 - }, - { - "id": "1.7.8", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/4ce8e0a2e386c73c56c28ab8f73d9c8b0ba72cd2/1.7.8.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-04-09T07:58:16+00:00", - "sha1": "4ce8e0a2e386c73c56c28ab8f73d9c8b0ba72cd2", - "complianceLevel": 0 - }, - { - "id": "1.7.7", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/b1c123633ead2f0fe0df42017e83d0d3177d8ce4/1.7.7.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-04-09T07:52:16+00:00", - "sha1": "b1c123633ead2f0fe0df42017e83d0d3177d8ce4", - "complianceLevel": 0 - }, - { - "id": "1.7.6", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/49929aa9a5d157c38c969afc53802de6169d7d87/1.7.6.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-04-09T07:52:06+00:00", - "sha1": "49929aa9a5d157c38c969afc53802de6169d7d87", - "complianceLevel": 0 - }, - { - "id": "14w11a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/68fbc4b8f248248a0626856ccaca08f6f0ca226c/14w11a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-03-13T14:02:50+00:00", - "sha1": "68fbc4b8f248248a0626856ccaca08f6f0ca226c", - "complianceLevel": 0 - }, - { - "id": "1.7.6-pre2", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/24b960eb8fee0c1087d5d96131609d1505aa15c2/1.7.6-pre2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-03-08T11:00:01+00:00", - "sha1": "24b960eb8fee0c1087d5d96131609d1505aa15c2", - "complianceLevel": 0 - }, - { - "id": "1.7.6-pre1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5378ab328ad9ebc3b192b89c15330093b520c2da/1.7.6-pre1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-03-08T11:00:00+00:00", - "sha1": "5378ab328ad9ebc3b192b89c15330093b520c2da", - "complianceLevel": 0 - }, - { - "id": "14w10c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a6f4ad151d3f09fffd96f619f787c2dff6a8efa1/14w10c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-03-07T13:49:55+00:00", - "sha1": "a6f4ad151d3f09fffd96f619f787c2dff6a8efa1", - "complianceLevel": 0 - }, - { - "id": "14w10b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e1e6da3d2943bd03dd6b8cca180309bda6ca1fd0/14w10b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-03-06T16:25:39+00:00", - "sha1": "e1e6da3d2943bd03dd6b8cca180309bda6ca1fd0", - "complianceLevel": 0 - }, - { - "id": "14w10a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/25607c23ac5061806239e0b60bd3ee81e4a6152c/14w10a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-03-06T14:23:04+00:00", - "sha1": "25607c23ac5061806239e0b60bd3ee81e4a6152c", - "complianceLevel": 0 - }, - { - "id": "14w08a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/592180c5afa6dd958cf00a88abe68e36d93640a9/14w08a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-02-26T17:00:00+00:00", - "sha1": "592180c5afa6dd958cf00a88abe68e36d93640a9", - "complianceLevel": 0 - }, - { - "id": "1.7.5", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/da92f9d5a633ca34be003aec319cdc848abb57c2/1.7.5.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-02-26T09:22:17+00:00", - "sha1": "da92f9d5a633ca34be003aec319cdc848abb57c2", - "complianceLevel": 0 - }, - { - "id": "14w07a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/702bca4f027a9b473085b838aa96f87f1489a92b/14w07a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-02-14T11:05:07+00:00", - "sha1": "702bca4f027a9b473085b838aa96f87f1489a92b", - "complianceLevel": 0 - }, - { - "id": "14w06b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/45ca1011d3c908ae949ed1694ac519bd9f17c0dc/14w06b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-02-06T17:30:42+00:00", - "sha1": "45ca1011d3c908ae949ed1694ac519bd9f17c0dc", - "complianceLevel": 0 - }, - { - "id": "14w06a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/cfa68ef85962aa7bb9fe720417fd7e8a62d13368/14w06a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-02-06T14:30:17+00:00", - "sha1": "cfa68ef85962aa7bb9fe720417fd7e8a62d13368", - "complianceLevel": 0 - }, - { - "id": "14w05b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/da4d5277212307574cd13ec8a4d6c85ad3328789/14w05b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-31T14:05:50+00:00", - "sha1": "da4d5277212307574cd13ec8a4d6c85ad3328789", - "complianceLevel": 0 - }, - { - "id": "14w05a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/73539cf4074d09df69195bdad16bac3e8d946d9e/14w05a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-30T15:32:41+00:00", - "sha1": "73539cf4074d09df69195bdad16bac3e8d946d9e", - "complianceLevel": 0 - }, - { - "id": "14w04b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/49b102ed0d5f13843b53e201c407827fe1c90d8f/14w04b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-24T15:48:46+00:00", - "sha1": "49b102ed0d5f13843b53e201c407827fe1c90d8f", - "complianceLevel": 0 - }, - { - "id": "14w04a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f9cea15b13a226d873bc2988cc334d22f3291631/14w04a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-23T15:26:13+00:00", - "sha1": "f9cea15b13a226d873bc2988cc334d22f3291631", - "complianceLevel": 0 - }, - { - "id": "14w03b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bab1cc61777b083bf2a00c11f2368e7d5e803872/14w03b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-16T16:36:19+00:00", - "sha1": "bab1cc61777b083bf2a00c11f2368e7d5e803872", - "complianceLevel": 0 - }, - { - "id": "14w03a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ee55dc6ca30310f4a9c244dd3c5a22a261a697bf/14w03a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-16T14:45:13+00:00", - "sha1": "ee55dc6ca30310f4a9c244dd3c5a22a261a697bf", - "complianceLevel": 0 - }, - { - "id": "14w02c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/814a10b40b7daebf36c5cdb5a6d566f066fcf178/14w02c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-10T15:42:36+00:00", - "sha1": "814a10b40b7daebf36c5cdb5a6d566f066fcf178", - "complianceLevel": 0 - }, - { - "id": "14w02b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bc445f35fc6447949a54ca09e1c8ce6c0494711c/14w02b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-09T15:45:55+00:00", - "sha1": "bc445f35fc6447949a54ca09e1c8ce6c0494711c", - "complianceLevel": 0 - }, - { - "id": "14w02a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7408f77fdea0ec201f84058cfd31173b19006098/14w02a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2014-01-09T14:44:41+00:00", - "sha1": "7408f77fdea0ec201f84058cfd31173b19006098", - "complianceLevel": 0 - }, - { - "id": "1.7.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/37ab7890e1a902f97cb986401d3c0f54bf64f99f/1.7.4.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-12-09T12:28:10+00:00", - "sha1": "37ab7890e1a902f97cb986401d3c0f54bf64f99f", - "complianceLevel": 0 - }, - { - "id": "1.7.3", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/8b33f4d91954c21fd2e4c0ca6cd0c779a40845f3/1.7.3.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-12-06T13:55:34+00:00", - "sha1": "8b33f4d91954c21fd2e4c0ca6cd0c779a40845f3", - "complianceLevel": 0 - }, - { - "id": "13w49a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fc84c746aad27d3b4f5c663d11ca9bd175db1ad7/13w49a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-12-05T14:34:41+00:00", - "sha1": "fc84c746aad27d3b4f5c663d11ca9bd175db1ad7", - "complianceLevel": 0 - }, - { - "id": "13w48b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7f3b3da2d4dd64ab8b6267c118162541af5f1acd/13w48b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-11-26T18:36:08+00:00", - "sha1": "7f3b3da2d4dd64ab8b6267c118162541af5f1acd", - "complianceLevel": 0 - }, - { - "id": "13w48a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bf9b559a703febb768874dc5780583ceeb82051b/13w48a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-11-25T16:53:39+00:00", - "sha1": "bf9b559a703febb768874dc5780583ceeb82051b", - "complianceLevel": 0 - }, - { - "id": "13w47e", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fb023687e5b3c7ca1728b5fe65fe5d54d0da7f12/13w47e.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-11-22T15:16:38+00:00", - "sha1": "fb023687e5b3c7ca1728b5fe65fe5d54d0da7f12", - "complianceLevel": 0 - }, - { - "id": "13w47d", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c895d8bb338ad4507bb2370db3d154272b6fdc60/13w47d.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-11-22T13:51:15+00:00", - "sha1": "c895d8bb338ad4507bb2370db3d154272b6fdc60", - "complianceLevel": 0 - }, - { - "id": "13w47c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/866640d11984fc4748d60d163e1451fe624dd4a0/13w47c.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-11-21T17:10:33+00:00", - "sha1": "866640d11984fc4748d60d163e1451fe624dd4a0", - "complianceLevel": 0 - }, - { - "id": "13w47b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5396843998e9891481e35f7747118a83faf3a8b8/13w47b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-11-21T16:57:41+00:00", - "sha1": "5396843998e9891481e35f7747118a83faf3a8b8", - "complianceLevel": 0 - }, - { - "id": "13w47a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/5d62fde25ef840aa7df3dd98d9ca284887a96eaf/13w47a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-11-21T15:59:58+00:00", - "sha1": "5d62fde25ef840aa7df3dd98d9ca284887a96eaf", - "complianceLevel": 0 - }, - { - "id": "1.7.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/82b79d6d190e6086b02a82a1de9ae760f98aa3c5/1.7.2.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-25T13:00:00+00:00", - "sha1": "82b79d6d190e6086b02a82a1de9ae760f98aa3c5", - "complianceLevel": 0 - }, - { - "id": "1.7.1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/c55b308528a2c1296d542fc665764ccae7287c71/1.7.1.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-23T12:01:07+00:00", - "sha1": "c55b308528a2c1296d542fc665764ccae7287c71", - "complianceLevel": 0 - }, - { - "id": "1.7", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b151105d242a6ecc22285c5be55967237b99fa3b/1.7.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-22T15:04:05+00:00", - "sha1": "b151105d242a6ecc22285c5be55967237b99fa3b", - "complianceLevel": 0 - }, - { - "id": "13w43a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/483ce8296e393fdfa74378971f291934a40e4add/13w43a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-21T16:34:47+00:00", - "sha1": "483ce8296e393fdfa74378971f291934a40e4add", - "complianceLevel": 0 - }, - { - "id": "13w42b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/edb2b1a1a948b7784ba2899b51caff0d610aad44/13w42b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-18T16:34:08+00:00", - "sha1": "edb2b1a1a948b7784ba2899b51caff0d610aad44", - "complianceLevel": 0 - }, - { - "id": "13w42a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/382995d518e88327860ac3ade85db5e910d58223/13w42a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-17T18:33:05+00:00", - "sha1": "382995d518e88327860ac3ade85db5e910d58223", - "complianceLevel": 0 - }, - { - "id": "13w41b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/313c4baeac13f8d40e1445440759d60ee7a9dc27/13w41b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-11T15:09:17+00:00", - "sha1": "313c4baeac13f8d40e1445440759d60ee7a9dc27", - "complianceLevel": 0 - }, - { - "id": "13w41a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/fb6b48658bfcfeb970242149bc11bf2df6a14561/13w41a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-10-10T14:21:43+00:00", - "sha1": "fb6b48658bfcfeb970242149bc11bf2df6a14561", - "complianceLevel": 0 - }, - { - "id": "13w39b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/d1a33256e1b942ccc0bca533152bcc6795bebfcd/13w39b.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-09-27T12:15:58+00:00", - "sha1": "d1a33256e1b942ccc0bca533152bcc6795bebfcd", - "complianceLevel": 0 - }, - { - "id": "13w39a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/24a4c280471d4aaab3869ceac673dd1a611dab4a/13w39a.json", - "time": "2021-12-10T10:53:35+00:00", - "releaseTime": "2013-09-26T15:11:19+00:00", - "sha1": "24a4c280471d4aaab3869ceac673dd1a611dab4a", - "complianceLevel": 0 - }, - { - "id": "13w38c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/a9e87e0699f19fea280878f5deb744c5d5d3ccb1/13w38c.json", - "time": "2019-06-28T07:08:09+00:00", - "releaseTime": "2013-09-20T15:11:34+00:00", - "sha1": "a9e87e0699f19fea280878f5deb744c5d5d3ccb1", - "complianceLevel": 0 - }, - { - "id": "13w38b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/6f426be1993b140ab5d10459c91eb1f542d58c82/13w38b.json", - "time": "2019-06-28T07:08:09+00:00", - "releaseTime": "2013-09-20T13:45:40+00:00", - "sha1": "6f426be1993b140ab5d10459c91eb1f542d58c82", - "complianceLevel": 0 - }, - { - "id": "13w38a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e6dc1d9f9c8efeec67af438d5bf61be082f6e8a4/13w38a.json", - "time": "2019-06-28T07:08:09+00:00", - "releaseTime": "2013-09-19T16:34:21+00:00", - "sha1": "e6dc1d9f9c8efeec67af438d5bf61be082f6e8a4", - "complianceLevel": 0 - }, - { - "id": "1.6.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/b71bae449192fbbe1582ff32fb3765edf0b9b0a8/1.6.4.json", - "time": "2019-06-28T07:06:16+00:00", - "releaseTime": "2013-09-19T15:52:37+00:00", - "sha1": "b71bae449192fbbe1582ff32fb3765edf0b9b0a8", - "complianceLevel": 0 - }, - { - "id": "13w37b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b8d28154ee056af6af3c8c37815418fe0e9f34f8/13w37b.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-09-13T10:54:41+00:00", - "sha1": "b8d28154ee056af6af3c8c37815418fe0e9f34f8", - "complianceLevel": 0 - }, - { - "id": "1.6.3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/903d6ba1bc87c301d88fa418f8b33446201c7d4e/1.6.3.json", - "time": "2019-06-28T07:07:47+00:00", - "releaseTime": "2013-09-13T10:54:41+00:00", - "sha1": "903d6ba1bc87c301d88fa418f8b33446201c7d4e", - "complianceLevel": 0 - }, - { - "id": "13w37a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2f33c613a4bb81ef5f56be03a8f578208ada382a/13w37a.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-09-12T14:23:14+00:00", - "sha1": "2f33c613a4bb81ef5f56be03a8f578208ada382a", - "complianceLevel": 0 - }, - { - "id": "13w36b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/4a538e23057a596fc8c7e04d8a7738d866467f51/13w36b.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-09-06T12:31:58+00:00", - "sha1": "4a538e23057a596fc8c7e04d8a7738d866467f51", - "complianceLevel": 0 - }, - { - "id": "13w36a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/bc915c4dc167dfba92fcc0ae3aa051ae0f9f089b/13w36a.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-09-05T13:05:40+00:00", - "sha1": "bc915c4dc167dfba92fcc0ae3aa051ae0f9f089b", - "complianceLevel": 0 - }, - { - "id": "1.6.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/c0729761bf65dc58138ce508645dba1442fa78b8/1.6.2.json", - "time": "2019-06-28T07:06:16+00:00", - "releaseTime": "2013-07-05T13:09:02+00:00", - "sha1": "c0729761bf65dc58138ce508645dba1442fa78b8", - "complianceLevel": 0 - }, - { - "id": "1.6.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/7fd8e0c76f62813eb0465e31bb74b160c01472d6/1.6.1.json", - "time": "2019-06-28T07:06:16+00:00", - "releaseTime": "2013-06-28T14:48:41+00:00", - "sha1": "7fd8e0c76f62813eb0465e31bb74b160c01472d6", - "complianceLevel": 0 - }, - { - "id": "1.6", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/20116297638f7c70cd046e25a6ac90fee4cae61a/1.6.json", - "time": "2019-06-28T07:07:47+00:00", - "releaseTime": "2013-06-25T13:08:56+00:00", - "sha1": "20116297638f7c70cd046e25a6ac90fee4cae61a", - "complianceLevel": 0 - }, - { - "id": "13w26a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b349702aef5e3adaebec30c79338300423943930/13w26a.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-06-24T16:06:06+00:00", - "sha1": "b349702aef5e3adaebec30c79338300423943930", - "complianceLevel": 0 - }, - { - "id": "13w25c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/934788bc580ef0a19725ee5bd31f02a0b866e0bf/13w25c.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-06-20T15:23:37+00:00", - "sha1": "934788bc580ef0a19725ee5bd31f02a0b866e0bf", - "complianceLevel": 0 - }, - { - "id": "13w25b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/8b7870ddd0d0b38779479ad782d65ad80e688cf7/13w25b.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-06-18T15:13:27+00:00", - "sha1": "8b7870ddd0d0b38779479ad782d65ad80e688cf7", - "complianceLevel": 0 - }, - { - "id": "13w25a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/65c0e5fff89b477ac6f8ddb336f0e718d525d311/13w25a.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-06-17T14:08:06+00:00", - "sha1": "65c0e5fff89b477ac6f8ddb336f0e718d525d311", - "complianceLevel": 0 - }, - { - "id": "13w24b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/e1294b52803771cfb06767c4c40dced70475cb25/13w24b.json", - "time": "2019-06-28T07:08:08+00:00", - "releaseTime": "2013-06-14T12:19:13+00:00", - "sha1": "e1294b52803771cfb06767c4c40dced70475cb25", - "complianceLevel": 0 - }, - { - "id": "13w24a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/74666ab85cc5539f08aec638eabd63a552ed4125/13w24a.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-06-13T15:32:23+00:00", - "sha1": "74666ab85cc5539f08aec638eabd63a552ed4125", - "complianceLevel": 0 - }, - { - "id": "13w23b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b19c87c7bfe0c0b202f79fc4b870b0ae97b00e53/13w23b.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-06-08T00:32:01+00:00", - "sha1": "b19c87c7bfe0c0b202f79fc4b870b0ae97b00e53", - "complianceLevel": 0 - }, - { - "id": "13w23a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/400a136ff102882dfa9bb8990aef32b81309d46a/13w23a.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-06-07T16:04:20+00:00", - "sha1": "400a136ff102882dfa9bb8990aef32b81309d46a", - "complianceLevel": 0 - }, - { - "id": "13w22a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f8277e55fae1555de47b44f5e6620f13b79fbe4e/13w22a.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-05-30T14:38:40+00:00", - "sha1": "f8277e55fae1555de47b44f5e6620f13b79fbe4e", - "complianceLevel": 0 - }, - { - "id": "13w21b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/eab4bc12a78d862fadb36192c5351e35888eab15/13w21b.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-05-27T08:50:42+00:00", - "sha1": "eab4bc12a78d862fadb36192c5351e35888eab15", - "complianceLevel": 0 - }, - { - "id": "13w21a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/ff59021171bd73aa155e40f84a924e1ab3f5307d/13w21a.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-05-23T15:38:28+00:00", - "sha1": "ff59021171bd73aa155e40f84a924e1ab3f5307d", - "complianceLevel": 0 - }, - { - "id": "13w19a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/9360f33fa1391cbbfead0e0033c5a1e763f28d19/13w19a.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-05-10T14:48:02+00:00", - "sha1": "9360f33fa1391cbbfead0e0033c5a1e763f28d19", - "complianceLevel": 0 - }, - { - "id": "13w18c", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/79ab628c68cefd8daa25d186f7961299ef4e63a9/13w18c.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-05-03T09:19:35+00:00", - "sha1": "79ab628c68cefd8daa25d186f7961299ef4e63a9", - "complianceLevel": 0 - }, - { - "id": "13w18b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/2eea88342657ab8327af70ac2e1416859e0ef02a/13w18b.json", - "time": "2019-06-28T07:08:07+00:00", - "releaseTime": "2013-05-02T17:12:25+00:00", - "sha1": "2eea88342657ab8327af70ac2e1416859e0ef02a", - "complianceLevel": 0 - }, - { - "id": "13w18a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/0fdbf2d4561027558ba9475c5d318540c743dec9/13w18a.json", - "time": "2019-06-28T07:08:06+00:00", - "releaseTime": "2013-05-02T15:45:59+00:00", - "sha1": "0fdbf2d4561027558ba9475c5d318540c743dec9", - "complianceLevel": 0 - }, - { - "id": "13w17a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/f65b6bd3c813d67b026a3c9ec12e3280c495cf87/13w17a.json", - "time": "2019-06-28T07:08:06+00:00", - "releaseTime": "2013-04-25T15:50:00+00:00", - "sha1": "f65b6bd3c813d67b026a3c9ec12e3280c495cf87", - "complianceLevel": 0 - }, - { - "id": "1.5.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/a611948faa091ef3a0af43d39f77589c5e402170/1.5.2.json", - "time": "2019-06-28T07:06:16+00:00", - "releaseTime": "2013-04-25T15:45:00+00:00", - "sha1": "a611948faa091ef3a0af43d39f77589c5e402170", - "complianceLevel": 0 - }, - { - "id": "13w16b", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/b2c1241c6ca9d119a9f2df431cdf18a56f883d0d/13w16b.json", - "time": "2019-06-28T07:08:06+00:00", - "releaseTime": "2013-04-23T21:51:22+00:00", - "sha1": "b2c1241c6ca9d119a9f2df431cdf18a56f883d0d", - "complianceLevel": 0 - }, - { - "id": "13w16a", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/7efbf7a86354b08f0a5011c96752c92cabb2e67c/13w16a.json", - "time": "2019-06-28T07:08:06+00:00", - "releaseTime": "2013-04-21T12:49:30+00:00", - "sha1": "7efbf7a86354b08f0a5011c96752c92cabb2e67c", - "complianceLevel": 0 - }, - { - "id": "1.5.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/46b50f586db97821f22d2c94914c3a31f733a264/1.5.1.json", - "time": "2019-06-28T07:06:16+00:00", - "releaseTime": "2013-03-20T10:00:00+00:00", - "sha1": "46b50f586db97821f22d2c94914c3a31f733a264", - "complianceLevel": 0 - }, - { - "id": "1.5", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/00fa1c125d5ad5ccdbc55f1d1b53b6f52c85bd22/1.5.json", - "time": "2019-06-28T07:07:47+00:00", - "releaseTime": "2013-03-06T22:00:00+00:00", - "sha1": "00fa1c125d5ad5ccdbc55f1d1b53b6f52c85bd22", - "complianceLevel": 0 - }, - { - "id": "1.4.7", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/89802d57ccee3d03ec59d2ab1f44386234adb399/1.4.7.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-12-27T22:00:00+00:00", - "sha1": "89802d57ccee3d03ec59d2ab1f44386234adb399", - "complianceLevel": 0 - }, - { - "id": "1.4.5", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/19555cce1f6d04e2b417ac2e9e06b6b752d5a2de/1.4.5.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-12-19T22:00:00+00:00", - "sha1": "19555cce1f6d04e2b417ac2e9e06b6b752d5a2de", - "complianceLevel": 0 - }, - { - "id": "1.4.6", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/a7d02971582fcdf14ea275cc549cb57604a37079/1.4.6.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-12-19T22:00:00+00:00", - "sha1": "a7d02971582fcdf14ea275cc549cb57604a37079", - "complianceLevel": 0 - }, - { - "id": "1.4.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/578d09d5753e590ea043a68bdaaea45a5b9711ab/1.4.4.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-12-13T22:00:00+00:00", - "sha1": "578d09d5753e590ea043a68bdaaea45a5b9711ab", - "complianceLevel": 0 - }, - { - "id": "1.4.3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/180deba5c263367e914217be0701bd9e1a44f13b/1.4.3.json", - "time": "2019-06-28T07:07:47+00:00", - "releaseTime": "2012-11-30T22:00:00+00:00", - "sha1": "180deba5c263367e914217be0701bd9e1a44f13b", - "complianceLevel": 0 - }, - { - "id": "1.4.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/aec7405cf74ae5f79b13f6e8c88f66920eac0e1b/1.4.2.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-11-24T22:00:00+00:00", - "sha1": "aec7405cf74ae5f79b13f6e8c88f66920eac0e1b", - "complianceLevel": 0 - }, - { - "id": "1.4.1", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/859f584890b4db227f267026510c6ac2a5076d94/1.4.1.json", - "time": "2019-06-28T07:07:47+00:00", - "releaseTime": "2012-11-22T22:00:00+00:00", - "sha1": "859f584890b4db227f267026510c6ac2a5076d94", - "complianceLevel": 0 - }, - { - "id": "1.4", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/070986801bc1d42eac872758cf12f00afa7b5f35/1.4.json", - "time": "2019-06-28T07:07:47+00:00", - "releaseTime": "2012-11-18T22:00:00+00:00", - "sha1": "070986801bc1d42eac872758cf12f00afa7b5f35", - "complianceLevel": 0 - }, - { - "id": "1.3.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/2bd0ca9b77465a29df4b9449772d008f8724dd19/1.3.2.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-08-15T22:00:00+00:00", - "sha1": "2bd0ca9b77465a29df4b9449772d008f8724dd19", - "complianceLevel": 0 - }, - { - "id": "1.3.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/be773d2fff5c6e4db9929ae4ea780f8837323b6b/1.3.1.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-07-31T22:00:00+00:00", - "sha1": "be773d2fff5c6e4db9929ae4ea780f8837323b6b", - "complianceLevel": 0 - }, - { - "id": "1.3", - "type": "snapshot", - "url": "https://launchermeta.mojang.com/v1/packages/aeb1bb40dc59420433ae46a0f133392508218bbe/1.3.json", - "time": "2019-06-28T07:07:47+00:00", - "releaseTime": "2012-07-25T22:00:00+00:00", - "sha1": "aeb1bb40dc59420433ae46a0f133392508218bbe", - "complianceLevel": 0 - }, - { - "id": "1.2.5", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/886723c6385e62bb8dbe8026abf685140602404b/1.2.5.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-03-29T22:00:00+00:00", - "sha1": "886723c6385e62bb8dbe8026abf685140602404b", - "complianceLevel": 0 - }, - { - "id": "1.2.4", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/3cc81a28957e8c7d94bdd4f6b2c51ef8cae54f9b/1.2.4.json", - "time": "2019-06-28T07:06:15+00:00", - "releaseTime": "2012-03-21T22:00:00+00:00", - "sha1": "3cc81a28957e8c7d94bdd4f6b2c51ef8cae54f9b", - "complianceLevel": 0 - }, - { - "id": "1.2.3", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/4100a29330f76c93ef3bdc47d11fa016fd73de99/1.2.3.json", - "time": "2019-06-28T07:06:14+00:00", - "releaseTime": "2012-03-01T22:00:00+00:00", - "sha1": "4100a29330f76c93ef3bdc47d11fa016fd73de99", - "complianceLevel": 0 - }, - { - "id": "1.2.2", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/897ce0e56d1cecc9e720f1934c09ea395008aa9e/1.2.2.json", - "time": "2019-06-28T07:06:14+00:00", - "releaseTime": "2012-02-29T22:00:01+00:00", - "sha1": "897ce0e56d1cecc9e720f1934c09ea395008aa9e", - "complianceLevel": 0 - }, - { - "id": "1.2.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/dc5f9e134da9e18a2db42ddc246aba5cdfe28d3c/1.2.1.json", - "time": "2019-06-28T07:06:14+00:00", - "releaseTime": "2012-02-29T22:00:00+00:00", - "sha1": "dc5f9e134da9e18a2db42ddc246aba5cdfe28d3c", - "complianceLevel": 0 - }, - { - "id": "1.1", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/1c1aaa3dea5155b549d4baf3491e3f5f564c3b8b/1.1.json", - "time": "2019-06-28T07:05:45+00:00", - "releaseTime": "2012-01-11T22:00:00+00:00", - "sha1": "1c1aaa3dea5155b549d4baf3491e3f5f564c3b8b", - "complianceLevel": 0 - }, - { - "id": "1.0", - "type": "release", - "url": "https://launchermeta.mojang.com/v1/packages/edfc56a64dfc6430665d745264732db53b0d1b41/1.0.json", - "time": "2019-06-28T07:05:45+00:00", - "releaseTime": "2011-11-17T22:00:00+00:00", - "sha1": "edfc56a64dfc6430665d745264732db53b0d1b41", - "complianceLevel": 0 - }, - { - "id": "b1.8.1", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/5b5a652a385b3d04af8e05bb773696a227ebc300/b1.8.1.json", - "time": "2019-06-28T07:05:45+00:00", - "releaseTime": "2011-09-18T22:00:00+00:00", - "sha1": "5b5a652a385b3d04af8e05bb773696a227ebc300", - "complianceLevel": 0 - }, - { - "id": "b1.8", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/c2ce53a6d847e3c3efb640dc7d32e47e72e0a24f/b1.8.json", - "time": "2019-06-28T07:05:45+00:00", - "releaseTime": "2011-09-14T22:00:00+00:00", - "sha1": "c2ce53a6d847e3c3efb640dc7d32e47e72e0a24f", - "complianceLevel": 0 - }, - { - "id": "b1.7.3", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/0de35233bea254fd0011cceb4aa96e0a32b7efd2/b1.7.3.json", - "time": "2019-06-28T07:05:45+00:00", - "releaseTime": "2011-07-07T22:00:00+00:00", - "sha1": "0de35233bea254fd0011cceb4aa96e0a32b7efd2", - "complianceLevel": 0 - }, - { - "id": "b1.7.2", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/b9ac061f45d78c16739c95871d1540c681a187c8/b1.7.2.json", - "time": "2019-06-28T07:05:45+00:00", - "releaseTime": "2011-06-30T22:00:00+00:00", - "sha1": "b9ac061f45d78c16739c95871d1540c681a187c8", - "complianceLevel": 0 - }, - { - "id": "b1.7", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/3b7900fbef699471069015beef2a0d62cb2efabf/b1.7.json", - "time": "2019-06-28T07:05:45+00:00", - "releaseTime": "2011-06-29T22:00:00+00:00", - "sha1": "3b7900fbef699471069015beef2a0d62cb2efabf", - "complianceLevel": 0 - }, - { - "id": "b1.6.6", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/2c39299d7841e5273f5a6347373ba89eb48149d2/b1.6.6.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-05-30T22:00:00+00:00", - "sha1": "2c39299d7841e5273f5a6347373ba89eb48149d2", - "complianceLevel": 0 - }, - { - "id": "b1.6.5", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/f52059e5de7e06cbceba3642895005a36c40a6ef/b1.6.5.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-05-27T22:00:00+00:00", - "sha1": "f52059e5de7e06cbceba3642895005a36c40a6ef", - "complianceLevel": 0 - }, - { - "id": "b1.6.4", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/3a6546e2e4be5492c974359880f5cd6f1c513478/b1.6.4.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-05-25T22:00:04+00:00", - "sha1": "3a6546e2e4be5492c974359880f5cd6f1c513478", - "complianceLevel": 0 - }, - { - "id": "b1.6.3", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/4aa947cc55a28139e06ba6cde47b2c5aa3d9941c/b1.6.3.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-05-25T22:00:03+00:00", - "sha1": "4aa947cc55a28139e06ba6cde47b2c5aa3d9941c", - "complianceLevel": 0 - }, - { - "id": "b1.6.2", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/299360b97a57c73c0a6f258313200f40a37ab758/b1.6.2.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-05-25T22:00:02+00:00", - "sha1": "299360b97a57c73c0a6f258313200f40a37ab758", - "complianceLevel": 0 - }, - { - "id": "b1.6.1", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/72fc5a50e6a2fc9cdb5d77dcaa1f4cd4391536d3/b1.6.1.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-05-25T22:00:01+00:00", - "sha1": "72fc5a50e6a2fc9cdb5d77dcaa1f4cd4391536d3", - "complianceLevel": 0 - }, - { - "id": "b1.6", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/70c6be33812e5a030db15b28ccd72d30a5dcbca7/b1.6.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-05-25T22:00:00+00:00", - "sha1": "70c6be33812e5a030db15b28ccd72d30a5dcbca7", - "complianceLevel": 0 - }, - { - "id": "b1.5_01", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/cda4fc0e6f35c8645a013045be7c746e1a5b63d3/b1.5_01.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-04-19T22:00:00+00:00", - "sha1": "cda4fc0e6f35c8645a013045be7c746e1a5b63d3", - "complianceLevel": 0 - }, - { - "id": "b1.5", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/cd33026e81d260495f859a446a1f17e3071a17ea/b1.5.json", - "time": "2019-06-28T07:05:44+00:00", - "releaseTime": "2011-04-18T22:00:00+00:00", - "sha1": "cd33026e81d260495f859a446a1f17e3071a17ea", - "complianceLevel": 0 - }, - { - "id": "b1.4_01", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/7a7fbff959b9576845d83b215cc82ecf6bca5bcf/b1.4_01.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2011-04-04T22:00:00+00:00", - "sha1": "7a7fbff959b9576845d83b215cc82ecf6bca5bcf", - "complianceLevel": 0 - }, - { - "id": "b1.4", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/ce8cd5c165f2941d343cd98ec76a5b93d9a95c8c/b1.4.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2011-03-30T22:00:00+00:00", - "sha1": "ce8cd5c165f2941d343cd98ec76a5b93d9a95c8c", - "complianceLevel": 0 - }, - { - "id": "b1.3_01", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/f551611d75278e2847cca77421a51b99f4bb32de/b1.3_01.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2011-02-22T22:00:00+00:00", - "sha1": "f551611d75278e2847cca77421a51b99f4bb32de", - "complianceLevel": 0 - }, - { - "id": "b1.3b", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/a803c5a24b04ccab17269183b872cdaa38b32ae3/b1.3b.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2011-02-21T22:00:00+00:00", - "sha1": "a803c5a24b04ccab17269183b872cdaa38b32ae3", - "complianceLevel": 0 - }, - { - "id": "b1.2_02", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/afeabd62ef6bab19c8d570ab39a5e4161b27b85d/b1.2_02.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2011-01-20T22:00:00+00:00", - "sha1": "afeabd62ef6bab19c8d570ab39a5e4161b27b85d", - "complianceLevel": 0 - }, - { - "id": "b1.2_01", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/e9ea31d80bb8979bf3042165094d2e10b2899eaf/b1.2_01.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2011-01-13T22:00:00+00:00", - "sha1": "e9ea31d80bb8979bf3042165094d2e10b2899eaf", - "complianceLevel": 0 - }, - { - "id": "b1.2", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/703f0c0797d0b83dac8304b1c35ee4dcee6a2413/b1.2.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2011-01-12T22:00:00+00:00", - "sha1": "703f0c0797d0b83dac8304b1c35ee4dcee6a2413", - "complianceLevel": 0 - }, - { - "id": "b1.1_02", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/219e087b54dc2ca9a51a0204238cccaa035b5cef/b1.1_02.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2010-12-21T22:00:01+00:00", - "sha1": "219e087b54dc2ca9a51a0204238cccaa035b5cef", - "complianceLevel": 0 - }, - { - "id": "b1.1_01", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/d0d10f21a0f8115bdfbc2f0ddef2d93833af2e9d/b1.1_01.json", - "time": "2019-06-28T07:05:43+00:00", - "releaseTime": "2010-12-21T22:00:00+00:00", - "sha1": "d0d10f21a0f8115bdfbc2f0ddef2d93833af2e9d", - "complianceLevel": 0 - }, - { - "id": "b1.0.2", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/f8d933ed690495b66f76d0a5045e40c18881a26a/b1.0.2.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2010-12-20T22:00:00+00:00", - "sha1": "f8d933ed690495b66f76d0a5045e40c18881a26a", - "complianceLevel": 0 - }, - { - "id": "b1.0_01", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/c7559faa3f520837456b2a3736c663a856812049/b1.0_01.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2010-12-19T22:00:01+00:00", - "sha1": "c7559faa3f520837456b2a3736c663a856812049", - "complianceLevel": 0 - }, - { - "id": "b1.0", - "type": "old_beta", - "url": "https://launchermeta.mojang.com/v1/packages/5be70efae7eea5f18ce164fb264eaa9563a054ee/b1.0.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2010-12-19T22:00:00+00:00", - "sha1": "5be70efae7eea5f18ce164fb264eaa9563a054ee", - "complianceLevel": 0 - }, - { - "id": "a1.2.6", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/d385e176aa7d3d3702bac78ad1ba906a77de13df/a1.2.6.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2010-12-02T22:00:00+00:00", - "sha1": "d385e176aa7d3d3702bac78ad1ba906a77de13df", - "complianceLevel": 0 - }, - { - "id": "a1.2.5", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/491a4961f00770bd130206c013795f35af948493/a1.2.5.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2010-11-30T22:00:00+00:00", - "sha1": "491a4961f00770bd130206c013795f35af948493", - "complianceLevel": 0 - }, - { - "id": "a1.2.4_01", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/e802a257031c5b9297c971599cc2573c2efece2c/a1.2.4_01.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2010-11-29T22:00:00+00:00", - "sha1": "e802a257031c5b9297c971599cc2573c2efece2c", - "complianceLevel": 0 - }, - { - "id": "a1.2.3_04", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/467403a159661d486fbe49480faf0909ea533759/a1.2.3_04.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2010-11-25T22:00:00+00:00", - "sha1": "467403a159661d486fbe49480faf0909ea533759", - "complianceLevel": 0 - }, - { - "id": "a1.2.3_02", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/31fa028661857f2e3d3732d07a6d36ec21d6dbdc/a1.2.3_02.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2010-11-24T22:00:00+00:00", - "sha1": "31fa028661857f2e3d3732d07a6d36ec21d6dbdc", - "complianceLevel": 0 - }, - { - "id": "a1.2.3_01", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/2dbccc4579a4481dc8d72a962d396de044648522/a1.2.3_01.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-11-23T22:00:01+00:00", - "sha1": "2dbccc4579a4481dc8d72a962d396de044648522", - "complianceLevel": 0 - }, - { - "id": "a1.2.3", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/48f077bf27e0a01a0bb2051e0ac17a96693cb730/a1.2.3.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-11-23T22:00:00+00:00", - "sha1": "48f077bf27e0a01a0bb2051e0ac17a96693cb730", - "complianceLevel": 0 - }, - { - "id": "a1.2.2b", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/0fbd340ae9087db32535ed0fb2d119240e7e0aaa/a1.2.2b.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-11-09T22:00:01+00:00", - "sha1": "0fbd340ae9087db32535ed0fb2d119240e7e0aaa", - "complianceLevel": 0 - }, - { - "id": "a1.2.2a", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/6679a974769ad2c6b88025ccbb60c72612dcee1f/a1.2.2a.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-11-09T22:00:00+00:00", - "sha1": "6679a974769ad2c6b88025ccbb60c72612dcee1f", - "complianceLevel": 0 - }, - { - "id": "a1.2.1_01", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/79e993b71f867e777a0ac9e2816bfd3df5c1aaed/a1.2.1_01.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-11-04T22:00:01+00:00", - "sha1": "79e993b71f867e777a0ac9e2816bfd3df5c1aaed", - "complianceLevel": 0 - }, - { - "id": "a1.2.1", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/f601478f5b0bf55ba0a302a4d1a8ce402c9311c1/a1.2.1.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-11-04T22:00:00+00:00", - "sha1": "f601478f5b0bf55ba0a302a4d1a8ce402c9311c1", - "complianceLevel": 0 - }, - { - "id": "a1.2.0_02", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/48465dfa2e2066e3b06e8fca934f2bbd7d03e65c/a1.2.0_02.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-11-03T22:00:00+00:00", - "sha1": "48465dfa2e2066e3b06e8fca934f2bbd7d03e65c", - "complianceLevel": 0 - }, - { - "id": "a1.2.0_01", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/e31556ae6f925d4dc7a06a5110cce731eca6eaa8/a1.2.0_01.json", - "time": "2019-06-28T07:05:40+00:00", - "releaseTime": "2010-10-30T22:00:00+00:00", - "sha1": "e31556ae6f925d4dc7a06a5110cce731eca6eaa8", - "complianceLevel": 0 - }, - { - "id": "a1.2.0", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/a0e7c34fba8c11fa8b84a56a01ed8505ad34b52a/a1.2.0.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-10-29T22:00:00+00:00", - "sha1": "a0e7c34fba8c11fa8b84a56a01ed8505ad34b52a", - "complianceLevel": 0 - }, - { - "id": "a1.1.2_01", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/c28a71888f39178cfc6f882fd62f49de920d138e/a1.1.2_01.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-09-22T22:00:00+00:00", - "sha1": "c28a71888f39178cfc6f882fd62f49de920d138e", - "complianceLevel": 0 - }, - { - "id": "a1.1.2", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/1231fefa206b10993e4b77b13f86fc72759db038/a1.1.2.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-09-19T22:00:00+00:00", - "sha1": "1231fefa206b10993e4b77b13f86fc72759db038", - "complianceLevel": 0 - }, - { - "id": "a1.1.0", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/a30685ddf58121f25dd845e5361a3d262409489d/a1.1.0.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-09-12T22:00:00+00:00", - "sha1": "a30685ddf58121f25dd845e5361a3d262409489d", - "complianceLevel": 0 - }, - { - "id": "a1.0.17_04", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/7c85d4575c9cc951135c3e6e958e1fcb2ed4e4ae/a1.0.17_04.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-08-22T22:00:00+00:00", - "sha1": "7c85d4575c9cc951135c3e6e958e1fcb2ed4e4ae", - "complianceLevel": 0 - }, - { - "id": "a1.0.17_02", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/54d1b6731a61a0b513ec0f677b58249be6c94086/a1.0.17_02.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-08-19T22:00:00+00:00", - "sha1": "54d1b6731a61a0b513ec0f677b58249be6c94086", - "complianceLevel": 0 - }, - { - "id": "a1.0.16", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/139fcc1a590a1166b872046bf4b82cfb04452061/a1.0.16.json", - "time": "2019-06-28T07:05:38+00:00", - "releaseTime": "2010-08-11T22:00:00+00:00", - "sha1": "139fcc1a590a1166b872046bf4b82cfb04452061", - "complianceLevel": 0 - }, - { - "id": "a1.0.15", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/786cd66bd91d3bd8ebd5d7fb10ec63daf671b646/a1.0.15.json", - "time": "2019-06-28T07:05:38+00:00", - "releaseTime": "2010-08-03T22:00:00+00:00", - "sha1": "786cd66bd91d3bd8ebd5d7fb10ec63daf671b646", - "complianceLevel": 0 - }, - { - "id": "a1.0.14", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/eff58a647f38df437241e769a9703baa40e0efd8/a1.0.14.json", - "time": "2019-06-28T07:05:38+00:00", - "releaseTime": "2010-07-29T22:00:00+00:00", - "sha1": "eff58a647f38df437241e769a9703baa40e0efd8", - "complianceLevel": 0 - }, - { - "id": "a1.0.11", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/3467dbde729f4e43047e54627ca20c82c9ee9a66/a1.0.11.json", - "time": "2019-06-28T07:05:38+00:00", - "releaseTime": "2010-07-22T22:00:00+00:00", - "sha1": "3467dbde729f4e43047e54627ca20c82c9ee9a66", - "complianceLevel": 0 - }, - { - "id": "a1.0.5_01", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/269ea3fdb671777a7bd0e97d4046cc27dc909694/a1.0.5_01.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-07-12T22:00:00+00:00", - "sha1": "269ea3fdb671777a7bd0e97d4046cc27dc909694", - "complianceLevel": 0 - }, - { - "id": "a1.0.4", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/a1f71c0a68d59c0b6570073b440fb55ff889ba5a/a1.0.4.json", - "time": "2019-06-28T07:05:39+00:00", - "releaseTime": "2010-07-08T22:00:00+00:00", - "sha1": "a1f71c0a68d59c0b6570073b440fb55ff889ba5a", - "complianceLevel": 0 - }, - { - "id": "inf-20100618", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/065ce5795aaf172080a4975cefac0d248bee7a3b/inf-20100618.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2010-06-15T22:00:00+00:00", - "sha1": "065ce5795aaf172080a4975cefac0d248bee7a3b", - "complianceLevel": 0 - }, - { - "id": "c0.30_01c", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/0bb9bdebc3e124818fd31779a4bb394283050a02/c0.30_01c.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2009-12-21T22:00:00+00:00", - "sha1": "0bb9bdebc3e124818fd31779a4bb394283050a02", - "complianceLevel": 0 - }, - { - "id": "c0.0.13a", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/5d77dd27f9684b4cbfbecc29a2c8c28d36a5e9c2/c0.0.13a.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2009-05-30T22:00:00+00:00", - "sha1": "5d77dd27f9684b4cbfbecc29a2c8c28d36a5e9c2", - "complianceLevel": 0 - }, - { - "id": "c0.0.13a_03", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/3e038a3d4ce26771a8019c8c348a18844b950fdc/c0.0.13a_03.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2009-05-21T22:00:00+00:00", - "sha1": "3e038a3d4ce26771a8019c8c348a18844b950fdc", - "complianceLevel": 0 - }, - { - "id": "c0.0.11a", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/d700384628b420acbe14388efc3b563e85ff7961/c0.0.11a.json", - "time": "2019-06-28T07:05:41+00:00", - "releaseTime": "2009-05-16T22:00:00+00:00", - "sha1": "d700384628b420acbe14388efc3b563e85ff7961", - "complianceLevel": 0 - }, - { - "id": "rd-161348", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/a937d17cca60af0a7d45d04b49a849af16b08a28/rd-161348.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2009-05-16T11:48:00+00:00", - "sha1": "a937d17cca60af0a7d45d04b49a849af16b08a28", - "complianceLevel": 0 - }, - { - "id": "rd-160052", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/c33dd04acfdbf34dcdfcca64db8545339ea24f02/rd-160052.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2009-05-15T22:52:00+00:00", - "sha1": "c33dd04acfdbf34dcdfcca64db8545339ea24f02", - "complianceLevel": 0 - }, - { - "id": "rd-20090515", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/1bcd01f323df5c5092e9f0967b3d310d5bc0013a/rd-20090515.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2009-05-14T22:00:00+00:00", - "sha1": "1bcd01f323df5c5092e9f0967b3d310d5bc0013a", - "complianceLevel": 0 - }, - { - "id": "rd-132328", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/77baa48d9cbbc6c3165c294e5bcdab2ca6903d57/rd-132328.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2009-05-13T21:28:00+00:00", - "sha1": "77baa48d9cbbc6c3165c294e5bcdab2ca6903d57", - "complianceLevel": 0 - }, - { - "id": "rd-132211", - "type": "old_alpha", - "url": "https://launchermeta.mojang.com/v1/packages/0f2a46082313d0ec67972f9f63c3fa6591f9bb85/rd-132211.json", - "time": "2019-06-28T07:05:42+00:00", - "releaseTime": "2009-05-13T20:11:00+00:00", - "sha1": "0f2a46082313d0ec67972f9f63c3fa6591f9bb85", - "complianceLevel": 0 - } - ] -} \ No newline at end of file diff --git a/test/data/MinecraftNews-2022-12-11.json b/test/data/MinecraftNews-2022-12-11.json deleted file mode 100644 index 8c2d579ea..000000000 --- a/test/data/MinecraftNews-2022-12-11.json +++ /dev/null @@ -1 +0,0 @@ -{"article_grid":[{"default_tile":{"sub_header":"The familiar yet unexpected allies in Minecraft Legends","image":{"content_type":"image","imageURL":"/content/dam/games/badger/game-characters/Legends_newfriends_tile.jpg"},"tile_size":"1x1","title":"When old foes become new friends"},"articleLang":"en-us","primary_category":"Deep Dives","preferred_tile":{"sub_header":"The familiar yet unexpected allies in Minecraft Legends","image":{"content_type":"image","imageURL":"/content/dam/games/badger/game-characters/Legends_newfriends_tile1x2.jpg"},"tile_size":"1x2","title":"When old foes become new friends"},"categories":["Deep Dives","Legends"],"article_url":"/en-us/article/when-old-foes-become-new-friends","publish_date":"09 December 2022 20:39:22 UTC","tags":["minecraft:stockholm/deep-dives","minecraft:stockholm/legends"]},{"default_tile":{"sub_header":"Not that plain at all!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/sunflower_1x1.jpg","alt":"A sunflower plains biome in the sunset!"},"tile_size":"1x1","title":"Around the Block: Sunflower Plains"},"articleLang":"en-us","primary_category":"Deep Dives","categories":["Deep Dives","Minecraft"],"article_url":"/en-us/article/around-block--sunflower-plains","publish_date":"08 December 2022 16:49:26 UTC","tags":["minecraft:stockholm/deep-dives","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"Winter wonders, mythical skins, and more! ","image":{"content_type":"image","imageURL":"MCMKT_RealmsPlus_December2022_NET_277x277.jpg","alt":"Jump into Realms Plus December! This image features a sneak peak into Pet Mobs, Cartoon Craft and Survival Scavenger Hunt!"},"tile_size":"1x1","title":"Realms Plus December 2022"},"articleLang":"en-us","primary_category":"Marketplace","preferred_tile":{"sub_header":"Winter wonders, mythical skins, and more! ","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/realms/MCMKT_RealmsPlus_December2022_NET_565x277.jpg","alt":"Jump into Realms Plus December! This image features a sneak peak into Pet Mobs, Cartoon Craft and Survival Scavenger Hunt!"},"tile_size":"2x1","title":"Realms Plus December 2022"},"categories":["Marketplace","Realms Plus"],"article_url":"/en-us/article/realms-plus-december-2022","publish_date":"07 December 2022 18:00:01 UTC","tags":["minecraft:stockholm/marketplace","minecraft:stockholm/realms-plus"]},{"default_tile":{"sub_header":"Minecraft Java 1.19.3 Released","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/1.19.3_1x1.jpg","alt":"An angry Vex floating above a Dark Oak Forest"},"tile_size":"1x1","title":"Minecraft Java Edition 1.19.3"},"articleLang":"en-us","primary_category":"News","categories":["News","Minecraft"],"article_url":"/en-us/article/minecraft-java-edition-1-19-3","publish_date":"07 December 2022 15:58:45 UTC","tags":["minecraft:stockholm/news","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"It\u2019s fashion, darling! ","image":{"content_type":"image","imageURL":"/content/dam/community/fy23/community-townhall/communityskins_fallfashion/FallFashion_Thumbnail_277x277.png","alt":"Image in Minecraft of a reddish brunette haired person wearing a pumpkin sweater surrounded by grass and flowers"},"tile_size":"1x1","title":"Monthly Skins: Fall Fashion"},"articleLang":"en-us","primary_category":"Fan Art","categories":["Fan Art"],"article_url":"/en-us/article/monthly-skins--fall-fashion","publish_date":"09 December 2022 19:59:55 UTC","tags":["minecraft:redmond/fan-art"]},{"default_tile":{"sub_header":"Aang, Korra, and friends are coming to Minecraft!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/marketplace/Avatar_.net_277x277.jpg","alt":"An epic image showing Aang, Korra and friends as part of the new Avatar Legends DLC!"},"tile_size":"1x1","title":"A new Avatar adventure"},"articleLang":"en-us","primary_category":"Marketplace","preferred_tile":{"sub_header":"Aang, Korra, and friends are coming to Minecraft!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/marketplace/Avatar_.net_565x565.jpg","alt":"An epic image showing Aang, Korra and friends as part of the new Avatar Legends DLC!"},"tile_size":"2x2","title":"A new Avatar adventure"},"categories":["Marketplace"],"article_url":"/en-us/article/new-avatar-adventure","publish_date":"06 December 2022 18:15:01 UTC","tags":["minecraft:stockholm/marketplace"]},{"default_tile":{"sub_header":"A Minecraft Java Release Candidate","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/1.19.3-rc3_1x1.jpg","alt":"An armor stand with chainmail and a trident standing in a lush cave."},"tile_size":"1x1","title":"Minecraft 1.19.3 Release Candidate 3"},"articleLang":"en-us","primary_category":"News","categories":["News","Minecraft"],"article_url":"/en-us/article/minecraft-1-19-3-release-candidate-3","publish_date":"06 December 2022 13:28:35 UTC","tags":["minecraft:stockholm/news","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"An introduction to Minecraft\u2019s own energy resource","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/redstone_tile.png","alt":"Minecraft Education Creator content tutorials for building blocks"},"tile_size":"1x1","title":"RESEARCHING REDSTONE"},"articleLang":"en-us","primary_category":"Guides","categories":["Guides","Creator"],"article_url":"/en-us/creator/article/redstone","publish_date":"09 December 2022 22:58:29 UTC","tags":["minecraft:redmond/guides","minecraft:redmond/creator"]},{"default_tile":{"sub_header":"The Great Barrier block!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/fence-1x1.jpg","alt":"A fence next to a lake!"},"tile_size":"1x1","title":"Block of the Month: Fence"},"articleLang":"en-us","primary_category":"Deep Dives","categories":["Deep Dives","Minecraft"],"article_url":"/en-us/article/block-month--fence","publish_date":"01 December 2022 20:12:19 UTC","tags":["minecraft:stockholm/deep-dives","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"A Minecraft Java Release Candidate","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/1.19.3-rc1-1x1.jpg","alt":"A huge cave opening in a taiga"},"tile_size":"1x1","title":"Minecraft 1.19.3 Release Candidate 1"},"articleLang":"en-us","primary_category":"News","categories":["News","Minecraft"],"article_url":"/en-us/article/minecraft-1-19-3-release-candidate-1","publish_date":"05 December 2022 14:10:32 UTC","tags":["minecraft:stockholm/news","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"A Minecraft Bedrock Edition Update","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/spectator_1x1.jpg","alt":"A Minecraft screenshot showing how the world looks when in spectator gamemode and flying underground "},"tile_size":"1x1","title":"1.19.50 Update Available on Bedrock"},"articleLang":"en-us","primary_category":"News","categories":["News"],"article_url":"/en-us/article/1-19-50-update-available-bedrock","publish_date":"02 December 2022 13:58:22 UTC","tags":["minecraft:stockholm/news"]},{"default_tile":{"sub_header":"A Minecraft Java Pre-Release","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/1.19.3-pre3-1x1.jpg","alt":"A Piglin holding a Gold Ingot and a Vex looking at a Gold Ingot on a Note Block."},"tile_size":"1x1","title":"Minecraft 1.19.3 Pre-Release 3"},"articleLang":"en-us","primary_category":"News","categories":["News","Minecraft"],"article_url":"/en-us/article/minecraft-1-19-3-pre-release-3","publish_date":"29 November 2022 15:34:52 UTC","tags":["minecraft:stockholm/news","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"Make your own custom blocks in 30-seconds or less!","image":{"content_type":"image","imageURL":"/content/dam/minecraft/article-pictures/Block_Wizard_Tile_277x277.png","alt":"Minecraft news key art"},"tile_size":"1x1","title":"Introducing the Minecraft Block Wizard in Blockbench"},"articleLang":"en-us","primary_category":"Guides","categories":["Guides","Creator"],"article_url":"/en-us/creator/article/introducing-minecraft-block-wizard-blockbench","publish_date":"29 November 2022 17:22:22 UTC","tags":["minecraft:redmond/guides","minecraft:redmond/creator"]},{"default_tile":{"sub_header":"Lorem ipsum these 12 new maps!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/realms/mftl_277x277.jpg","alt":"An underwater city in Java Realms"},"tile_size":"1x1","title":"New on Java Realms: Maps for the lazy"},"articleLang":"en-us","primary_category":"News","categories":["News","Realms Java"],"article_url":"/en-us/article/new-java-realms--maps-lazy","publish_date":"28 November 2022 12:26:41 UTC","tags":["minecraft:stockholm/news","minecraft:stockholm/realms-java"]},{"default_tile":{"sub_header":"Not to be confused with cornflour!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/cornflower-1x1.jpg","alt":"A field of flowers in Minecraft"},"tile_size":"1x1","title":"Taking Inventory: Cornflower"},"articleLang":"en-us","primary_category":"Deep Dives","categories":["Deep Dives","Minecraft"],"article_url":"/en-us/article/taking-inventory--cornflower","publish_date":"24 November 2022 15:55:58 UTC","tags":["minecraft:stockholm/deep-dives","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"Celebrating our new friend!","image":{"content_type":"image","imageURL":"/content/dam/community/fy23/community-townhall/fanart-sniffer/sniffer_thumbnail_277x277.png","alt":"Drawing that's of a two girls with a brown bob haircuts, yellow sweaters, and green scaled skirts against a purple background. "},"tile_size":"1x1","title":"Community Roundup: Sniffer Fanart"},"articleLang":"en-us","primary_category":"Fan Art","categories":["Fan Art"],"article_url":"/en-us/article/community-roundup--sniffer-fanart","publish_date":"23 November 2022 00:59:56 UTC","tags":["minecraft:redmond/fan-art"]},{"default_tile":{"sub_header":"Read it! Ribbit!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/key-art/frog_1x1.jpg","alt":"Ribbit!"},"tile_size":"1x1","title":"Mob Menagerie: Frog"},"articleLang":"en-us","primary_category":"Deep Dives","preferred_tile":{"sub_header":"Read it! Ribbit!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/key-art/frog_2x2.jpg","alt":"Ribbit!"},"tile_size":"2x2","title":"Mob Menagerie: Frog"},"categories":["Deep Dives","Minecraft"],"article_url":"/en-us/article/mob-menagerie--frog","publish_date":"23 November 2022 16:16:46 UTC","tags":["minecraft:stockholm/deep-dives","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"Quacking freebies, deals, and more!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/marketplace/Minecraft_BlockFriday2022_.Net_277x277.jpg","alt":"This image shows a glowing green chest, opening to reveal.... our Block Friday Sale!"},"tile_size":"1x1","title":"Block Friday returns"},"articleLang":"en-us","primary_category":"Marketplace","preferred_tile":{"sub_header":"Quacking freebies, deals, and more!","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/marketplace/Minecraft_BlockFriday2022_.Net_565x565.jpg","alt":"This image shows a glowing green chest, opening to reveal.... our Block Friday Sale!"},"tile_size":"2x2","title":"Block Friday returns"},"categories":["Marketplace"],"article_url":"/en-us/article/block-friday-returns","publish_date":"22 November 2022 18:00:10 UTC","tags":["minecraft:stockholm/marketplace"]},{"default_tile":{"sub_header":"A Minecraft Java Pre-Release","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/screenshots/1-19-3-pre-release-1-1x1.jpg","alt":"In the Nether, on a small piece of land, there's a piglin mob head on top of a note block. It has a heart above its head, and on the side of the note block, there's an item frame with a lava bucket in it. Next to the note block, there's a piglin brute standing in the lava next to a pillar of lava."},"tile_size":"1x1","title":"Minecraft 1.19.3 Pre-Release 2"},"articleLang":"en-us","primary_category":"News","categories":["News","Minecraft"],"article_url":"/en-us/article/minecraft-1-19-3-pre-release-1","publish_date":"23 November 2022 16:50:24 UTC","tags":["minecraft:stockholm/news","minecraft:stockholm/minecraft"]},{"default_tile":{"sub_header":"Holiday presents and treats for Minecrafters","image":{"content_type":"image","imageURL":"/content/dam/games/minecraft/merch/HGG2022_tile.png"},"tile_size":"1x1","title":"A festive gift guide 2022 "},"articleLang":"en-us","primary_category":"Merch","categories":["Merch"],"article_url":"/en-us/article/a-festive-gift-guide-2022","publish_date":"02 December 2022 08:56:52 UTC","tags":["minecraft:stockholm/merch"]}],"article_count":967} \ No newline at end of file diff --git a/test/data/RPMLauncher-Logo.png b/test/data/RPMLauncher-Logo.png deleted file mode 100644 index b99ffe915..000000000 Binary files a/test/data/RPMLauncher-Logo.png and /dev/null differ diff --git a/test/data/RPMTW-Update-Mod-Fabric-1.18.1-1.3.1.jar b/test/data/RPMTW-Update-Mod-Fabric-1.18.1-1.3.1.jar deleted file mode 100644 index 02dd5a919..000000000 Binary files a/test/data/RPMTW-Update-Mod-Fabric-1.18.1-1.3.1.jar and /dev/null differ diff --git a/test/data/rd-132211_version_meta.json b/test/data/rd-132211_version_meta.json new file mode 100644 index 000000000..839989272 --- /dev/null +++ b/test/data/rd-132211_version_meta.json @@ -0,0 +1 @@ +{"assetIndex": {"id": "pre-1.6", "sha1": "3d8e55480977e32acd9844e545177e69a52f594b", "size": 74091, "totalSize": 49505710, "url": "https://launchermeta.mojang.com/v1/packages/3d8e55480977e32acd9844e545177e69a52f594b/pre-1.6.json"}, "assets": "pre-1.6", "complianceLevel": 0, "downloads": {"client": {"sha1": "393e8d4b4d708587e2accd7c5221db65365e1075", "size": 26704, "url": "https://launcher.mojang.com/v1/objects/393e8d4b4d708587e2accd7c5221db65365e1075/client.jar"}}, "id": "rd-132211", "javaVersion": {"component": "jre-legacy", "majorVersion": 8}, "libraries": [{"downloads": {"artifact": {"path": "net/minecraft/launchwrapper/1.6/launchwrapper-1.6.jar", "sha1": "4ea0aca9c022a234ebaf14b51fb119055955fc9d", "size": 27583, "url": "https://libraries.minecraft.net/net/minecraft/launchwrapper/1.6/launchwrapper-1.6.jar"}}, "name": "net.minecraft:launchwrapper:1.6"}, {"downloads": {"artifact": {"path": "net/sf/jopt-simple/jopt-simple/4.5/jopt-simple-4.5.jar", "sha1": "6065cc95c661255349c1d0756657be17c29a4fd3", "size": 61311, "url": "https://libraries.minecraft.net/net/sf/jopt-simple/jopt-simple/4.5/jopt-simple-4.5.jar"}}, "name": "net.sf.jopt-simple:jopt-simple:4.5"}, {"downloads": {"artifact": {"path": "org/ow2/asm/asm-all/4.1/asm-all-4.1.jar", "sha1": "054986e962b88d8660ae4566475658469595ef58", "size": 214592, "url": "https://libraries.minecraft.net/org/ow2/asm/asm-all/4.1/asm-all-4.1.jar"}}, "name": "org.ow2.asm:asm-all:4.1"}, {"downloads": {"artifact": {"path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar", "sha1": "39c7796b469a600f72380316f6b1f11db6c2c7c4", "size": 208338, "url": "https://libraries.minecraft.net/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar"}}, "name": "net.java.jinput:jinput:2.0.5"}, {"downloads": {"artifact": {"path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar", "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6", "size": 7508, "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar"}}, "name": "net.java.jutils:jutils:1.0.0"}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/lwjgl/2.9.0/lwjgl-2.9.0.jar", "sha1": "5654d06e61a1bba7ae1e7f5233e1106be64c91cd", "size": 994633, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.0/lwjgl-2.9.0.jar"}}, "name": "org.lwjgl.lwjgl:lwjgl:2.9.0", "rules": [{"action": "allow"}, {"action": "disallow", "os": {"name": "osx", "version": "^10\\.5\\.\\d$"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/lwjgl_util/2.9.0/lwjgl_util-2.9.0.jar", "sha1": "a778846b64008fc7f48ead2377f034e547991699", "size": 173360, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.0/lwjgl_util-2.9.0.jar"}}, "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.0", "rules": [{"action": "allow"}, {"action": "disallow", "os": {"name": "osx", "version": "^10\\.5\\.\\d$"}}]}, {"downloads": {"classifiers": {"natives-linux": {"path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.0/lwjgl-platform-2.9.0-natives-linux.jar", "sha1": "2ba5dcb11048147f1a74eff2deb192c001321f77", "size": 569061, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.0/lwjgl-platform-2.9.0-natives-linux.jar"}, "natives-osx": {"path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.0/lwjgl-platform-2.9.0-natives-osx.jar", "sha1": "6621b382cb14cc409b041d8d72829156a87c31aa", "size": 518924, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.0/lwjgl-platform-2.9.0-natives-osx.jar"}, "natives-windows": {"path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.0/lwjgl-platform-2.9.0-natives-windows.jar", "sha1": "3f11873dc8e84c854ec7c5a8fd2e869f8aaef764", "size": 609967, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.0/lwjgl-platform-2.9.0-natives-windows.jar"}}}, "extract": {"exclude": ["META-INF/"]}, "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.0", "natives": {"linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows"}, "rules": [{"action": "allow"}, {"action": "disallow", "os": {"name": "osx", "version": "^10\\.5\\.\\d$"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/lwjgl/2.9.1-nightly-20130708-debug3/lwjgl-2.9.1-nightly-20130708-debug3.jar", "sha1": "884511652c756fac16b37236f863f346bd1ea121", "size": 996625, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.1-nightly-20130708-debug3/lwjgl-2.9.1-nightly-20130708-debug3.jar"}}, "name": "org.lwjgl.lwjgl:lwjgl:2.9.1-nightly-20130708-debug3", "rules": [{"action": "allow", "os": {"name": "osx", "version": "^10\\.5\\.\\d$"}}]}, {"downloads": {"artifact": {"path": "org/lwjgl/lwjgl/lwjgl_util/2.9.1-nightly-20130708-debug3/lwjgl_util-2.9.1-nightly-20130708-debug3.jar", "sha1": "fb693ba4e22a85432a32e8a048893dc7a92f42ac", "size": 173338, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.1-nightly-20130708-debug3/lwjgl_util-2.9.1-nightly-20130708-debug3.jar"}}, "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.1-nightly-20130708-debug3", "rules": [{"action": "allow", "os": {"name": "osx", "version": "^10\\.5\\.\\d$"}}]}, {"downloads": {"classifiers": {"natives-osx": {"path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.1-nightly-20130708-debug3/lwjgl-platform-2.9.1-nightly-20130708-debug3-natives-osx.jar", "sha1": "a9b83ad85742cad09c3574a91b0423bac3f7a0f5", "size": 458181, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.1-nightly-20130708-debug3/lwjgl-platform-2.9.1-nightly-20130708-debug3-natives-osx.jar"}}}, "extract": {"exclude": ["META-INF/"]}, "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.1-nightly-20130708-debug3", "natives": {"linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows"}, "rules": [{"action": "allow", "os": {"name": "osx", "version": "^10\\.5\\.\\d$"}}]}, {"downloads": {"classifiers": {"natives-linux": {"path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", "sha1": "7ff832a6eb9ab6a767f1ade2b548092d0fa64795", "size": 10362, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar"}, "natives-osx": {"path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar", "sha1": "53f9c919f34d2ca9de8c51fc4e1e8282029a9232", "size": 12186, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar"}, "natives-windows": {"path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar", "sha1": "385ee093e01f587f30ee1c8a2ee7d408fd732e16", "size": 155179, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar"}}}, "extract": {"exclude": ["META-INF/"]}, "name": "net.java.jinput:jinput-platform:2.0.5", "natives": {"linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows"}}, {"downloads": {"classifiers": {"natives-linux": {"path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", "sha1": "7ff832a6eb9ab6a767f1ade2b548092d0fa64795", "size": 10362, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar"}, "natives-osx": {"path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar", "sha1": "53f9c919f34d2ca9de8c51fc4e1e8282029a9232", "size": 12186, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar"}, "natives-windows": {"path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar", "sha1": "385ee093e01f587f30ee1c8a2ee7d408fd732e16", "size": 155179, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar"}}}, "extract": {"exclude": ["META-INF/"]}, "name": "net.java.jinput:jinput-platform:2.0.5", "natives": {"linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows"}}], "mainClass": "com.mojang.rubydung.RubyDung", "minecraftArguments": "${auth_player_name} ${auth_session}", "minimumLauncherVersion": 7, "releaseTime": "2009-05-13T20:11:00+00:00", "time": "2009-05-13T20:11:00+00:00", "type": "old_alpha"} \ No newline at end of file diff --git a/test/data/version_manifest_v2.json b/test/data/version_manifest_v2.json new file mode 100644 index 000000000..5d00ff451 --- /dev/null +++ b/test/data/version_manifest_v2.json @@ -0,0 +1 @@ +{"latest": {"release": "1.19.3", "snapshot": "1.19.3"}, "versions": [{"id": "1.19.3", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/6607feafdb2f96baad9314f207277730421a8e76/1.19.3.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-12-07T08:17:18+00:00", "sha1": "6607feafdb2f96baad9314f207277730421a8e76", "complianceLevel": 1}, {"id": "1.19.3-rc3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/3cee07d5dbdf81832a05613987fefdecae2eb37b/1.19.3-rc3.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-12-06T10:24:01+00:00", "sha1": "3cee07d5dbdf81832a05613987fefdecae2eb37b", "complianceLevel": 1}, {"id": "1.19.3-rc2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/1444fdf3b0e4c4891cb6d13b462ac2c72fa44afd/1.19.3-rc2.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-12-05T13:21:34+00:00", "sha1": "1444fdf3b0e4c4891cb6d13b462ac2c72fa44afd", "complianceLevel": 1}, {"id": "1.19.3-rc1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/d0ef907403bc461b20c2a1586f660be973622449/1.19.3-rc1.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-12-01T13:45:18+00:00", "sha1": "d0ef907403bc461b20c2a1586f660be973622449", "complianceLevel": 1}, {"id": "1.19.3-pre3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/745541dc2e9e17a5e3dd924e783d9c1b989f87b7/1.19.3-pre3.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-11-29T14:28:08+00:00", "sha1": "745541dc2e9e17a5e3dd924e783d9c1b989f87b7", "complianceLevel": 1}, {"id": "1.19.3-pre2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/41e4f317ba1c07c6329bb87f1baca6863bb4f7b8/1.19.3-pre2.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-11-23T16:12:25+00:00", "sha1": "41e4f317ba1c07c6329bb87f1baca6863bb4f7b8", "complianceLevel": 1}, {"id": "1.19.3-pre1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/8f1a8f2df99727b7fc83404bf02994fbb506881f/1.19.3-pre1.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-11-22T13:59:37+00:00", "sha1": "8f1a8f2df99727b7fc83404bf02994fbb506881f", "complianceLevel": 1}, {"id": "22w46a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/074d141f3aafde129705763d7b9578ea7a2ae371/22w46a.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-11-16T13:32:50+00:00", "sha1": "074d141f3aafde129705763d7b9578ea7a2ae371", "complianceLevel": 1}, {"id": "22w45a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/e56415a9215a7a891dd9b39de11368c25ee014e3/22w45a.json", "time": "2022-12-07T08:58:43+00:00", "releaseTime": "2022-11-09T14:30:16+00:00", "sha1": "e56415a9215a7a891dd9b39de11368c25ee014e3", "complianceLevel": 1}, {"id": "22w44a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/07c0c150c361d3ac67b003d97f6a5ff98818d74e/22w44a.json", "time": "2022-12-07T09:22:42+00:00", "releaseTime": "2022-11-02T13:15:43+00:00", "sha1": "07c0c150c361d3ac67b003d97f6a5ff98818d74e", "complianceLevel": 1}, {"id": "22w43a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/0fca847cf28d5caf18864ab7a31718094800abff/22w43a.json", "time": "2022-12-07T09:22:42+00:00", "releaseTime": "2022-10-26T11:55:59+00:00", "sha1": "0fca847cf28d5caf18864ab7a31718094800abff", "complianceLevel": 1}, {"id": "22w42a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/018825c6e5b95335397f399fee64671387818ae7/22w42a.json", "time": "2022-12-07T09:22:42+00:00", "releaseTime": "2022-10-19T09:34:22+00:00", "sha1": "018825c6e5b95335397f399fee64671387818ae7", "complianceLevel": 1}, {"id": "1.19.2", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/678862600e99991a2bf1d434af69ded3a321e22a/1.19.2.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-08-05T11:57:05+00:00", "sha1": "678862600e99991a2bf1d434af69ded3a321e22a", "complianceLevel": 1}, {"id": "1.19.2-rc2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/cff1d0316053d003311ccb9208cf4a97f962fb11/1.19.2-rc2.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-08-04T15:19:44+00:00", "sha1": "cff1d0316053d003311ccb9208cf4a97f962fb11", "complianceLevel": 1}, {"id": "1.19.2-rc1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/155bbd31c10338844fbe5a0cfbbe0d97eca25b16/1.19.2-rc1.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-08-04T10:07:26+00:00", "sha1": "155bbd31c10338844fbe5a0cfbbe0d97eca25b16", "complianceLevel": 1}, {"id": "1.19.1", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/24944abbf6446c6acb4d32598e590e82e087a4a9/1.19.1.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-07-27T09:25:33+00:00", "sha1": "24944abbf6446c6acb4d32598e590e82e087a4a9", "complianceLevel": 1}, {"id": "1.19.1-rc3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/4a9b9eca8c0abfe99f1d6feb355cbd1f55a5920b/1.19.1-rc3.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-07-26T15:34:35+00:00", "sha1": "4a9b9eca8c0abfe99f1d6feb355cbd1f55a5920b", "complianceLevel": 1}, {"id": "1.19.1-rc2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/ec383676070d95001f978296864f7226300f07cc/1.19.1-rc2.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-07-21T16:25:50+00:00", "sha1": "ec383676070d95001f978296864f7226300f07cc", "complianceLevel": 1}, {"id": "1.19.1-pre6", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/c8ab6514a7d630f911d64708bf42599231ff6723/1.19.1-pre6.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-07-20T15:49:31+00:00", "sha1": "c8ab6514a7d630f911d64708bf42599231ff6723", "complianceLevel": 1}, {"id": "1.19.1-pre5", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/23fe3969e406f47920a832c9ab4a757ddf9c70fd/1.19.1-pre5.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-07-15T11:51:44+00:00", "sha1": "23fe3969e406f47920a832c9ab4a757ddf9c70fd", "complianceLevel": 1}, {"id": "1.19.1-pre4", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/89258653c67f84dbaf4803c900b2fb7a92897913/1.19.1-pre4.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-07-08T11:41:59+00:00", "sha1": "89258653c67f84dbaf4803c900b2fb7a92897913", "complianceLevel": 1}, {"id": "1.19.1-pre3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/dbf193f6bc7551b31fdf6b4e82a77dac4cea4030/1.19.1-pre3.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-07-06T14:50:46+00:00", "sha1": "dbf193f6bc7551b31fdf6b4e82a77dac4cea4030", "complianceLevel": 1}, {"id": "1.19.1-pre2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/9ed36f1ee25a4d9516f46e6ca86c2897dfeafbd2/1.19.1-pre2.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-30T15:57:20+00:00", "sha1": "9ed36f1ee25a4d9516f46e6ca86c2897dfeafbd2", "complianceLevel": 1}, {"id": "1.19.1-rc1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/2de1779e4a6bff32820f79f138c13dee8228084e/1.19.1-rc1.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-23T16:32:41+00:00", "sha1": "2de1779e4a6bff32820f79f138c13dee8228084e", "complianceLevel": 1}, {"id": "1.19.1-pre1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/0388b8a85eabf2b357c4aff19bd8370678300785/1.19.1-pre1.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-21T17:13:59+00:00", "sha1": "0388b8a85eabf2b357c4aff19bd8370678300785", "complianceLevel": 1}, {"id": "22w24a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/98801d5d7e50bc1392ead00c3cebba53fde7ccd5/22w24a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-15T16:21:49+00:00", "sha1": "98801d5d7e50bc1392ead00c3cebba53fde7ccd5", "complianceLevel": 1}, {"id": "1.19", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/935698a4956915ffd2ecf2ad580663097a27337d/1.19.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-07T09:42:18+00:00", "sha1": "935698a4956915ffd2ecf2ad580663097a27337d", "complianceLevel": 1}, {"id": "1.19-rc2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/8086b28e334bcd022612008501d1f9f80ccb0f9c/1.19-rc2.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-03T11:47:25+00:00", "sha1": "8086b28e334bcd022612008501d1f9f80ccb0f9c", "complianceLevel": 1}, {"id": "1.19-rc1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/ab4d6500d7204432e149390669e31821271474f8/1.19-rc1.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-02T12:12:52+00:00", "sha1": "ab4d6500d7204432e149390669e31821271474f8", "complianceLevel": 1}, {"id": "1.19-pre5", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/110aefc6432b800455a4dbbc2264fbd73a3d9548/1.19-pre5.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-06-01T10:56:23+00:00", "sha1": "110aefc6432b800455a4dbbc2264fbd73a3d9548", "complianceLevel": 1}, {"id": "1.19-pre4", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/c20a0a969b978197e37507ad3c516d45e7a10b52/1.19-pre4.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-05-30T14:43:01+00:00", "sha1": "c20a0a969b978197e37507ad3c516d45e7a10b52", "complianceLevel": 1}, {"id": "1.19-pre3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/67fcf3fa377252b745ed7954d212a158511c68b0/1.19-pre3.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-05-25T09:56:47+00:00", "sha1": "67fcf3fa377252b745ed7954d212a158511c68b0", "complianceLevel": 1}, {"id": "1.19-pre2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/d863ab928ce785ef8f095125026356ab25982bf4/1.19-pre2.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-05-23T14:54:00+00:00", "sha1": "d863ab928ce785ef8f095125026356ab25982bf4", "complianceLevel": 1}, {"id": "1.19-pre1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/2eef3b459ffde0974c8bed1dbf787fd5bfe05828/1.19-pre1.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-05-18T13:51:54+00:00", "sha1": "2eef3b459ffde0974c8bed1dbf787fd5bfe05828", "complianceLevel": 1}, {"id": "22w19a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/10a1c5b02523485b23b6383d34c93643f6282038/22w19a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-05-12T15:36:11+00:00", "sha1": "10a1c5b02523485b23b6383d34c93643f6282038", "complianceLevel": 1}, {"id": "22w18a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/3cfb8fd06c1bb6089abe09df0cff61323b116a3b/22w18a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-05-04T14:41:35+00:00", "sha1": "3cfb8fd06c1bb6089abe09df0cff61323b116a3b", "complianceLevel": 1}, {"id": "22w17a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/2e7214f6d5777b63248c914103b3db3351870bd4/22w17a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-04-27T15:54:15+00:00", "sha1": "2e7214f6d5777b63248c914103b3db3351870bd4", "complianceLevel": 1}, {"id": "22w16b", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/d7aba3be8fe8bf41df27d2a677e565b39d5ae34e/22w16b.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-04-20T17:25:32+00:00", "sha1": "d7aba3be8fe8bf41df27d2a677e565b39d5ae34e", "complianceLevel": 1}, {"id": "22w16a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/26d168cb27b3300b9ff82eca48e7f831eee9fc55/22w16a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-04-20T14:37:07+00:00", "sha1": "26d168cb27b3300b9ff82eca48e7f831eee9fc55", "complianceLevel": 1}, {"id": "22w15a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/cec34b7c6336773bbfe47b66e51b3f8b2c950447/22w15a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-04-13T15:41:17+00:00", "sha1": "cec34b7c6336773bbfe47b66e51b3f8b2c950447", "complianceLevel": 1}, {"id": "22w14a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/5d909e59acfbba71134f935d809cede5086ce159/22w14a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-04-06T13:37:12+00:00", "sha1": "5d909e59acfbba71134f935d809cede5086ce159", "complianceLevel": 1}, {"id": "22w13oneblockatatime", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/f94d05420f7c42a3a29c34c43bc148862dafdc36/22w13oneblockatatime.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-04-01T11:56:58+00:00", "sha1": "f94d05420f7c42a3a29c34c43bc148862dafdc36", "complianceLevel": 1}, {"id": "22w13a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/f72840cd32829f77b114bf7ace991f29608e4bfa/22w13a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-03-31T14:53:25+00:00", "sha1": "f72840cd32829f77b114bf7ace991f29608e4bfa", "complianceLevel": 1}, {"id": "22w12a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/daf286d1053e0fa927c2ac548d676b513e93ee8c/22w12a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-03-24T16:15:02+00:00", "sha1": "daf286d1053e0fa927c2ac548d676b513e93ee8c", "complianceLevel": 1}, {"id": "22w11a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/77bcd699764ed4d0b13fcc775a2ce765a022e83f/22w11a.json", "time": "2022-09-13T14:29:56+00:00", "releaseTime": "2022-03-16T15:55:38+00:00", "sha1": "77bcd699764ed4d0b13fcc775a2ce765a022e83f", "complianceLevel": 1}, {"id": "1.18.2", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/23b6e0e0d0f87da36075a4290cd98df1a76e2415/1.18.2.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-28T10:42:45+00:00", "sha1": "23b6e0e0d0f87da36075a4290cd98df1a76e2415", "complianceLevel": 1}, {"id": "1.18.2-rc1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/04d0d1e9b1f46b904dbbe10d2aa629232b104e3e/1.18.2-rc1.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-25T13:25:40+00:00", "sha1": "04d0d1e9b1f46b904dbbe10d2aa629232b104e3e", "complianceLevel": 1}, {"id": "1.18.2-pre3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/ecc76e608a0f8ccacb58324762a6201e912010ff/1.18.2-pre3.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-23T15:23:12+00:00", "sha1": "ecc76e608a0f8ccacb58324762a6201e912010ff", "complianceLevel": 1}, {"id": "1.18.2-pre2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/51e018a95c51eb335cc0e425b70179af6fa96ce4/1.18.2-pre2.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-21T15:26:19+00:00", "sha1": "51e018a95c51eb335cc0e425b70179af6fa96ce4", "complianceLevel": 1}, {"id": "1.18.2-pre1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/081702b870ff53328de8a838992d81b88ea40907/1.18.2-pre1.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-18T16:00:32+00:00", "sha1": "081702b870ff53328de8a838992d81b88ea40907", "complianceLevel": 1}, {"id": "22w07a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/816201dcefada6d185620f0e56507f00d666bbfa/22w07a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-16T16:13:58+00:00", "sha1": "816201dcefada6d185620f0e56507f00d666bbfa", "complianceLevel": 1}, {"id": "22w06a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/e8e75829ead3d6dd246c31976f91d4976e1f2402/22w06a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-09T16:47:48+00:00", "sha1": "e8e75829ead3d6dd246c31976f91d4976e1f2402", "complianceLevel": 1}, {"id": "22w05a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/a4088ba532aff354d9435ad0e0d93bd1f0abb1e8/22w05a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-02-02T16:08:39+00:00", "sha1": "a4088ba532aff354d9435ad0e0d93bd1f0abb1e8", "complianceLevel": 1}, {"id": "22w03a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/b5a2c75230d132ae2e61d9a6088506204ef829f7/22w03a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2022-01-19T16:04:59+00:00", "sha1": "b5a2c75230d132ae2e61d9a6088506204ef829f7", "complianceLevel": 1}, {"id": "1.18.1", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/989549ecba162cba2ec066e19a2f0364586f18bf/1.18.1.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-12-10T08:23:00+00:00", "sha1": "989549ecba162cba2ec066e19a2f0364586f18bf", "complianceLevel": 1}, {"id": "1.18.1-rc3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/373b0531c62011ea2ab1b42ba49f87a460d5edff/1.18.1-rc3.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-12-10T03:36:38+00:00", "sha1": "373b0531c62011ea2ab1b42ba49f87a460d5edff", "complianceLevel": 1}, {"id": "1.18.1-rc2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/42fe8387afaed32ba28598bfecac7fc14df928a7/1.18.1-rc2.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-12-08T12:29:36+00:00", "sha1": "42fe8387afaed32ba28598bfecac7fc14df928a7", "complianceLevel": 1}, {"id": "1.18.1-rc1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/cb80a28590126e508b7ce0211c40a947efa808f6/1.18.1-rc1.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-12-07T15:52:47+00:00", "sha1": "cb80a28590126e508b7ce0211c40a947efa808f6", "complianceLevel": 1}, {"id": "1.18.1-pre1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/3982dfbe153e0bd2e45ff7c7828b38385daa77a5/1.18.1-pre1.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-12-03T13:45:38+00:00", "sha1": "3982dfbe153e0bd2e45ff7c7828b38385daa77a5", "complianceLevel": 1}, {"id": "1.18", "type": "release", "url": "https://piston-meta.mojang.com/v1/packages/2a5ba362c7743d19c474faa9ba3595d9bc41fa02/1.18.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-30T09:16:29+00:00", "sha1": "2a5ba362c7743d19c474faa9ba3595d9bc41fa02", "complianceLevel": 1}, {"id": "1.18-rc4", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/e0eebc7178fc96b9e4ff84ac9b1a47a2b276a067/1.18-rc4.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-29T13:43:42+00:00", "sha1": "e0eebc7178fc96b9e4ff84ac9b1a47a2b276a067", "complianceLevel": 1}, {"id": "1.18-rc3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/2cb6fc426a0cc3d62fb844126d908f9d20519690/1.18-rc3.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-26T15:51:56+00:00", "sha1": "2cb6fc426a0cc3d62fb844126d908f9d20519690", "complianceLevel": 1}, {"id": "1.18-rc2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/44aeb45a977f9ddb9450e34a37e04caa0591e662/1.18-rc2.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-26T10:02:04+00:00", "sha1": "44aeb45a977f9ddb9450e34a37e04caa0591e662", "complianceLevel": 1}, {"id": "1.18-rc1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/3dd8097afc4220d5b7b0966ee50c6aa97ecebae1/1.18-rc1.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-25T14:28:49+00:00", "sha1": "3dd8097afc4220d5b7b0966ee50c6aa97ecebae1", "complianceLevel": 1}, {"id": "1.18-pre8", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/3b966017f2ebdfb5c72fe1449732558af8f815aa/1.18-pre8.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-24T14:57:32+00:00", "sha1": "3b966017f2ebdfb5c72fe1449732558af8f815aa", "complianceLevel": 1}, {"id": "1.18-pre7", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/bd769b95bab9c40914e316f2d645858e2cdfa07b/1.18-pre7.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-23T16:37:41+00:00", "sha1": "bd769b95bab9c40914e316f2d645858e2cdfa07b", "complianceLevel": 1}, {"id": "1.18-pre6", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/45e29a5a739403b46b47cfdf7b485179f7bf2d5a/1.18-pre6.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-22T17:09:05+00:00", "sha1": "45e29a5a739403b46b47cfdf7b485179f7bf2d5a", "complianceLevel": 1}, {"id": "1.18-pre5", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/8c59a40d41e05c53c321b7454905ed27a409f539/1.18-pre5.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-19T15:47:09+00:00", "sha1": "8c59a40d41e05c53c321b7454905ed27a409f539", "complianceLevel": 1}, {"id": "1.18-pre4", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/6dbbac05fd0088c5ab3db2411d49d2408e39d72f/1.18-pre4.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-17T18:07:56+00:00", "sha1": "6dbbac05fd0088c5ab3db2411d49d2408e39d72f", "complianceLevel": 1}, {"id": "1.18-pre3", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/433e3d1f0150c81b5764a7346d173c2b9ba4e688/1.18-pre3.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-17T16:04:25+00:00", "sha1": "433e3d1f0150c81b5764a7346d173c2b9ba4e688", "complianceLevel": 1}, {"id": "1.18-pre2", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/1179725d80c57492ef5ea265b38211e305037369/1.18-pre2.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-16T17:04:48+00:00", "sha1": "1179725d80c57492ef5ea265b38211e305037369", "complianceLevel": 1}, {"id": "1.18-pre1", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/13fb60db552bb290c36ca87dc9f8a227c4438386/1.18-pre1.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-11T16:14:06+00:00", "sha1": "13fb60db552bb290c36ca87dc9f8a227c4438386", "complianceLevel": 1}, {"id": "21w44a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/a9509793df625c4735f96de3239d4df94ae5d914/21w44a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-11-03T16:14:34+00:00", "sha1": "a9509793df625c4735f96de3239d4df94ae5d914", "complianceLevel": 1}, {"id": "21w43a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/4790d021544c177d3d6d18cbdf500498770daad3/21w43a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-10-27T14:38:55+00:00", "sha1": "4790d021544c177d3d6d18cbdf500498770daad3", "complianceLevel": 1}, {"id": "21w42a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/766c139a3bf978f4f11efffc1a2283e3b990a063/21w42a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-10-20T12:41:25+00:00", "sha1": "766c139a3bf978f4f11efffc1a2283e3b990a063", "complianceLevel": 1}, {"id": "21w41a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/1c3476a8a4018d4e01b33f742c4ff2e219b92136/21w41a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-10-13T15:23:23+00:00", "sha1": "1c3476a8a4018d4e01b33f742c4ff2e219b92136", "complianceLevel": 1}, {"id": "21w40a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/76ed423e8fa780185343afb0bfc7cb048e785e79/21w40a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-10-07T11:17:50+00:00", "sha1": "76ed423e8fa780185343afb0bfc7cb048e785e79", "complianceLevel": 1}, {"id": "21w39a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/6a2d23d0e104b5df4c7efb42f6e2647e223ec329/21w39a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-09-29T16:27:05+00:00", "sha1": "6a2d23d0e104b5df4c7efb42f6e2647e223ec329", "complianceLevel": 1}, {"id": "21w38a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/75b1445a842995461114d1a0716eb3f55d150b00/21w38a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-09-23T14:36:06+00:00", "sha1": "75b1445a842995461114d1a0716eb3f55d150b00", "complianceLevel": 1}, {"id": "21w37a", "type": "snapshot", "url": "https://piston-meta.mojang.com/v1/packages/fd0b26610a49eb38af3fc8c1eca424cf05d108f6/21w37a.json", "time": "2022-09-19T15:11:09+00:00", "releaseTime": "2021-09-15T16:04:30+00:00", "sha1": "fd0b26610a49eb38af3fc8c1eca424cf05d108f6", "complianceLevel": 1}, {"id": "1.17.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/a769e66272ae058c60c27a11b5adb04d7065884a/1.17.1.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-07-06T12:01:34+00:00", "sha1": "a769e66272ae058c60c27a11b5adb04d7065884a", "complianceLevel": 1}, {"id": "1.17.1-rc2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/82b5b1aa011ffd8e4843f0261f33cf7fa3a1ea65/1.17.1-rc2.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-07-05T12:58:01+00:00", "sha1": "82b5b1aa011ffd8e4843f0261f33cf7fa3a1ea65", "complianceLevel": 1}, {"id": "1.17.1-rc1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/537405cef3f0dfc57275468e3e1aa4ed2578ef80/1.17.1-rc1.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-07-01T15:23:37+00:00", "sha1": "537405cef3f0dfc57275468e3e1aa4ed2578ef80", "complianceLevel": 1}, {"id": "1.17.1-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/be7f223eb5a6e60fb63bc7affd81905bf3ed3d37/1.17.1-pre3.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-30T15:43:16+00:00", "sha1": "be7f223eb5a6e60fb63bc7affd81905bf3ed3d37", "complianceLevel": 1}, {"id": "1.17.1-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9db3da53e72d08571d5310a6ac21fa6cbd25ac1d/1.17.1-pre2.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-29T15:14:12+00:00", "sha1": "9db3da53e72d08571d5310a6ac21fa6cbd25ac1d", "complianceLevel": 1}, {"id": "1.17.1-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9b4a96ebaad2373bd8ff1062f0482065293af40b/1.17.1-pre1.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-18T12:24:40+00:00", "sha1": "9b4a96ebaad2373bd8ff1062f0482065293af40b", "complianceLevel": 1}, {"id": "1.17", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/64b06c2837c64cc658d33f05a7d7ae14191fd47e/1.17.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-08T11:00:40+00:00", "sha1": "64b06c2837c64cc658d33f05a7d7ae14191fd47e", "complianceLevel": 1}, {"id": "1.17-rc2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f4b7731d0987aa6b389fa3fee99bbc834bfe0a30/1.17-rc2.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-07T11:46:28+00:00", "sha1": "f4b7731d0987aa6b389fa3fee99bbc834bfe0a30", "complianceLevel": 1}, {"id": "1.17-rc1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/76b86acdc7a9162e228ffbb93f70199c26d4e037/1.17-rc1.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-04T13:24:48+00:00", "sha1": "76b86acdc7a9162e228ffbb93f70199c26d4e037", "complianceLevel": 1}, {"id": "1.17-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2e8e7e939bd1c55e7ff9b125e21c6ea25a11c2e0/1.17-pre5.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-03T17:01:28+00:00", "sha1": "2e8e7e939bd1c55e7ff9b125e21c6ea25a11c2e0", "complianceLevel": 1}, {"id": "1.17-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a793118489874d50fe5df1ec3cb6cbde78535d33/1.17-pre4.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-02T16:15:43+00:00", "sha1": "a793118489874d50fe5df1ec3cb6cbde78535d33", "complianceLevel": 1}, {"id": "1.17-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2178ae08d023cd27f427108b383c67ffd4bfb8d7/1.17-pre3.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-06-01T15:43:46+00:00", "sha1": "2178ae08d023cd27f427108b383c67ffd4bfb8d7", "complianceLevel": 1}, {"id": "1.17-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7d3080125ea49e0f1bc4283fa5f9ac7737650220/1.17-pre2.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-05-31T15:54:05+00:00", "sha1": "7d3080125ea49e0f1bc4283fa5f9ac7737650220", "complianceLevel": 1}, {"id": "1.17-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e95522481c142c41a1b2f3c30fa3dbbd35020992/1.17-pre1.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-05-27T09:39:21+00:00", "sha1": "e95522481c142c41a1b2f3c30fa3dbbd35020992", "complianceLevel": 1}, {"id": "21w20a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ecbda211fa62d8e972b15b2604425bf9882de4bc/21w20a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-05-19T15:22:02+00:00", "sha1": "ecbda211fa62d8e972b15b2604425bf9882de4bc", "complianceLevel": 1}, {"id": "21w19a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/749d7b651e5421443c8f4633295a663b343fff98/21w19a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-05-12T11:19:15+00:00", "sha1": "749d7b651e5421443c8f4633295a663b343fff98", "complianceLevel": 1}, {"id": "21w18a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e9cad4bb27cc54eee549ca2777d3ee57fa72165f/21w18a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-05-05T15:24:35+00:00", "sha1": "e9cad4bb27cc54eee549ca2777d3ee57fa72165f", "complianceLevel": 1}, {"id": "21w17a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5b001d645468cb41a3fa58c7c63e193a87ba6ef9/21w17a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-04-28T13:54:05+00:00", "sha1": "5b001d645468cb41a3fa58c7c63e193a87ba6ef9", "complianceLevel": 1}, {"id": "21w16a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f29d548a02eaa94e91986433336373c01654427f/21w16a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-04-21T16:41:14+00:00", "sha1": "f29d548a02eaa94e91986433336373c01654427f", "complianceLevel": 1}, {"id": "21w15a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fa5a175d64f06b7cb6d4cc6bed19554515bcc061/21w15a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-04-14T13:41:34+00:00", "sha1": "fa5a175d64f06b7cb6d4cc6bed19554515bcc061", "complianceLevel": 1}, {"id": "21w14a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c26b96e42ffb24d56ba15a6bcdc9b9664ec01ae2/21w14a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-04-07T14:04:09+00:00", "sha1": "c26b96e42ffb24d56ba15a6bcdc9b9664ec01ae2", "complianceLevel": 1}, {"id": "21w13a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6162315993762d0df359ba8b7949d282817238d5/21w13a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-03-31T16:17:46+00:00", "sha1": "6162315993762d0df359ba8b7949d282817238d5", "complianceLevel": 1}, {"id": "21w11a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/841d40809099b5e57aa3a7e5f955c999e1dfd440/21w11a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-03-17T15:05:50+00:00", "sha1": "841d40809099b5e57aa3a7e5f955c999e1dfd440", "complianceLevel": 1}, {"id": "21w10a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1b1e310213dce4b979e13ed2f9647b5dbe72970a/21w10a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-03-10T15:24:38+00:00", "sha1": "1b1e310213dce4b979e13ed2f9647b5dbe72970a", "complianceLevel": 1}, {"id": "21w08b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ecc0a0bc426f23539806a2e4d3c99f1825e9bb9f/21w08b.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-02-25T11:46:34+00:00", "sha1": "ecc0a0bc426f23539806a2e4d3c99f1825e9bb9f", "complianceLevel": 1}, {"id": "21w08a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b6d06bf2779a4dbe40ffce5df8d0d9376f42ee1a/21w08a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-02-24T14:38:51+00:00", "sha1": "b6d06bf2779a4dbe40ffce5df8d0d9376f42ee1a", "complianceLevel": 1}, {"id": "21w07a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/86a5a82b1f89c21f138970991a007a6717597eae/21w07a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-02-17T16:35:40+00:00", "sha1": "86a5a82b1f89c21f138970991a007a6717597eae", "complianceLevel": 1}, {"id": "21w06a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4f3d809d1de4fe6743bcd428146908cfad87b9ea/21w06a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-02-10T17:13:54+00:00", "sha1": "4f3d809d1de4fe6743bcd428146908cfad87b9ea", "complianceLevel": 1}, {"id": "21w05b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/984168a87e50aeefd01c90c45ac59eb0ce34d81e/21w05b.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-02-04T15:09:29+00:00", "sha1": "984168a87e50aeefd01c90c45ac59eb0ce34d81e", "complianceLevel": 1}, {"id": "21w05a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/29209c95d03403052c1ce07db170fac0d032f556/21w05a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-02-03T15:56:54+00:00", "sha1": "29209c95d03403052c1ce07db170fac0d032f556", "complianceLevel": 1}, {"id": "21w03a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/adecbead882fbcdb3f7ec7410e8e825aa8685b8f/21w03a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2021-01-20T14:56:29+00:00", "sha1": "adecbead882fbcdb3f7ec7410e8e825aa8685b8f", "complianceLevel": 1}, {"id": "1.16.5", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/95af6e50cd04f06f65c76e4a62237504387e5480/1.16.5.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2021-01-14T16:05:32+00:00", "sha1": "95af6e50cd04f06f65c76e4a62237504387e5480", "complianceLevel": 1}, {"id": "1.16.5-rc1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ec36dacfce7475bc0383bcb3b4bcb07764238b18/1.16.5-rc1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2021-01-13T15:58:55+00:00", "sha1": "ec36dacfce7475bc0383bcb3b4bcb07764238b18", "complianceLevel": 1}, {"id": "20w51a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4e737566880bbb229dfb1d90f7efcde60315ce19/20w51a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2020-12-16T16:27:57+00:00", "sha1": "4e737566880bbb229dfb1d90f7efcde60315ce19", "complianceLevel": 1}, {"id": "20w49a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/165339ba00adad2c7cd4a08e0c8936c608135331/20w49a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2020-12-02T16:47:20+00:00", "sha1": "165339ba00adad2c7cd4a08e0c8936c608135331", "complianceLevel": 1}, {"id": "20w48a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e338789b007c9d95715b5fd385be36a36be544d8/20w48a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2020-11-25T15:42:24+00:00", "sha1": "e338789b007c9d95715b5fd385be36a36be544d8", "complianceLevel": 1}, {"id": "20w46a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6cdf0446ae0e00ddf3ddad424a2c019bb394cf59/20w46a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2020-11-11T15:30:32+00:00", "sha1": "6cdf0446ae0e00ddf3ddad424a2c019bb394cf59", "complianceLevel": 1}, {"id": "20w45a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8b094645c5c85c4967c632d50d4b2cda080847b0/20w45a.json", "time": "2022-06-07T08:08:11+00:00", "releaseTime": "2020-11-04T16:42:00+00:00", "sha1": "8b094645c5c85c4967c632d50d4b2cda080847b0", "complianceLevel": 1}, {"id": "1.16.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/893c540ffde53ffad693695a7aafb8a1d7478894/1.16.4.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-10-29T15:49:37+00:00", "sha1": "893c540ffde53ffad693695a7aafb8a1d7478894", "complianceLevel": 1}, {"id": "1.16.4-rc1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a3de339916d2035a33e82e830637635c5955ea83/1.16.4-rc1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-10-27T16:31:08+00:00", "sha1": "a3de339916d2035a33e82e830637635c5955ea83", "complianceLevel": 1}, {"id": "1.16.4-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9f4a53343fb0b0e6bfaaa3aaabef928e3faed9d3/1.16.4-pre2.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-10-22T15:32:17+00:00", "sha1": "9f4a53343fb0b0e6bfaaa3aaabef928e3faed9d3", "complianceLevel": 0}, {"id": "1.16.4-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2209e3ddec0d68828c09dbe373baaed8e07aee99/1.16.4-pre1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-10-13T14:36:07+00:00", "sha1": "2209e3ddec0d68828c09dbe373baaed8e07aee99", "complianceLevel": 0}, {"id": "1.16.3", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/54ad06cd4c7c1205d05c28e7f4de22d166779d47/1.16.3.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-09-10T13:42:37+00:00", "sha1": "54ad06cd4c7c1205d05c28e7f4de22d166779d47", "complianceLevel": 0}, {"id": "1.16.3-rc1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/80962ff8d66d9e6be8ae71352b2ec8aa899a9d7b/1.16.3-rc1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-09-07T12:34:06+00:00", "sha1": "80962ff8d66d9e6be8ae71352b2ec8aa899a9d7b", "complianceLevel": 0}, {"id": "1.16.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/a5cd0a3e52f38c9fb713010b07f7ae89e183b0ff/1.16.2.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-08-11T10:13:46+00:00", "sha1": "a5cd0a3e52f38c9fb713010b07f7ae89e183b0ff", "complianceLevel": 0}, {"id": "1.16.2-rc2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0bea810bb372c8f44f2946b98288e298c48edd4d/1.16.2-rc2.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-08-10T11:43:36+00:00", "sha1": "0bea810bb372c8f44f2946b98288e298c48edd4d", "complianceLevel": 0}, {"id": "1.16.2-rc1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/162e8f04e5d0d12d7944d20735666e9cc34639d5/1.16.2-rc1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-08-07T14:35:39+00:00", "sha1": "162e8f04e5d0d12d7944d20735666e9cc34639d5", "complianceLevel": 0}, {"id": "1.16.2-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ce60958868cd0b7e8ec69b0d3df5fe900fbf41a1/1.16.2-pre3.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-08-06T16:44:52+00:00", "sha1": "ce60958868cd0b7e8ec69b0d3df5fe900fbf41a1", "complianceLevel": 0}, {"id": "1.16.2-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dc2b919fa002cb0be5366655c29db26588f2a2d8/1.16.2-pre2.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-08-05T15:30:50+00:00", "sha1": "dc2b919fa002cb0be5366655c29db26588f2a2d8", "complianceLevel": 0}, {"id": "1.16.2-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ef6bc76723754a865ee2f2da19dc50d6826a041b/1.16.2-pre1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-07-29T13:19:05+00:00", "sha1": "ef6bc76723754a865ee2f2da19dc50d6826a041b", "complianceLevel": 0}, {"id": "20w30a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9b7948cfd40175e7e885a7772754aef6bc850fd7/20w30a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-07-22T15:05:15+00:00", "sha1": "9b7948cfd40175e7e885a7772754aef6bc850fd7", "complianceLevel": 0}, {"id": "20w29a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a81fb95682024c16d5180c21db2044c522617ca4/20w29a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-07-15T14:13:47+00:00", "sha1": "a81fb95682024c16d5180c21db2044c522617ca4", "complianceLevel": 0}, {"id": "20w28a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1c059f22e88f87945c6626a25002debdbbdcc9c9/20w28a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-07-08T15:10:40+00:00", "sha1": "1c059f22e88f87945c6626a25002debdbbdcc9c9", "complianceLevel": 0}, {"id": "20w27a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/20b1926e127e2363eafd18cb4c15f0322ad926e2/20w27a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-07-01T15:07:35+00:00", "sha1": "20b1926e127e2363eafd18cb4c15f0322ad926e2", "complianceLevel": 0}, {"id": "1.16.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/db482626f80f0d3b30495948d3ed89a7c904c42e/1.16.1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-24T10:31:40+00:00", "sha1": "db482626f80f0d3b30495948d3ed89a7c904c42e", "complianceLevel": 0}, {"id": "1.16", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/56c19936e578db40b910835faefa2c2fcfc07628/1.16.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-23T16:20:52+00:00", "sha1": "56c19936e578db40b910835faefa2c2fcfc07628", "complianceLevel": 0}, {"id": "1.16-rc1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7b41975f529e8c8b683d383cb30ed0c73508cfd3/1.16-rc1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-18T12:49:28+00:00", "sha1": "7b41975f529e8c8b683d383cb30ed0c73508cfd3", "complianceLevel": 0}, {"id": "1.16-pre8", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/07794250d83f83ae02674ba2eef8191c666362f1/1.16-pre8.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-17T14:45:23+00:00", "sha1": "07794250d83f83ae02674ba2eef8191c666362f1", "complianceLevel": 0}, {"id": "1.16-pre7", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fd91aeab0b3c5e8c2592b8382030c84d3d2207ea/1.16-pre7.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-16T15:31:35+00:00", "sha1": "fd91aeab0b3c5e8c2592b8382030c84d3d2207ea", "complianceLevel": 0}, {"id": "1.16-pre6", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5e6392e56ab5e7abe7252429d12929977fce8160/1.16-pre6.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-15T16:57:57+00:00", "sha1": "5e6392e56ab5e7abe7252429d12929977fce8160", "complianceLevel": 0}, {"id": "1.16-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ff786c6b5a31cdeebb388da8737795272007c4d0/1.16-pre5.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-12T14:33:59+00:00", "sha1": "ff786c6b5a31cdeebb388da8737795272007c4d0", "complianceLevel": 0}, {"id": "1.16-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/af1397e9f10ec2801d997ff1f59baff721adccc0/1.16-pre4.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-11T15:45:55+00:00", "sha1": "af1397e9f10ec2801d997ff1f59baff721adccc0", "complianceLevel": 0}, {"id": "1.16-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/905f2d5b67d591829e35dddae3c4de858ade34d1/1.16-pre3.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-10T14:57:43+00:00", "sha1": "905f2d5b67d591829e35dddae3c4de858ade34d1", "complianceLevel": 0}, {"id": "1.16-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dc006b45efa5406b84d25947b00c5de30f307394/1.16-pre2.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-05T10:47:59+00:00", "sha1": "dc006b45efa5406b84d25947b00c5de30f307394", "complianceLevel": 0}, {"id": "1.16-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fca4a3ce929df68ab63b780c942ce678af64d3c7/1.16-pre1.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-06-04T18:17:51+00:00", "sha1": "fca4a3ce929df68ab63b780c942ce678af64d3c7", "complianceLevel": 0}, {"id": "20w22a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/07590ad3722bc903cf28507399c484723421c983/20w22a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-05-29T11:25:02+00:00", "sha1": "07590ad3722bc903cf28507399c484723421c983", "complianceLevel": 0}, {"id": "20w21a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e6f6c6a802bf47764dfded0bc961a9830e9cf559/20w21a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-05-20T12:07:18+00:00", "sha1": "e6f6c6a802bf47764dfded0bc961a9830e9cf559", "complianceLevel": 0}, {"id": "20w20b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8cf88a6c3c2d8cfd1c817e118d56bb2ecef0ae59/20w20b.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-05-14T08:16:26+00:00", "sha1": "8cf88a6c3c2d8cfd1c817e118d56bb2ecef0ae59", "complianceLevel": 0}, {"id": "20w20a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5047dd1cfbdffbdeca13727d0f86239c65f11a10/20w20a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-05-13T15:11:43+00:00", "sha1": "5047dd1cfbdffbdeca13727d0f86239c65f11a10", "complianceLevel": 0}, {"id": "20w19a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ef91eccad2dd0a7af63f06f654c16ed292ff16b2/20w19a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-05-06T16:23:24+00:00", "sha1": "ef91eccad2dd0a7af63f06f654c16ed292ff16b2", "complianceLevel": 0}, {"id": "20w18a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5a37f7cde062eb1db85e44e432e3139d9149b5cb/20w18a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-04-29T15:16:34+00:00", "sha1": "5a37f7cde062eb1db85e44e432e3139d9149b5cb", "complianceLevel": 0}, {"id": "20w17a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1c29e2acfaea79c595c9b30e1fb0a2345a0e0ec7/20w17a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-04-22T13:47:50+00:00", "sha1": "1c29e2acfaea79c595c9b30e1fb0a2345a0e0ec7", "complianceLevel": 0}, {"id": "20w16a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0b692abcf39fe11730efbc75298ae4c6cb164371/20w16a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-04-15T14:13:01+00:00", "sha1": "0b692abcf39fe11730efbc75298ae4c6cb164371", "complianceLevel": 0}, {"id": "20w15a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f41d2a37668b37267c057e0d870d2262639e3e48/20w15a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-04-08T12:29:24+00:00", "sha1": "f41d2a37668b37267c057e0d870d2262639e3e48", "complianceLevel": 0}, {"id": "20w14a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2a916b7368aa072faa10b26d577fe425ef9da6e6/20w14a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-04-02T14:28:06+00:00", "sha1": "2a916b7368aa072faa10b26d577fe425ef9da6e6", "complianceLevel": 0}, {"id": "20w14infinite", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/394eb84b4082b11470f82e9c889ad324727d98c2/20w14infinite.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-04-01T12:47:08+00:00", "sha1": "394eb84b4082b11470f82e9c889ad324727d98c2", "complianceLevel": 0}, {"id": "20w13b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e4c01be12f979826673cec20260e97b46cb18dc4/20w13b.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-03-26T13:00:34+00:00", "sha1": "e4c01be12f979826673cec20260e97b46cb18dc4", "complianceLevel": 0}, {"id": "20w13a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fb3b0899072407ef6c2bebbfa36196a8a4e693bf/20w13a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-03-25T17:05:33+00:00", "sha1": "fb3b0899072407ef6c2bebbfa36196a8a4e693bf", "complianceLevel": 0}, {"id": "20w12a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7458c85b2eb3953c324e34f09bc061461fdff782/20w12a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-03-18T16:42:06+00:00", "sha1": "7458c85b2eb3953c324e34f09bc061461fdff782", "complianceLevel": 0}, {"id": "20w11a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ce3bed61ccad2cc1d9caa4b7665665390adc54fd/20w11a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-03-11T16:28:27+00:00", "sha1": "ce3bed61ccad2cc1d9caa4b7665665390adc54fd", "complianceLevel": 0}, {"id": "20w10a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e40a6086411ec0b1df33ffd2241cdcc3c0bc5c68/20w10a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-03-04T16:21:41+00:00", "sha1": "e40a6086411ec0b1df33ffd2241cdcc3c0bc5c68", "complianceLevel": 0}, {"id": "20w09a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0eaa824535bbae9faed0f151527ef9e2c973320d/20w09a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-02-26T16:43:08+00:00", "sha1": "0eaa824535bbae9faed0f151527ef9e2c973320d", "complianceLevel": 0}, {"id": "20w08a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/27e45629bcdbb195f3c38efbbf61262b5553150d/20w08a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-02-19T13:30:09+00:00", "sha1": "27e45629bcdbb195f3c38efbbf61262b5553150d", "complianceLevel": 0}, {"id": "20w07a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a8bdc23d97c7abf3e1ab6745089861e28aeac056/20w07a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-02-14T13:20:49+00:00", "sha1": "a8bdc23d97c7abf3e1ab6745089861e28aeac056", "complianceLevel": 0}, {"id": "20w06a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ddf99edd0a9a9d79502b53e79d186bb718a9bbf6/20w06a.json", "time": "2022-02-25T13:15:31+00:00", "releaseTime": "2020-02-05T16:05:22+00:00", "sha1": "ddf99edd0a9a9d79502b53e79d186bb718a9bbf6", "complianceLevel": 0}, {"id": "1.15.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/a134a40902959810875d4642a4ac9c69c37e39a0/1.15.2.json", "time": "2021-12-15T15:43:59+00:00", "releaseTime": "2020-01-17T10:03:52+00:00", "sha1": "a134a40902959810875d4642a4ac9c69c37e39a0", "complianceLevel": 0}, {"id": "1.15.2-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7dfbe4b42cf61df47f71713590dd069267449420/1.15.2-pre2.json", "time": "2021-12-15T15:44:47+00:00", "releaseTime": "2020-01-16T12:35:57+00:00", "sha1": "7dfbe4b42cf61df47f71713590dd069267449420", "complianceLevel": 0}, {"id": "1.15.2-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6e8723a73cc47b4a81e44ca25b1a2d2dce3b1c04/1.15.2-pre1.json", "time": "2021-12-15T15:44:46+00:00", "releaseTime": "2020-01-14T16:19:31+00:00", "sha1": "6e8723a73cc47b4a81e44ca25b1a2d2dce3b1c04", "complianceLevel": 0}, {"id": "1.15.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/3526ede769b1a1d30aca5b7b98a81607c60305f7/1.15.1.json", "time": "2021-12-15T15:43:59+00:00", "releaseTime": "2019-12-16T10:29:47+00:00", "sha1": "3526ede769b1a1d30aca5b7b98a81607c60305f7", "complianceLevel": 0}, {"id": "1.15.1-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/27ceb6dbdca4821a509ed70d26773cfecef4ce63/1.15.1-pre1.json", "time": "2021-12-15T15:44:45+00:00", "releaseTime": "2019-12-12T14:02:30+00:00", "sha1": "27ceb6dbdca4821a509ed70d26773cfecef4ce63", "complianceLevel": 0}, {"id": "1.15", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/75d15976c2d82a0afd796802bac73c934ea59df7/1.15.json", "time": "2021-12-15T15:44:00+00:00", "releaseTime": "2019-12-09T13:13:38+00:00", "sha1": "75d15976c2d82a0afd796802bac73c934ea59df7", "complianceLevel": 0}, {"id": "1.15-pre7", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/54358db380289fb3547a1d7fb5761b1b1f6d0ba8/1.15-pre7.json", "time": "2021-12-15T15:44:44+00:00", "releaseTime": "2019-12-09T12:14:11+00:00", "sha1": "54358db380289fb3547a1d7fb5761b1b1f6d0ba8", "complianceLevel": 0}, {"id": "1.15-pre6", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6d4356b81f961250fccd9355c0bb115afc181a1b/1.15-pre6.json", "time": "2021-12-15T15:44:44+00:00", "releaseTime": "2019-12-06T12:04:30+00:00", "sha1": "6d4356b81f961250fccd9355c0bb115afc181a1b", "complianceLevel": 0}, {"id": "1.15-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/97d4e275e0d5cf754fabfc6017e0cf410fb25e50/1.15-pre5.json", "time": "2021-12-15T15:44:43+00:00", "releaseTime": "2019-12-05T13:20:00+00:00", "sha1": "97d4e275e0d5cf754fabfc6017e0cf410fb25e50", "complianceLevel": 0}, {"id": "1.15-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a2c68e78c6887d91af86cbe51bda9902565c36b5/1.15-pre4.json", "time": "2021-12-15T15:44:43+00:00", "releaseTime": "2019-12-03T12:24:24+00:00", "sha1": "a2c68e78c6887d91af86cbe51bda9902565c36b5", "complianceLevel": 0}, {"id": "1.15-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d99a70baa558e7912f9331e42263125d18c97e27/1.15-pre3.json", "time": "2021-12-15T15:44:42+00:00", "releaseTime": "2019-11-28T17:17:50+00:00", "sha1": "d99a70baa558e7912f9331e42263125d18c97e27", "complianceLevel": 0}, {"id": "1.15-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ab23ff69e4bc33349868f88ec0eab0a9024e9d79/1.15-pre2.json", "time": "2021-12-15T15:44:41+00:00", "releaseTime": "2019-11-25T18:09:38+00:00", "sha1": "ab23ff69e4bc33349868f88ec0eab0a9024e9d79", "complianceLevel": 0}, {"id": "1.15-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b92f7ad0e2dea435c9738b09a069199b37869398/1.15-pre1.json", "time": "2021-12-15T15:44:41+00:00", "releaseTime": "2019-11-21T17:01:17+00:00", "sha1": "b92f7ad0e2dea435c9738b09a069199b37869398", "complianceLevel": 0}, {"id": "19w46b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c8aabd44ac18b173a68091c00b4ff0151f29faeb/19w46b.json", "time": "2021-12-15T15:47:14+00:00", "releaseTime": "2019-11-14T13:29:24+00:00", "sha1": "c8aabd44ac18b173a68091c00b4ff0151f29faeb", "complianceLevel": 0}, {"id": "19w46a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d129178105f1a1ab114b925752322c963f521eb7/19w46a.json", "time": "2021-12-15T15:47:13+00:00", "releaseTime": "2019-11-13T16:37:46+00:00", "sha1": "d129178105f1a1ab114b925752322c963f521eb7", "complianceLevel": 0}, {"id": "19w45b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f2ab5210dbc8768733b227747139e6054b93ae57/19w45b.json", "time": "2021-12-15T15:47:12+00:00", "releaseTime": "2019-11-08T12:42:44+00:00", "sha1": "f2ab5210dbc8768733b227747139e6054b93ae57", "complianceLevel": 0}, {"id": "19w45a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9ac552143b82bb8740b34c23dd997c4aab23f2dd/19w45a.json", "time": "2021-12-15T15:47:12+00:00", "releaseTime": "2019-11-07T16:19:20+00:00", "sha1": "9ac552143b82bb8740b34c23dd997c4aab23f2dd", "complianceLevel": 0}, {"id": "19w44a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0b7015eb577c9e04a5a21b83fa7f4d8d7b723e93/19w44a.json", "time": "2021-12-15T15:47:11+00:00", "releaseTime": "2019-10-30T15:31:44+00:00", "sha1": "0b7015eb577c9e04a5a21b83fa7f4d8d7b723e93", "complianceLevel": 0}, {"id": "19w42a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ac1a6a7f15382cda12878d5e304c6a34ed6349d5/19w42a.json", "time": "2021-12-15T15:47:10+00:00", "releaseTime": "2019-10-16T15:30:39+00:00", "sha1": "ac1a6a7f15382cda12878d5e304c6a34ed6349d5", "complianceLevel": 0}, {"id": "19w41a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fbcc94f6cdb36247a70e87d06811bfacc83f3e8c/19w41a.json", "time": "2021-12-15T15:47:10+00:00", "releaseTime": "2019-10-09T15:21:35+00:00", "sha1": "fbcc94f6cdb36247a70e87d06811bfacc83f3e8c", "complianceLevel": 0}, {"id": "19w40a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/02785403832a5aad38d51e71d5912d75dc196f10/19w40a.json", "time": "2021-12-15T15:47:09+00:00", "releaseTime": "2019-10-02T13:40:26+00:00", "sha1": "02785403832a5aad38d51e71d5912d75dc196f10", "complianceLevel": 0}, {"id": "19w39a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/75eb7c180e5be8e730ba7b2a9d495a9d33092a7f/19w39a.json", "time": "2021-12-15T15:47:08+00:00", "releaseTime": "2019-09-27T10:13:33+00:00", "sha1": "75eb7c180e5be8e730ba7b2a9d495a9d33092a7f", "complianceLevel": 0}, {"id": "19w38b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b9166a39db29187cb5aca9ba57457d300dc30390/19w38b.json", "time": "2021-12-15T15:47:08+00:00", "releaseTime": "2019-09-18T14:59:13+00:00", "sha1": "b9166a39db29187cb5aca9ba57457d300dc30390", "complianceLevel": 0}, {"id": "19w38a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5fd4c2e92056d7ad3727af992a1c1c1a96f892e6/19w38a.json", "time": "2021-12-15T15:47:07+00:00", "releaseTime": "2019-09-18T10:03:22+00:00", "sha1": "5fd4c2e92056d7ad3727af992a1c1c1a96f892e6", "complianceLevel": 0}, {"id": "19w37a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/add2b28d6e297e10e139955c13e3c3d7caa19603/19w37a.json", "time": "2021-12-15T15:47:06+00:00", "releaseTime": "2019-09-11T11:46:44+00:00", "sha1": "add2b28d6e297e10e139955c13e3c3d7caa19603", "complianceLevel": 0}, {"id": "19w36a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fa89fbd191476584136418814c4e1e808f6e5381/19w36a.json", "time": "2021-12-15T15:47:06+00:00", "releaseTime": "2019-09-04T11:19:34+00:00", "sha1": "fa89fbd191476584136418814c4e1e808f6e5381", "complianceLevel": 0}, {"id": "19w35a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/160fd40277b049c90c3402261f59f2ccb5e81506/19w35a.json", "time": "2021-12-15T15:47:05+00:00", "releaseTime": "2019-08-28T15:01:44+00:00", "sha1": "160fd40277b049c90c3402261f59f2ccb5e81506", "complianceLevel": 0}, {"id": "19w34a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f7419386943a8c54a6067078921508730bee3d87/19w34a.json", "time": "2021-12-15T15:47:05+00:00", "releaseTime": "2019-08-22T12:06:21+00:00", "sha1": "f7419386943a8c54a6067078921508730bee3d87", "complianceLevel": 0}, {"id": "1.14.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/8f81523cd2c2fdaf9c37431fceb0a446dbe71ed5/1.14.4.json", "time": "2021-12-15T15:43:57+00:00", "releaseTime": "2019-07-19T09:25:47+00:00", "sha1": "8f81523cd2c2fdaf9c37431fceb0a446dbe71ed5", "complianceLevel": 0}, {"id": "1.14.4-pre7", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/225785e19ae0b841ca67e71a2dae81fe109c872a/1.14.4-pre7.json", "time": "2021-12-15T15:44:40+00:00", "releaseTime": "2019-07-18T11:32:36+00:00", "sha1": "225785e19ae0b841ca67e71a2dae81fe109c872a", "complianceLevel": 0}, {"id": "1.14.4-pre6", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5064ae3ee503c8ebc27928d6ae8d6937367bcdc5/1.14.4-pre6.json", "time": "2021-12-15T15:44:40+00:00", "releaseTime": "2019-07-15T12:39:49+00:00", "sha1": "5064ae3ee503c8ebc27928d6ae8d6937367bcdc5", "complianceLevel": 0}, {"id": "1.14.4-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/edae35914121182e2253dabdb6ad37e9219dfd1a/1.14.4-pre5.json", "time": "2021-12-15T15:44:39+00:00", "releaseTime": "2019-07-11T10:52:33+00:00", "sha1": "edae35914121182e2253dabdb6ad37e9219dfd1a", "complianceLevel": 0}, {"id": "1.14.4-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9d8f7c94726e8c9058d98aa5d791540854c06c28/1.14.4-pre4.json", "time": "2021-12-15T15:44:39+00:00", "releaseTime": "2019-07-10T12:53:29+00:00", "sha1": "9d8f7c94726e8c9058d98aa5d791540854c06c28", "complianceLevel": 0}, {"id": "1.14.4-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/23ea71adf67bfe0159c271e4113ad46a4c7ff8b8/1.14.4-pre3.json", "time": "2021-12-15T15:44:38+00:00", "releaseTime": "2019-07-08T11:21:42+00:00", "sha1": "23ea71adf67bfe0159c271e4113ad46a4c7ff8b8", "complianceLevel": 0}, {"id": "1.14.4-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b781fc09310f223c0304297b7e094a75a5612381/1.14.4-pre2.json", "time": "2021-12-15T15:44:38+00:00", "releaseTime": "2019-07-04T14:41:05+00:00", "sha1": "b781fc09310f223c0304297b7e094a75a5612381", "complianceLevel": 0}, {"id": "1.14.4-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dd23d80d9b2d4ecc2800541922f0b0ec4e6c89d3/1.14.4-pre1.json", "time": "2021-12-15T15:44:37+00:00", "releaseTime": "2019-07-03T13:01:01+00:00", "sha1": "dd23d80d9b2d4ecc2800541922f0b0ec4e6c89d3", "complianceLevel": 0}, {"id": "1.14.3", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/970fb095132b5fa3edf829bbc487d415f3267d7f/1.14.3.json", "time": "2021-12-15T15:43:57+00:00", "releaseTime": "2019-06-24T12:52:52+00:00", "sha1": "970fb095132b5fa3edf829bbc487d415f3267d7f", "complianceLevel": 0}, {"id": "1.14.3-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6a3086cecaffe3030dcd8abd4afcf57520928ad1/1.14.3-pre4.json", "time": "2021-12-15T15:44:37+00:00", "releaseTime": "2019-06-19T11:44:29+00:00", "sha1": "6a3086cecaffe3030dcd8abd4afcf57520928ad1", "complianceLevel": 0}, {"id": "1.14.3-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d3224381a7c5708e9482e80d1d59ce7e242dbc44/1.14.3-pre3.json", "time": "2021-12-15T15:44:36+00:00", "releaseTime": "2019-06-14T08:03:33+00:00", "sha1": "d3224381a7c5708e9482e80d1d59ce7e242dbc44", "complianceLevel": 0}, {"id": "1.14.3-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d13fcddba357336276798124d5ad91cb5071db34/1.14.3-pre2.json", "time": "2021-12-15T15:44:36+00:00", "releaseTime": "2019-06-07T09:11:29+00:00", "sha1": "d13fcddba357336276798124d5ad91cb5071db34", "complianceLevel": 0}, {"id": "1.14.3-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b76415aca4de58ad4d2da3920af61e2d0aab423f/1.14.3-pre1.json", "time": "2021-12-15T15:44:36+00:00", "releaseTime": "2019-06-03T14:34:20+00:00", "sha1": "b76415aca4de58ad4d2da3920af61e2d0aab423f", "complianceLevel": 0}, {"id": "1.14.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/13a7b4ed55faa6a88f7729c0923bbe12a03f63e8/1.14.2.json", "time": "2021-12-15T15:43:56+00:00", "releaseTime": "2019-05-27T11:48:25+00:00", "sha1": "13a7b4ed55faa6a88f7729c0923bbe12a03f63e8", "complianceLevel": 0}, {"id": "1.14.2 Pre-Release 4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2b6d0cd838d546f6082bd52f105d7627ae0794ec/1.14.2%20Pre-Release%204.json", "time": "2021-12-15T15:44:35+00:00", "releaseTime": "2019-05-27T07:21:11+00:00", "sha1": "2b6d0cd838d546f6082bd52f105d7627ae0794ec", "complianceLevel": 0}, {"id": "1.14.2 Pre-Release 3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b47e5d28c614cd8ce3c1d7f3490c370d10db19b9/1.14.2%20Pre-Release%203.json", "time": "2021-12-15T15:44:35+00:00", "releaseTime": "2019-05-22T13:12:51+00:00", "sha1": "b47e5d28c614cd8ce3c1d7f3490c370d10db19b9", "complianceLevel": 0}, {"id": "1.14.2 Pre-Release 2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e9df02944c109a001c4aa207339d3a120b7e47bb/1.14.2%20Pre-Release%202.json", "time": "2021-12-15T15:44:34+00:00", "releaseTime": "2019-05-17T12:21:03+00:00", "sha1": "e9df02944c109a001c4aa207339d3a120b7e47bb", "complianceLevel": 0}, {"id": "1.14.2 Pre-Release 1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/409a8748249533d58ffa9e3190f0753cdbcfbf71/1.14.2%20Pre-Release%201.json", "time": "2021-12-15T15:44:34+00:00", "releaseTime": "2019-05-16T15:40:25+00:00", "sha1": "409a8748249533d58ffa9e3190f0753cdbcfbf71", "complianceLevel": 0}, {"id": "1.14.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/770df8fddfd27061560507267eccb650345b61e2/1.14.1.json", "time": "2021-12-15T15:43:56+00:00", "releaseTime": "2019-05-13T11:10:12+00:00", "sha1": "770df8fddfd27061560507267eccb650345b61e2", "complianceLevel": 0}, {"id": "1.14.1 Pre-Release 2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/292e1547a1840ede46346ada4fb67118fe94171a/1.14.1%20Pre-Release%202.json", "time": "2021-12-15T15:44:33+00:00", "releaseTime": "2019-05-09T14:01:04+00:00", "sha1": "292e1547a1840ede46346ada4fb67118fe94171a", "complianceLevel": 0}, {"id": "1.14.1 Pre-Release 1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c4151fa78258c9a337d5b5906280fe341e861c0a/1.14.1%20Pre-Release%201.json", "time": "2021-12-15T15:44:33+00:00", "releaseTime": "2019-05-07T14:44:42+00:00", "sha1": "c4151fa78258c9a337d5b5906280fe341e861c0a", "complianceLevel": 0}, {"id": "1.14", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/44552c5e051cff06483616adb1f6fd2d94ee3447/1.14.json", "time": "2021-12-15T15:43:58+00:00", "releaseTime": "2019-04-23T14:52:44+00:00", "sha1": "44552c5e051cff06483616adb1f6fd2d94ee3447", "complianceLevel": 0}, {"id": "1.14 Pre-Release 5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e85eb16ce5f7b337fdd81a41195457faf3724de1/1.14%20Pre-Release%205.json", "time": "2021-12-15T15:44:32+00:00", "releaseTime": "2019-04-18T11:05:19+00:00", "sha1": "e85eb16ce5f7b337fdd81a41195457faf3724de1", "complianceLevel": 0}, {"id": "1.14 Pre-Release 4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d1142b477da49f3be98c6de7130ff9bcc73c64de/1.14%20Pre-Release%204.json", "time": "2021-12-15T15:44:32+00:00", "releaseTime": "2019-04-17T15:31:12+00:00", "sha1": "d1142b477da49f3be98c6de7130ff9bcc73c64de", "complianceLevel": 0}, {"id": "1.14 Pre-Release 3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5119ee7ea1455a0655fe1139aa4224aea02af54b/1.14%20Pre-Release%203.json", "time": "2021-12-15T15:44:31+00:00", "releaseTime": "2019-04-16T13:57:10+00:00", "sha1": "5119ee7ea1455a0655fe1139aa4224aea02af54b", "complianceLevel": 0}, {"id": "1.14 Pre-Release 2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0c322327071722961042d33e7221c05037e22171/1.14%20Pre-Release%202.json", "time": "2021-12-15T15:44:30+00:00", "releaseTime": "2019-04-12T11:38:53+00:00", "sha1": "0c322327071722961042d33e7221c05037e22171", "complianceLevel": 0}, {"id": "1.14 Pre-Release 1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9f71a3ea076d679b54fbc9fea64b899fe189bbbd/1.14%20Pre-Release%201.json", "time": "2021-12-15T15:44:30+00:00", "releaseTime": "2019-04-10T14:24:16+00:00", "sha1": "9f71a3ea076d679b54fbc9fea64b899fe189bbbd", "complianceLevel": 0}, {"id": "19w14b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1f797b0d05b5dec63940d5fcf090fc7e55d30e01/19w14b.json", "time": "2021-12-15T15:47:04+00:00", "releaseTime": "2019-04-05T10:33:58+00:00", "sha1": "1f797b0d05b5dec63940d5fcf090fc7e55d30e01", "complianceLevel": 0}, {"id": "19w14a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d0269d17bf3af72b454c8275e8603561541fc4df/19w14a.json", "time": "2021-12-15T15:47:04+00:00", "releaseTime": "2019-04-03T13:45:00+00:00", "sha1": "d0269d17bf3af72b454c8275e8603561541fc4df", "complianceLevel": 0}, {"id": "3D Shareware v1.34", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fe072c0f3f14f254407068b9515efd825b736a2e/3D%20Shareware%20v1.34.json", "time": "2021-12-15T15:47:44+00:00", "releaseTime": "2019-04-01T11:18:08+00:00", "sha1": "fe072c0f3f14f254407068b9515efd825b736a2e", "complianceLevel": 0}, {"id": "19w13b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/549dab6b7f4710ef9431edc2871ecca1e0ea56d0/19w13b.json", "time": "2021-12-15T15:47:03+00:00", "releaseTime": "2019-03-29T16:53:22+00:00", "sha1": "549dab6b7f4710ef9431edc2871ecca1e0ea56d0", "complianceLevel": 0}, {"id": "19w13a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4060bc60451368fe9ba137c9a56f8f925aaf0320/19w13a.json", "time": "2021-12-15T15:47:03+00:00", "releaseTime": "2019-03-27T15:15:31+00:00", "sha1": "4060bc60451368fe9ba137c9a56f8f925aaf0320", "complianceLevel": 0}, {"id": "19w12b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b5faf778f1026d1904723f1db2d81b3044c957d9/19w12b.json", "time": "2021-12-15T15:47:02+00:00", "releaseTime": "2019-03-21T15:20:01+00:00", "sha1": "b5faf778f1026d1904723f1db2d81b3044c957d9", "complianceLevel": 0}, {"id": "19w12a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f391138069573253105f68d2a14a12d8db2b1f5c/19w12a.json", "time": "2021-12-15T15:47:02+00:00", "releaseTime": "2019-03-20T16:47:34+00:00", "sha1": "f391138069573253105f68d2a14a12d8db2b1f5c", "complianceLevel": 0}, {"id": "19w11b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/540388f4c2f08ccaf4314d8c886cff842963ade9/19w11b.json", "time": "2021-12-15T15:47:01+00:00", "releaseTime": "2019-03-14T14:26:23+00:00", "sha1": "540388f4c2f08ccaf4314d8c886cff842963ade9", "complianceLevel": 0}, {"id": "19w11a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/08288d16d3b9253c3d3230df04ed970b38f42dab/19w11a.json", "time": "2021-12-15T15:47:01+00:00", "releaseTime": "2019-03-13T13:59:29+00:00", "sha1": "08288d16d3b9253c3d3230df04ed970b38f42dab", "complianceLevel": 0}, {"id": "19w09a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/813cb9ae2e902cd7c916858b6666064e9c201911/19w09a.json", "time": "2021-12-15T15:47:00+00:00", "releaseTime": "2019-02-27T14:44:30+00:00", "sha1": "813cb9ae2e902cd7c916858b6666064e9c201911", "complianceLevel": 0}, {"id": "19w08b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c12ced303962763107f61e6ceb8e875d6af400d3/19w08b.json", "time": "2021-12-15T15:47:00+00:00", "releaseTime": "2019-02-21T13:38:09+00:00", "sha1": "c12ced303962763107f61e6ceb8e875d6af400d3", "complianceLevel": 0}, {"id": "19w08a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8ecab11a8dd85b1876eadef14320805c86fdbd0e/19w08a.json", "time": "2021-12-15T15:46:59+00:00", "releaseTime": "2019-02-20T14:56:58+00:00", "sha1": "8ecab11a8dd85b1876eadef14320805c86fdbd0e", "complianceLevel": 0}, {"id": "19w07a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a19c014d87039cb3fbc19b9582eeaaca04cb1240/19w07a.json", "time": "2021-12-15T15:46:59+00:00", "releaseTime": "2019-02-13T16:12:08+00:00", "sha1": "a19c014d87039cb3fbc19b9582eeaaca04cb1240", "complianceLevel": 0}, {"id": "19w06a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9c04771f81e2a5b8be9da887ced4250c9af11f83/19w06a.json", "time": "2021-12-15T15:46:58+00:00", "releaseTime": "2019-02-06T16:24:13+00:00", "sha1": "9c04771f81e2a5b8be9da887ced4250c9af11f83", "complianceLevel": 0}, {"id": "19w05a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d540b37f16a36cb8e808b99619264e3b7e887099/19w05a.json", "time": "2021-12-15T15:46:58+00:00", "releaseTime": "2019-01-30T15:16:49+00:00", "sha1": "d540b37f16a36cb8e808b99619264e3b7e887099", "complianceLevel": 0}, {"id": "19w04b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/cffc41bb7f89f3c1d8fe1afcac9bc602b53e0de0/19w04b.json", "time": "2021-12-15T15:46:57+00:00", "releaseTime": "2019-01-25T12:20:15+00:00", "sha1": "cffc41bb7f89f3c1d8fe1afcac9bc602b53e0de0", "complianceLevel": 0}, {"id": "19w04a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8353572034619fb0bbb884401756d73abddfc190/19w04a.json", "time": "2021-12-15T15:46:57+00:00", "releaseTime": "2019-01-24T15:31:52+00:00", "sha1": "8353572034619fb0bbb884401756d73abddfc190", "complianceLevel": 0}, {"id": "19w03c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/74c257a53a59c196c90a7c63812ed626aa6dc658/19w03c.json", "time": "2021-12-15T15:46:56+00:00", "releaseTime": "2019-01-18T11:27:13+00:00", "sha1": "74c257a53a59c196c90a7c63812ed626aa6dc658", "complianceLevel": 0}, {"id": "19w03b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/01ccb6e33dd283f70d713acbd353bf5091825397/19w03b.json", "time": "2021-12-15T15:46:56+00:00", "releaseTime": "2019-01-17T16:43:27+00:00", "sha1": "01ccb6e33dd283f70d713acbd353bf5091825397", "complianceLevel": 0}, {"id": "19w03a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7767943a6356d3073ec4d9ccc51be69b4a2a7631/19w03a.json", "time": "2021-12-15T15:46:55+00:00", "releaseTime": "2019-01-16T16:45:02+00:00", "sha1": "7767943a6356d3073ec4d9ccc51be69b4a2a7631", "complianceLevel": 0}, {"id": "19w02a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ae10d5ccb31ef32365e7249e9695904ef4781618/19w02a.json", "time": "2021-12-15T15:46:55+00:00", "releaseTime": "2019-01-09T15:52:07+00:00", "sha1": "ae10d5ccb31ef32365e7249e9695904ef4781618", "complianceLevel": 0}, {"id": "18w50a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6fa8a7460769147341dc03891e1b522556aad82d/18w50a.json", "time": "2021-12-15T15:46:54+00:00", "releaseTime": "2018-12-12T14:58:13+00:00", "sha1": "6fa8a7460769147341dc03891e1b522556aad82d", "complianceLevel": 0}, {"id": "18w49a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7033863050ea4dc3a4b829cfc1d347d991699689/18w49a.json", "time": "2021-12-15T15:46:54+00:00", "releaseTime": "2018-12-05T12:24:30+00:00", "sha1": "7033863050ea4dc3a4b829cfc1d347d991699689", "complianceLevel": 0}, {"id": "18w48b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/bd00e4cf9d834b8176770d66805ce429f5bd7317/18w48b.json", "time": "2021-12-15T15:46:53+00:00", "releaseTime": "2018-11-30T10:37:31+00:00", "sha1": "bd00e4cf9d834b8176770d66805ce429f5bd7317", "complianceLevel": 0}, {"id": "18w48a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0061465b249e04ebc24192bf0b96fe51f6d9bfef/18w48a.json", "time": "2021-12-15T15:46:53+00:00", "releaseTime": "2018-11-29T13:11:38+00:00", "sha1": "0061465b249e04ebc24192bf0b96fe51f6d9bfef", "complianceLevel": 0}, {"id": "18w47b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/200c849b8a0baa3492e069a484b36a6d5ad5612d/18w47b.json", "time": "2021-12-15T15:46:52+00:00", "releaseTime": "2018-11-23T10:46:41+00:00", "sha1": "200c849b8a0baa3492e069a484b36a6d5ad5612d", "complianceLevel": 0}, {"id": "18w47a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6931ca7ca10b90df541bc946ad6410f7db81f63c/18w47a.json", "time": "2021-12-15T15:46:52+00:00", "releaseTime": "2018-11-21T15:45:22+00:00", "sha1": "6931ca7ca10b90df541bc946ad6410f7db81f63c", "complianceLevel": 0}, {"id": "18w46a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/87fbc01477063b55de78760f68464d296e1438a1/18w46a.json", "time": "2021-12-15T15:46:51+00:00", "releaseTime": "2018-11-15T13:43:14+00:00", "sha1": "87fbc01477063b55de78760f68464d296e1438a1", "complianceLevel": 0}, {"id": "18w45a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b725ef482b49878b96e621fc91a825ba64958413/18w45a.json", "time": "2021-12-15T15:46:50+00:00", "releaseTime": "2018-11-07T14:40:06+00:00", "sha1": "b725ef482b49878b96e621fc91a825ba64958413", "complianceLevel": 0}, {"id": "18w44a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a9e51fcb479a02ba7761194b6bbf1e571f2410ea/18w44a.json", "time": "2021-12-15T15:46:50+00:00", "releaseTime": "2018-10-31T15:29:16+00:00", "sha1": "a9e51fcb479a02ba7761194b6bbf1e571f2410ea", "complianceLevel": 0}, {"id": "18w43c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3ef1f818fdc1437e4ede574a3f5077fa9c6a4ba7/18w43c.json", "time": "2021-12-15T15:46:49+00:00", "releaseTime": "2018-10-26T08:40:46+00:00", "sha1": "3ef1f818fdc1437e4ede574a3f5077fa9c6a4ba7", "complianceLevel": 0}, {"id": "18w43b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/efb23e85ea13754e7e994ec773106a15e5037844/18w43b.json", "time": "2021-12-15T15:46:49+00:00", "releaseTime": "2018-10-24T15:02:30+00:00", "sha1": "efb23e85ea13754e7e994ec773106a15e5037844", "complianceLevel": 0}, {"id": "18w43a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/663210679231f9bdee94159ee7ad806bd6b31cd4/18w43a.json", "time": "2021-12-15T15:46:48+00:00", "releaseTime": "2018-10-24T10:52:16+00:00", "sha1": "663210679231f9bdee94159ee7ad806bd6b31cd4", "complianceLevel": 0}, {"id": "1.13.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/1f439c21fee6a7816356c40a6194d064d1f44baa/1.13.2.json", "time": "2021-12-15T15:43:55+00:00", "releaseTime": "2018-10-22T11:41:07+00:00", "sha1": "1f439c21fee6a7816356c40a6194d064d1f44baa", "complianceLevel": 0}, {"id": "1.13.2-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1486e6c0f9281ea2ffff15fb594435960eb30b16/1.13.2-pre2.json", "time": "2021-12-15T15:44:29+00:00", "releaseTime": "2018-10-18T14:46:12+00:00", "sha1": "1486e6c0f9281ea2ffff15fb594435960eb30b16", "complianceLevel": 0}, {"id": "1.13.2-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4e50a1e4aa1fc871c296c6ba1345097bce864098/1.13.2-pre1.json", "time": "2021-12-15T15:44:29+00:00", "releaseTime": "2018-10-16T13:40:58+00:00", "sha1": "4e50a1e4aa1fc871c296c6ba1345097bce864098", "complianceLevel": 0}, {"id": "1.13.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/85c39d0f9d94a4777a9b401deb08cf07f55dfe39/1.13.1.json", "time": "2021-12-15T15:43:54+00:00", "releaseTime": "2018-08-22T14:03:42+00:00", "sha1": "85c39d0f9d94a4777a9b401deb08cf07f55dfe39", "complianceLevel": 0}, {"id": "1.13.1-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b6c39c683f1cae8b8a8c28398515f94dc3e75c94/1.13.1-pre2.json", "time": "2021-12-15T15:44:28+00:00", "releaseTime": "2018-08-20T13:52:09+00:00", "sha1": "b6c39c683f1cae8b8a8c28398515f94dc3e75c94", "complianceLevel": 0}, {"id": "1.13.1-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1e3b24bfa2f1c5612161faadf6612d9060d8ae4e/1.13.1-pre1.json", "time": "2021-12-15T15:44:28+00:00", "releaseTime": "2018-08-16T13:08:44+00:00", "sha1": "1e3b24bfa2f1c5612161faadf6612d9060d8ae4e", "complianceLevel": 0}, {"id": "18w33a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c55882f0c52ced8cb6d27dd76de44aca327a77d8/18w33a.json", "time": "2021-12-15T15:46:48+00:00", "releaseTime": "2018-08-15T14:28:56+00:00", "sha1": "c55882f0c52ced8cb6d27dd76de44aca327a77d8", "complianceLevel": 0}, {"id": "18w32a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b08212f3d19d05fc5c7e0f14008983d3f10fdd4b/18w32a.json", "time": "2021-12-15T15:46:47+00:00", "releaseTime": "2018-08-08T13:16:57+00:00", "sha1": "b08212f3d19d05fc5c7e0f14008983d3f10fdd4b", "complianceLevel": 0}, {"id": "18w31a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/66398c5a17f41841c3b46e143eb8fe4b25aeea64/18w31a.json", "time": "2021-12-15T15:46:47+00:00", "releaseTime": "2018-08-01T12:54:44+00:00", "sha1": "66398c5a17f41841c3b46e143eb8fe4b25aeea64", "complianceLevel": 0}, {"id": "18w30b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/99192fa960329f349ba8691be513917795ee2397/18w30b.json", "time": "2021-12-15T15:46:46+00:00", "releaseTime": "2018-07-26T16:06:57+00:00", "sha1": "99192fa960329f349ba8691be513917795ee2397", "complianceLevel": 0}, {"id": "18w30a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b4f2355456d625c4a28a09830b4b58b849f6034a/18w30a.json", "time": "2021-12-15T15:46:45+00:00", "releaseTime": "2018-07-25T14:29:31+00:00", "sha1": "b4f2355456d625c4a28a09830b4b58b849f6034a", "complianceLevel": 0}, {"id": "1.13", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/54e62e6c77c38f0094357d7d85a3c63a2b2f3dce/1.13.json", "time": "2021-12-15T15:43:55+00:00", "releaseTime": "2018-07-18T15:11:46+00:00", "sha1": "54e62e6c77c38f0094357d7d85a3c63a2b2f3dce", "complianceLevel": 0}, {"id": "1.13-pre10", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/85f3919d95ecf499c384465d6b9526a943529302/1.13-pre10.json", "time": "2021-12-15T15:44:22+00:00", "releaseTime": "2018-07-17T14:48:06+00:00", "sha1": "85f3919d95ecf499c384465d6b9526a943529302", "complianceLevel": 0}, {"id": "1.13-pre9", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/759d8e0774bf80e7989185da0e177034ba5256f1/1.13-pre9.json", "time": "2021-12-15T15:44:27+00:00", "releaseTime": "2018-07-16T14:17:42+00:00", "sha1": "759d8e0774bf80e7989185da0e177034ba5256f1", "complianceLevel": 0}, {"id": "1.13-pre8", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/481cdc143ea4e78cede29834904afeccdc85d1b2/1.13-pre8.json", "time": "2021-12-15T15:44:26+00:00", "releaseTime": "2018-07-13T11:45:00+00:00", "sha1": "481cdc143ea4e78cede29834904afeccdc85d1b2", "complianceLevel": 0}, {"id": "1.13-pre7", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/31af1da692c303e96a151d4cb5d0c08fae9c243b/1.13-pre7.json", "time": "2021-12-15T15:44:26+00:00", "releaseTime": "2018-07-10T14:21:42+00:00", "sha1": "31af1da692c303e96a151d4cb5d0c08fae9c243b", "complianceLevel": 0}, {"id": "1.13-pre6", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e5a3e56dc265a8791563967dae462cf9fbd5fc1b/1.13-pre6.json", "time": "2021-12-15T15:44:25+00:00", "releaseTime": "2018-07-04T12:36:00+00:00", "sha1": "e5a3e56dc265a8791563967dae462cf9fbd5fc1b", "complianceLevel": 0}, {"id": "1.13-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b4826d4299f4fbb009c1011a8730a86f3e0db01e/1.13-pre5.json", "time": "2021-12-15T15:44:25+00:00", "releaseTime": "2018-06-28T13:58:53+00:00", "sha1": "b4826d4299f4fbb009c1011a8730a86f3e0db01e", "complianceLevel": 0}, {"id": "1.13-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2bb3ce6a72e5bd9ebb0bd97c93c6e7323d7ccedf/1.13-pre4.json", "time": "2021-12-15T15:44:24+00:00", "releaseTime": "2018-06-26T13:00:55+00:00", "sha1": "2bb3ce6a72e5bd9ebb0bd97c93c6e7323d7ccedf", "complianceLevel": 0}, {"id": "1.13-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1fcc1e73021190b41a5bf20b65985c191292b855/1.13-pre3.json", "time": "2021-12-15T15:44:24+00:00", "releaseTime": "2018-06-21T12:57:11+00:00", "sha1": "1fcc1e73021190b41a5bf20b65985c191292b855", "complianceLevel": 0}, {"id": "1.13-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6e1e8a40b4301e67f6eb17b1b5ce7aabd88455cb/1.13-pre2.json", "time": "2021-12-15T15:44:23+00:00", "releaseTime": "2018-06-15T09:20:00+00:00", "sha1": "6e1e8a40b4301e67f6eb17b1b5ce7aabd88455cb", "complianceLevel": 0}, {"id": "1.13-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/265ac26f2bcd418656f00d23260fc2a8348a1ea1/1.13-pre1.json", "time": "2021-12-15T15:44:22+00:00", "releaseTime": "2018-06-04T15:17:34+00:00", "sha1": "265ac26f2bcd418656f00d23260fc2a8348a1ea1", "complianceLevel": 0}, {"id": "18w22c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4bf9154d3e7f493dad8349ec2d36ff45a776476a/18w22c.json", "time": "2021-12-15T15:46:45+00:00", "releaseTime": "2018-05-31T13:53:15+00:00", "sha1": "4bf9154d3e7f493dad8349ec2d36ff45a776476a", "complianceLevel": 0}, {"id": "18w22b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/70b35ec337ef414c6c20a0c262b6e20292149950/18w22b.json", "time": "2021-12-15T15:46:44+00:00", "releaseTime": "2018-05-30T13:48:58+00:00", "sha1": "70b35ec337ef414c6c20a0c262b6e20292149950", "complianceLevel": 0}, {"id": "18w22a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/adc9b9d5fd1152c1ffa17a6075e57e764438c3c8/18w22a.json", "time": "2021-12-15T15:46:44+00:00", "releaseTime": "2018-05-29T13:23:55+00:00", "sha1": "adc9b9d5fd1152c1ffa17a6075e57e764438c3c8", "complianceLevel": 0}, {"id": "18w21b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e6d564e18d99d20bc74a79bacafb457978558c3b/18w21b.json", "time": "2021-12-15T15:46:43+00:00", "releaseTime": "2018-05-25T10:09:09+00:00", "sha1": "e6d564e18d99d20bc74a79bacafb457978558c3b", "complianceLevel": 0}, {"id": "18w21a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a61dfb8bf507cd530787fe4ad9cd2b14063cc4dc/18w21a.json", "time": "2021-12-15T15:46:43+00:00", "releaseTime": "2018-05-23T13:11:49+00:00", "sha1": "a61dfb8bf507cd530787fe4ad9cd2b14063cc4dc", "complianceLevel": 0}, {"id": "18w20c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7281db0ef1cabf02d34d3f2ed7f07aabfc299740/18w20c.json", "time": "2021-12-15T15:46:42+00:00", "releaseTime": "2018-05-17T14:06:56+00:00", "sha1": "7281db0ef1cabf02d34d3f2ed7f07aabfc299740", "complianceLevel": 0}, {"id": "18w20b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/bd72cc730f396bbe650de1ece6ceada70850a131/18w20b.json", "time": "2021-12-15T15:46:42+00:00", "releaseTime": "2018-05-16T14:35:35+00:00", "sha1": "bd72cc730f396bbe650de1ece6ceada70850a131", "complianceLevel": 0}, {"id": "18w20a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b7178822e4c4d04346e34e1d102f7dc3ee4d1c4f/18w20a.json", "time": "2021-12-15T15:46:41+00:00", "releaseTime": "2018-05-15T14:02:25+00:00", "sha1": "b7178822e4c4d04346e34e1d102f7dc3ee4d1c4f", "complianceLevel": 0}, {"id": "18w19b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b61e982949acfe2cc3f5b2e5b9be058396a85f95/18w19b.json", "time": "2021-12-15T15:46:41+00:00", "releaseTime": "2018-05-09T10:00:51+00:00", "sha1": "b61e982949acfe2cc3f5b2e5b9be058396a85f95", "complianceLevel": 0}, {"id": "18w19a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dc8c8f0826b4b495eb59584885c590db058eba55/18w19a.json", "time": "2021-12-15T15:46:40+00:00", "releaseTime": "2018-05-08T13:05:19+00:00", "sha1": "dc8c8f0826b4b495eb59584885c590db058eba55", "complianceLevel": 0}, {"id": "18w16a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7ba5eb88eddd93a5ca79db48a5b165c03b1e86ee/18w16a.json", "time": "2021-12-15T15:46:40+00:00", "releaseTime": "2018-04-19T14:46:35+00:00", "sha1": "7ba5eb88eddd93a5ca79db48a5b165c03b1e86ee", "complianceLevel": 0}, {"id": "18w15a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d12a23f6fcc83dfe4aa7bada51cb232d81de5efa/18w15a.json", "time": "2021-12-15T15:46:39+00:00", "releaseTime": "2018-04-11T14:54:22+00:00", "sha1": "d12a23f6fcc83dfe4aa7bada51cb232d81de5efa", "complianceLevel": 0}, {"id": "18w14b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/145c2eec9e617b93eaf502dc8670868901d596d0/18w14b.json", "time": "2021-12-15T15:46:39+00:00", "releaseTime": "2018-04-05T14:44:02+00:00", "sha1": "145c2eec9e617b93eaf502dc8670868901d596d0", "complianceLevel": 0}, {"id": "18w14a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/cf3dc2bacafe030c10568d78a40924149af06bec/18w14a.json", "time": "2021-12-15T15:46:38+00:00", "releaseTime": "2018-04-04T14:36:14+00:00", "sha1": "cf3dc2bacafe030c10568d78a40924149af06bec", "complianceLevel": 0}, {"id": "18w11a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0855bcb6451c60dcd57652ad7e17ab76f3a0ee41/18w11a.json", "time": "2021-12-15T15:46:38+00:00", "releaseTime": "2018-03-13T15:10:59+00:00", "sha1": "0855bcb6451c60dcd57652ad7e17ab76f3a0ee41", "complianceLevel": 0}, {"id": "18w10d", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3e59dae66bbe21fe5574fe225515953af900ccc7/18w10d.json", "time": "2021-12-15T15:46:37+00:00", "releaseTime": "2018-03-09T15:19:12+00:00", "sha1": "3e59dae66bbe21fe5574fe225515953af900ccc7", "complianceLevel": 0}, {"id": "18w10c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/34a84c146290fc084c3720522845cb45bc695d3f/18w10c.json", "time": "2021-12-15T15:46:37+00:00", "releaseTime": "2018-03-08T15:29:23+00:00", "sha1": "34a84c146290fc084c3720522845cb45bc695d3f", "complianceLevel": 0}, {"id": "18w10b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6b5e66fdd49b953cc3a045e0eca8fbd0ca6f2c0f/18w10b.json", "time": "2021-12-15T15:46:36+00:00", "releaseTime": "2018-03-07T15:56:01+00:00", "sha1": "6b5e66fdd49b953cc3a045e0eca8fbd0ca6f2c0f", "complianceLevel": 0}, {"id": "18w10a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/aa9bf09ec5b113f3fa0b9ebffbb4348e69ab7f7b/18w10a.json", "time": "2021-12-15T15:46:36+00:00", "releaseTime": "2018-03-06T15:54:24+00:00", "sha1": "aa9bf09ec5b113f3fa0b9ebffbb4348e69ab7f7b", "complianceLevel": 0}, {"id": "18w09a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e7ba45e34b4f582960869ba8d9f7aed5768d269b/18w09a.json", "time": "2021-12-15T15:46:35+00:00", "releaseTime": "2018-03-01T14:15:10+00:00", "sha1": "e7ba45e34b4f582960869ba8d9f7aed5768d269b", "complianceLevel": 0}, {"id": "18w08b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7147b296dd0a82a4004c638939fce87a8e3c6fa8/18w08b.json", "time": "2021-12-15T15:46:35+00:00", "releaseTime": "2018-02-22T15:44:49+00:00", "sha1": "7147b296dd0a82a4004c638939fce87a8e3c6fa8", "complianceLevel": 0}, {"id": "18w08a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0b76a7b1f67f015222a1c245da516e55ffb3b16a/18w08a.json", "time": "2021-12-15T15:46:34+00:00", "releaseTime": "2018-02-21T14:59:00+00:00", "sha1": "0b76a7b1f67f015222a1c245da516e55ffb3b16a", "complianceLevel": 0}, {"id": "18w07c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3b11ace342a976858186c25b4939f72358390d85/18w07c.json", "time": "2021-12-15T15:46:33+00:00", "releaseTime": "2018-02-16T13:23:32+00:00", "sha1": "3b11ace342a976858186c25b4939f72358390d85", "complianceLevel": 0}, {"id": "18w07b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/81c57cbe71987fc549c198ca0bd967a27b709068/18w07b.json", "time": "2021-12-15T15:46:33+00:00", "releaseTime": "2018-02-15T14:28:42+00:00", "sha1": "81c57cbe71987fc549c198ca0bd967a27b709068", "complianceLevel": 0}, {"id": "18w07a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/11ed6f837b5a7ae96852adcde599d02490f541b2/18w07a.json", "time": "2021-12-15T15:46:32+00:00", "releaseTime": "2018-02-14T17:34:13+00:00", "sha1": "11ed6f837b5a7ae96852adcde599d02490f541b2", "complianceLevel": 0}, {"id": "18w06a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/42204d4625f9c541bc78685bd0113ab6620c615d/18w06a.json", "time": "2021-12-15T15:46:32+00:00", "releaseTime": "2018-02-09T12:09:55+00:00", "sha1": "42204d4625f9c541bc78685bd0113ab6620c615d", "complianceLevel": 0}, {"id": "18w05a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7db497bdadc50245f2c4fa5e60730bfec22d682f/18w05a.json", "time": "2021-12-15T15:46:31+00:00", "releaseTime": "2018-01-31T13:32:09+00:00", "sha1": "7db497bdadc50245f2c4fa5e60730bfec22d682f", "complianceLevel": 0}, {"id": "18w03b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8dad2dee3a546d2affa746a12efc739616d67e25/18w03b.json", "time": "2021-12-15T15:46:31+00:00", "releaseTime": "2018-01-17T15:09:14+00:00", "sha1": "8dad2dee3a546d2affa746a12efc739616d67e25", "complianceLevel": 0}, {"id": "18w03a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8a966aa6baf8f359d84bf505937d9f654e12bdce/18w03a.json", "time": "2021-12-15T15:46:30+00:00", "releaseTime": "2018-01-17T14:25:24+00:00", "sha1": "8a966aa6baf8f359d84bf505937d9f654e12bdce", "complianceLevel": 0}, {"id": "18w02a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5f3ca530535f41f4545ea42bf674bcff12d368d4/18w02a.json", "time": "2021-12-15T15:46:30+00:00", "releaseTime": "2018-01-10T11:54:55+00:00", "sha1": "5f3ca530535f41f4545ea42bf674bcff12d368d4", "complianceLevel": 0}, {"id": "18w01a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/785ade0867cc8fb9f1bb68b6c15ca1ccb89477a6/18w01a.json", "time": "2021-12-15T15:46:29+00:00", "releaseTime": "2018-01-03T13:29:30+00:00", "sha1": "785ade0867cc8fb9f1bb68b6c15ca1ccb89477a6", "complianceLevel": 0}, {"id": "17w50a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/efe9f23d6eddef432b89deafebbd17ceae0492cc/17w50a.json", "time": "2021-12-15T15:46:29+00:00", "releaseTime": "2017-12-11T15:28:08+00:00", "sha1": "efe9f23d6eddef432b89deafebbd17ceae0492cc", "complianceLevel": 0}, {"id": "17w49b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8ad72046ee3f7f82c22b323dc41c245c09e97e54/17w49b.json", "time": "2021-12-15T15:46:28+00:00", "releaseTime": "2017-12-07T15:29:54+00:00", "sha1": "8ad72046ee3f7f82c22b323dc41c245c09e97e54", "complianceLevel": 0}, {"id": "17w49a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/807995ef62ffe655b41ee7608f85863c9d0ecfd3/17w49a.json", "time": "2021-12-15T15:46:28+00:00", "releaseTime": "2017-12-06T14:24:30+00:00", "sha1": "807995ef62ffe655b41ee7608f85863c9d0ecfd3", "complianceLevel": 0}, {"id": "17w48a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/37cfcb5e613bf6f62f8ffe19401ebd7fd725a99d/17w48a.json", "time": "2021-12-15T15:46:27+00:00", "releaseTime": "2017-11-27T15:36:33+00:00", "sha1": "37cfcb5e613bf6f62f8ffe19401ebd7fd725a99d", "complianceLevel": 0}, {"id": "17w47b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8796dc8b2667ce0cdc43ec0170e79930aca66509/17w47b.json", "time": "2021-12-15T15:46:26+00:00", "releaseTime": "2017-11-23T15:30:12+00:00", "sha1": "8796dc8b2667ce0cdc43ec0170e79930aca66509", "complianceLevel": 0}, {"id": "17w47a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3122ac16a5ffe9931ef199629c67d96ffe17d637/17w47a.json", "time": "2021-12-15T15:46:26+00:00", "releaseTime": "2017-11-22T12:40:05+00:00", "sha1": "3122ac16a5ffe9931ef199629c67d96ffe17d637", "complianceLevel": 0}, {"id": "17w46a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6666ccc72ccb68b972a83cfd8c57314008ef4da7/17w46a.json", "time": "2021-12-15T15:46:25+00:00", "releaseTime": "2017-11-15T15:21:55+00:00", "sha1": "6666ccc72ccb68b972a83cfd8c57314008ef4da7", "complianceLevel": 0}, {"id": "17w45b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f1a9ddceaca9e2d0f98892ebfcdf5067bf61f894/17w45b.json", "time": "2021-12-15T15:46:25+00:00", "releaseTime": "2017-11-10T10:07:02+00:00", "sha1": "f1a9ddceaca9e2d0f98892ebfcdf5067bf61f894", "complianceLevel": 0}, {"id": "17w45a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1a2335e8c66a502c844c5372ea20c04d24838c3a/17w45a.json", "time": "2021-12-15T15:46:24+00:00", "releaseTime": "2017-11-08T15:48:00+00:00", "sha1": "1a2335e8c66a502c844c5372ea20c04d24838c3a", "complianceLevel": 0}, {"id": "17w43b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e0dd9b1386a39d56ef1577e97034e3866dd3acc2/17w43b.json", "time": "2021-12-15T15:46:24+00:00", "releaseTime": "2017-10-26T13:36:22+00:00", "sha1": "e0dd9b1386a39d56ef1577e97034e3866dd3acc2", "complianceLevel": 0}, {"id": "17w43a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4ca5bcd076e20b7234227697cd113987fbded613/17w43a.json", "time": "2021-12-15T15:46:23+00:00", "releaseTime": "2017-10-25T14:43:50+00:00", "sha1": "4ca5bcd076e20b7234227697cd113987fbded613", "complianceLevel": 0}, {"id": "1.12.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/cfd75871c03119093d7c96a6a99f21137d00c855/1.12.2.json", "time": "2021-12-15T15:43:53+00:00", "releaseTime": "2017-09-18T08:39:46+00:00", "sha1": "cfd75871c03119093d7c96a6a99f21137d00c855", "complianceLevel": 0}, {"id": "1.12.2-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d0ab3dcd73f99e058fceef93780bcfb5dceb67e7/1.12.2-pre2.json", "time": "2021-12-15T15:44:21+00:00", "releaseTime": "2017-09-15T08:21:17+00:00", "sha1": "d0ab3dcd73f99e058fceef93780bcfb5dceb67e7", "complianceLevel": 0}, {"id": "1.12.2-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3aecfd3a13247d6541ee20478b4b91469f4bfc06/1.12.2-pre1.json", "time": "2021-12-15T15:44:21+00:00", "releaseTime": "2017-09-13T13:33:31+00:00", "sha1": "3aecfd3a13247d6541ee20478b4b91469f4bfc06", "complianceLevel": 0}, {"id": "1.12.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/ece2a75ac1eb6cabef04106a15f340a4cb87d86b/1.12.1.json", "time": "2021-12-15T15:43:52+00:00", "releaseTime": "2017-08-03T12:40:39+00:00", "sha1": "ece2a75ac1eb6cabef04106a15f340a4cb87d86b", "complianceLevel": 0}, {"id": "1.12.1-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f42b239c077b9b025d7b2bf764315040479c7c32/1.12.1-pre1.json", "time": "2021-12-15T15:44:20+00:00", "releaseTime": "2017-08-02T10:53:55+00:00", "sha1": "f42b239c077b9b025d7b2bf764315040479c7c32", "complianceLevel": 0}, {"id": "17w31a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7c12f84c7ed5ba230463d8875890a29c2fec45e1/17w31a.json", "time": "2021-12-15T15:46:23+00:00", "releaseTime": "2017-08-01T09:41:23+00:00", "sha1": "7c12f84c7ed5ba230463d8875890a29c2fec45e1", "complianceLevel": 0}, {"id": "1.12", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/fa3085f26ec90ef361352c3076e98aba6781b4b5/1.12.json", "time": "2021-12-15T15:43:53+00:00", "releaseTime": "2017-06-02T13:50:27+00:00", "sha1": "fa3085f26ec90ef361352c3076e98aba6781b4b5", "complianceLevel": 0}, {"id": "1.12-pre7", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0d423dd47bb49610bba145e8e76f62202ca762a8/1.12-pre7.json", "time": "2021-12-15T15:44:20+00:00", "releaseTime": "2017-05-31T10:56:41+00:00", "sha1": "0d423dd47bb49610bba145e8e76f62202ca762a8", "complianceLevel": 0}, {"id": "1.12-pre6", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9052c709ca5f730a813141b348091c239384a987/1.12-pre6.json", "time": "2021-12-15T15:44:20+00:00", "releaseTime": "2017-05-29T11:45:12+00:00", "sha1": "9052c709ca5f730a813141b348091c239384a987", "complianceLevel": 0}, {"id": "1.12-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d04e9e6aab9fbbbe0d22c14efa88df91316bd047/1.12-pre5.json", "time": "2021-12-15T15:44:19+00:00", "releaseTime": "2017-05-19T07:43:28+00:00", "sha1": "d04e9e6aab9fbbbe0d22c14efa88df91316bd047", "complianceLevel": 0}, {"id": "1.12-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/606b08d8fa71ccaf4fbb4399b9539032b0f2969a/1.12-pre4.json", "time": "2021-12-15T15:44:18+00:00", "releaseTime": "2017-05-18T12:28:16+00:00", "sha1": "606b08d8fa71ccaf4fbb4399b9539032b0f2969a", "complianceLevel": 0}, {"id": "1.12-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6a38ed63f8af119ac4aeb03410b025a312e6e890/1.12-pre3.json", "time": "2021-12-15T15:44:18+00:00", "releaseTime": "2017-05-17T14:09:18+00:00", "sha1": "6a38ed63f8af119ac4aeb03410b025a312e6e890", "complianceLevel": 0}, {"id": "1.12-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1e4135c2ff59143b436fae4300573cc49b479439/1.12-pre2.json", "time": "2021-12-15T15:44:18+00:00", "releaseTime": "2017-05-11T12:11:12+00:00", "sha1": "1e4135c2ff59143b436fae4300573cc49b479439", "complianceLevel": 0}, {"id": "1.12-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/333430ebb5102b6f96f3597e01c5fccef93a6b19/1.12-pre1.json", "time": "2021-12-15T15:44:17+00:00", "releaseTime": "2017-05-10T11:37:17+00:00", "sha1": "333430ebb5102b6f96f3597e01c5fccef93a6b19", "complianceLevel": 0}, {"id": "17w18b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a8e91e6b7e1beec49428fbeb1907f7fbe7b69d4e/17w18b.json", "time": "2021-12-15T15:46:22+00:00", "releaseTime": "2017-05-04T13:40:22+00:00", "sha1": "a8e91e6b7e1beec49428fbeb1907f7fbe7b69d4e", "complianceLevel": 0}, {"id": "17w18a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/616724ca0d52bfc6f8dcde854f3e23ce0903fafe/17w18a.json", "time": "2021-12-15T15:46:22+00:00", "releaseTime": "2017-05-03T14:50:23+00:00", "sha1": "616724ca0d52bfc6f8dcde854f3e23ce0903fafe", "complianceLevel": 0}, {"id": "17w17b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7aa9acd138aa9f67ab637b63e694389ce40a9b0c/17w17b.json", "time": "2021-12-15T15:46:21+00:00", "releaseTime": "2017-04-27T13:24:23+00:00", "sha1": "7aa9acd138aa9f67ab637b63e694389ce40a9b0c", "complianceLevel": 0}, {"id": "17w17a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/49d16540ce18168a33e7df1ef622cbec797f1f0f/17w17a.json", "time": "2021-12-15T15:46:21+00:00", "releaseTime": "2017-04-26T13:48:23+00:00", "sha1": "49d16540ce18168a33e7df1ef622cbec797f1f0f", "complianceLevel": 0}, {"id": "17w16b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/72a645ed45a018a5f25e59a7cfc963b55144c875/17w16b.json", "time": "2021-12-15T15:46:20+00:00", "releaseTime": "2017-04-21T12:02:59+00:00", "sha1": "72a645ed45a018a5f25e59a7cfc963b55144c875", "complianceLevel": 0}, {"id": "17w16a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5b21a349859d9bb104d35f0f27d0cd02d8a50212/17w16a.json", "time": "2021-12-15T15:46:20+00:00", "releaseTime": "2017-04-20T13:58:35+00:00", "sha1": "5b21a349859d9bb104d35f0f27d0cd02d8a50212", "complianceLevel": 0}, {"id": "17w15a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f906cf06c6a01c21ec4cf3a2ec788507ca3dacf8/17w15a.json", "time": "2021-12-15T15:46:19+00:00", "releaseTime": "2017-04-12T09:30:50+00:00", "sha1": "f906cf06c6a01c21ec4cf3a2ec788507ca3dacf8", "complianceLevel": 0}, {"id": "17w14a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d49a6949a6cd8545d24f580f64705f5e3826b840/17w14a.json", "time": "2021-12-13T14:56:26+00:00", "releaseTime": "2017-04-05T13:58:01+00:00", "sha1": "d49a6949a6cd8545d24f580f64705f5e3826b840", "complianceLevel": 0}, {"id": "17w13b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3ac23bb43d97324eea4fe4054a823a878db347eb/17w13b.json", "time": "2021-12-15T15:46:19+00:00", "releaseTime": "2017-03-31T11:06:35+00:00", "sha1": "3ac23bb43d97324eea4fe4054a823a878db347eb", "complianceLevel": 0}, {"id": "17w13a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/01f88db38661f78a52e21aefed8215e0fb01a6ee/17w13a.json", "time": "2021-12-15T15:46:18+00:00", "releaseTime": "2017-03-30T09:32:19+00:00", "sha1": "01f88db38661f78a52e21aefed8215e0fb01a6ee", "complianceLevel": 0}, {"id": "17w06a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a2a236d5ceb2838b44ff6e1291106b3a6de9ca21/17w06a.json", "time": "2021-12-15T15:46:18+00:00", "releaseTime": "2017-02-08T13:16:29+00:00", "sha1": "a2a236d5ceb2838b44ff6e1291106b3a6de9ca21", "complianceLevel": 0}, {"id": "1.11.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/4a7b205586fa9f827085c389ec8ce46a6df5fd94/1.11.2.json", "time": "2021-12-15T15:43:51+00:00", "releaseTime": "2016-12-21T09:29:12+00:00", "sha1": "4a7b205586fa9f827085c389ec8ce46a6df5fd94", "complianceLevel": 0}, {"id": "1.11.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/b4280e36205db0d7040c2fb1ecc8609dba675720/1.11.1.json", "time": "2021-12-15T15:43:51+00:00", "releaseTime": "2016-12-20T14:05:34+00:00", "sha1": "b4280e36205db0d7040c2fb1ecc8609dba675720", "complianceLevel": 0}, {"id": "16w50a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/febd9a04bec341431d89a4a6d27d87173df0bd6e/16w50a.json", "time": "2021-12-15T15:46:17+00:00", "releaseTime": "2016-12-15T14:38:52+00:00", "sha1": "febd9a04bec341431d89a4a6d27d87173df0bd6e", "complianceLevel": 0}, {"id": "1.11", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/ec94669865c0b0f6e9acfdb172985861790c8538/1.11.json", "time": "2021-12-15T15:43:52+00:00", "releaseTime": "2016-11-14T14:34:40+00:00", "sha1": "ec94669865c0b0f6e9acfdb172985861790c8538", "complianceLevel": 0}, {"id": "1.11-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1a8419e7379c2ce12ebafd79b1d28737071fc499/1.11-pre1.json", "time": "2021-12-15T15:44:17+00:00", "releaseTime": "2016-11-08T13:42:50+00:00", "sha1": "1a8419e7379c2ce12ebafd79b1d28737071fc499", "complianceLevel": 0}, {"id": "16w44a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dc2f38676cdd32671863c91f8573a135dbe21356/16w44a.json", "time": "2021-12-15T15:46:17+00:00", "releaseTime": "2016-11-03T14:17:11+00:00", "sha1": "dc2f38676cdd32671863c91f8573a135dbe21356", "complianceLevel": 0}, {"id": "16w43a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/130658ebc980c1decaaf73097df1e2c55b85f127/16w43a.json", "time": "2021-12-15T15:46:16+00:00", "releaseTime": "2016-10-27T09:00:51+00:00", "sha1": "130658ebc980c1decaaf73097df1e2c55b85f127", "complianceLevel": 0}, {"id": "16w42a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/989eda4f27b8585d7c36c7f01fec664639dcf3d7/16w42a.json", "time": "2021-12-15T15:46:16+00:00", "releaseTime": "2016-10-19T11:17:47+00:00", "sha1": "989eda4f27b8585d7c36c7f01fec664639dcf3d7", "complianceLevel": 0}, {"id": "16w41a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8d9a5e0b4bc0136bab50f37464dbf965ea552f32/16w41a.json", "time": "2021-12-15T15:46:16+00:00", "releaseTime": "2016-10-13T14:28:35+00:00", "sha1": "8d9a5e0b4bc0136bab50f37464dbf965ea552f32", "complianceLevel": 0}, {"id": "16w40a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/06888c9471858caab263061b2c9f4706c8e8271b/16w40a.json", "time": "2021-12-15T15:46:15+00:00", "releaseTime": "2016-10-06T13:57:59+00:00", "sha1": "06888c9471858caab263061b2c9f4706c8e8271b", "complianceLevel": 0}, {"id": "16w39c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e598bc854707defa1a672bdc783177e35fa8ec1b/16w39c.json", "time": "2021-12-15T15:46:15+00:00", "releaseTime": "2016-09-30T14:11:48+00:00", "sha1": "e598bc854707defa1a672bdc783177e35fa8ec1b", "complianceLevel": 0}, {"id": "16w39b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8dda45987a2c91c1ddf862d7b1b08f212cc5ff97/16w39b.json", "time": "2021-12-15T15:46:14+00:00", "releaseTime": "2016-09-29T14:39:39+00:00", "sha1": "8dda45987a2c91c1ddf862d7b1b08f212cc5ff97", "complianceLevel": 0}, {"id": "16w39a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/473a1f1a4ece5db81c062026c2a34aec9c5aeb45/16w39a.json", "time": "2021-12-15T15:46:14+00:00", "releaseTime": "2016-09-28T13:32:06+00:00", "sha1": "473a1f1a4ece5db81c062026c2a34aec9c5aeb45", "complianceLevel": 0}, {"id": "16w38a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/73db24801ef3a77b18c6af3332044409d0db300f/16w38a.json", "time": "2021-12-15T15:46:13+00:00", "releaseTime": "2016-09-20T12:40:49+00:00", "sha1": "73db24801ef3a77b18c6af3332044409d0db300f", "complianceLevel": 0}, {"id": "16w36a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/55d4bda92c3fc94b4ea0e5ec3764f47cb3301909/16w36a.json", "time": "2021-12-15T15:46:13+00:00", "releaseTime": "2016-09-08T14:55:10+00:00", "sha1": "55d4bda92c3fc94b4ea0e5ec3764f47cb3301909", "complianceLevel": 0}, {"id": "16w35a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3f2b102c366bac1b7cbe46f7ac8617c55f050ca7/16w35a.json", "time": "2021-12-15T15:46:12+00:00", "releaseTime": "2016-09-01T13:13:38+00:00", "sha1": "3f2b102c366bac1b7cbe46f7ac8617c55f050ca7", "complianceLevel": 0}, {"id": "16w33a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1673a47f0205fdb46aaca7132491b96ce3cdde88/16w33a.json", "time": "2021-12-15T15:46:12+00:00", "releaseTime": "2016-08-17T12:48:57+00:00", "sha1": "1673a47f0205fdb46aaca7132491b96ce3cdde88", "complianceLevel": 0}, {"id": "16w32b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3b78094b56811eb43a01e82a430a30db35a1bc2b/16w32b.json", "time": "2021-12-15T15:46:11+00:00", "releaseTime": "2016-08-11T14:34:29+00:00", "sha1": "3b78094b56811eb43a01e82a430a30db35a1bc2b", "complianceLevel": 0}, {"id": "16w32a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c2a086ced95735241b3a9bb7a4f4ad5af22a672c/16w32a.json", "time": "2021-12-15T15:46:11+00:00", "releaseTime": "2016-08-10T12:30:10+00:00", "sha1": "c2a086ced95735241b3a9bb7a4f4ad5af22a672c", "complianceLevel": 0}, {"id": "1.10.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/453f5b2785876c07c4723c5811d101132f2f6b74/1.10.2.json", "time": "2021-12-15T15:43:50+00:00", "releaseTime": "2016-06-23T09:17:32+00:00", "sha1": "453f5b2785876c07c4723c5811d101132f2f6b74", "complianceLevel": 0}, {"id": "1.10.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/6c26e69ed37120c0f6c93c3f69e7dc7b8e697ab8/1.10.1.json", "time": "2021-12-15T15:43:50+00:00", "releaseTime": "2016-06-22T10:13:22+00:00", "sha1": "6c26e69ed37120c0f6c93c3f69e7dc7b8e697ab8", "complianceLevel": 0}, {"id": "1.10", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/ed24421f4d1c6eae738a614ff1d9a60a05a700f2/1.10.json", "time": "2021-12-15T15:43:51+00:00", "releaseTime": "2016-06-08T13:06:18+00:00", "sha1": "ed24421f4d1c6eae738a614ff1d9a60a05a700f2", "complianceLevel": 0}, {"id": "1.10-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9adf95a19e6cfdcfb7b70da9946f3bbe4f77eb70/1.10-pre2.json", "time": "2021-12-15T15:44:16+00:00", "releaseTime": "2016-06-07T14:56:34+00:00", "sha1": "9adf95a19e6cfdcfb7b70da9946f3bbe4f77eb70", "complianceLevel": 0}, {"id": "1.10-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1a1804d54af75def8770336a46d598371bcbaef0/1.10-pre1.json", "time": "2021-12-15T15:44:16+00:00", "releaseTime": "2016-06-02T14:45:16+00:00", "sha1": "1a1804d54af75def8770336a46d598371bcbaef0", "complianceLevel": 0}, {"id": "16w21b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/168705060407358a42ddfbf657eb57e3fa3f29d3/16w21b.json", "time": "2021-12-15T15:46:10+00:00", "releaseTime": "2016-05-26T12:47:22+00:00", "sha1": "168705060407358a42ddfbf657eb57e3fa3f29d3", "complianceLevel": 0}, {"id": "16w21a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6a6706d50f29111c218ad9be1a77fd3b4a5c79d7/16w21a.json", "time": "2021-12-15T15:46:10+00:00", "releaseTime": "2016-05-25T13:12:09+00:00", "sha1": "6a6706d50f29111c218ad9be1a77fd3b4a5c79d7", "complianceLevel": 0}, {"id": "16w20a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/779532b8918db58fcbce6ba6b177c8fd44c6e08e/16w20a.json", "time": "2021-12-15T15:46:09+00:00", "releaseTime": "2016-05-18T12:45:14+00:00", "sha1": "779532b8918db58fcbce6ba6b177c8fd44c6e08e", "complianceLevel": 0}, {"id": "1.9.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/47a4367ff27db76067065807a97416711b7c7d09/1.9.4.json", "time": "2021-12-15T15:44:15+00:00", "releaseTime": "2016-05-10T10:17:16+00:00", "sha1": "47a4367ff27db76067065807a97416711b7c7d09", "complianceLevel": 0}, {"id": "1.9.3", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/6107cb3185da153f79be05b3f424804c130b2c88/1.9.3.json", "time": "2021-12-15T15:44:14+00:00", "releaseTime": "2016-05-10T08:33:35+00:00", "sha1": "6107cb3185da153f79be05b3f424804c130b2c88", "complianceLevel": 0}, {"id": "1.9.3-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2f6bb7de07c0784fe63d6edda19b0b80dcb45411/1.9.3-pre3.json", "time": "2021-12-15T15:45:15+00:00", "releaseTime": "2016-05-03T09:28:11+00:00", "sha1": "2f6bb7de07c0784fe63d6edda19b0b80dcb45411", "complianceLevel": 0}, {"id": "1.9.3-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c7b8426a21dabaddcf54c6fae4aa2a8c704e95b3/1.9.3-pre2.json", "time": "2021-12-15T15:45:15+00:00", "releaseTime": "2016-04-27T13:33:20+00:00", "sha1": "c7b8426a21dabaddcf54c6fae4aa2a8c704e95b3", "complianceLevel": 0}, {"id": "1.9.3-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8ebf237965d89a814b0d2075f5da41d13e3a714a/1.9.3-pre1.json", "time": "2021-12-15T15:45:14+00:00", "releaseTime": "2016-04-21T12:41:42+00:00", "sha1": "8ebf237965d89a814b0d2075f5da41d13e3a714a", "complianceLevel": 0}, {"id": "16w15b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9c04bc3453a5e1eea9cbbeb0c036cda8849fde95/16w15b.json", "time": "2021-12-15T15:46:09+00:00", "releaseTime": "2016-04-13T13:56:41+00:00", "sha1": "9c04bc3453a5e1eea9cbbeb0c036cda8849fde95", "complianceLevel": 0}, {"id": "16w15a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/18e789cfde0a8c02bbf9bf24102c2836fb24a412/16w15a.json", "time": "2021-12-15T15:46:08+00:00", "releaseTime": "2016-04-11T14:38:28+00:00", "sha1": "18e789cfde0a8c02bbf9bf24102c2836fb24a412", "complianceLevel": 0}, {"id": "16w14a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d35ef730a73c8862740fb7c85960e61889806f06/16w14a.json", "time": "2021-12-15T15:46:08+00:00", "releaseTime": "2016-04-07T12:47:51+00:00", "sha1": "d35ef730a73c8862740fb7c85960e61889806f06", "complianceLevel": 0}, {"id": "1.RV-Pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/268a225c8dbf8a2e6e17f1c2ac36192f046cd371/1.RV-Pre1.json", "time": "2021-12-15T15:45:15+00:00", "releaseTime": "2016-03-31T16:18:53+00:00", "sha1": "268a225c8dbf8a2e6e17f1c2ac36192f046cd371", "complianceLevel": 0}, {"id": "1.9.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/b0be54cf45695e324b9926f990434f9a0235bd9b/1.9.2.json", "time": "2021-12-15T15:44:14+00:00", "releaseTime": "2016-03-30T15:23:55+00:00", "sha1": "b0be54cf45695e324b9926f990434f9a0235bd9b", "complianceLevel": 0}, {"id": "1.9.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/cb32af124abf1bc87c38b788926b3e592126a77c/1.9.1.json", "time": "2021-12-15T15:44:13+00:00", "releaseTime": "2016-03-30T13:43:07+00:00", "sha1": "cb32af124abf1bc87c38b788926b3e592126a77c", "complianceLevel": 0}, {"id": "1.9.1-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e13d03fac8dbe3b518542da89e9fa8064b76d4a3/1.9.1-pre3.json", "time": "2021-12-15T15:45:14+00:00", "releaseTime": "2016-03-11T09:20:36+00:00", "sha1": "e13d03fac8dbe3b518542da89e9fa8064b76d4a3", "complianceLevel": 0}, {"id": "1.9.1-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c9e2a5309789b76381a5de3fe0c73bec4c58ea6f/1.9.1-pre2.json", "time": "2021-12-15T15:45:13+00:00", "releaseTime": "2016-03-10T15:06:03+00:00", "sha1": "c9e2a5309789b76381a5de3fe0c73bec4c58ea6f", "complianceLevel": 0}, {"id": "1.9.1-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5adf2e9a343daa3e0b1609de322ff3c78b7299ec/1.9.1-pre1.json", "time": "2021-12-15T15:45:13+00:00", "releaseTime": "2016-03-09T16:27:29+00:00", "sha1": "5adf2e9a343daa3e0b1609de322ff3c78b7299ec", "complianceLevel": 0}, {"id": "1.9", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/7bb4eab858d50c58d2855228110dfe1a4c2fa293/1.9.json", "time": "2021-12-15T15:44:15+00:00", "releaseTime": "2016-02-29T13:49:54+00:00", "sha1": "7bb4eab858d50c58d2855228110dfe1a4c2fa293", "complianceLevel": 0}, {"id": "1.9-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0e8a702dc4d23661786f97b96129478b78ca928c/1.9-pre4.json", "time": "2021-12-15T15:45:12+00:00", "releaseTime": "2016-02-26T15:21:11+00:00", "sha1": "0e8a702dc4d23661786f97b96129478b78ca928c", "complianceLevel": 0}, {"id": "1.9-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9123315c9933002d2524e5cbb7300b49845a164c/1.9-pre3.json", "time": "2021-12-15T15:45:12+00:00", "releaseTime": "2016-02-24T15:52:36+00:00", "sha1": "9123315c9933002d2524e5cbb7300b49845a164c", "complianceLevel": 0}, {"id": "1.9-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/14e95ebcd3fb703c84183b8e84931563b03f84bc/1.9-pre2.json", "time": "2021-12-15T15:45:11+00:00", "releaseTime": "2016-02-18T17:41:00+00:00", "sha1": "14e95ebcd3fb703c84183b8e84931563b03f84bc", "complianceLevel": 0}, {"id": "1.9-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/712caae8ee8d75215896d17c5676dc15759d4079/1.9-pre1.json", "time": "2021-12-15T15:45:11+00:00", "releaseTime": "2016-02-17T15:23:19+00:00", "sha1": "712caae8ee8d75215896d17c5676dc15759d4079", "complianceLevel": 0}, {"id": "16w07b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/73d28ebef8689c0f9fd458da1988ebe95a3ba071/16w07b.json", "time": "2021-12-15T15:46:07+00:00", "releaseTime": "2016-02-16T15:22:39+00:00", "sha1": "73d28ebef8689c0f9fd458da1988ebe95a3ba071", "complianceLevel": 0}, {"id": "16w07a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7bf8e32bbdb481d7349f1cd283b17946827e8b60/16w07a.json", "time": "2021-12-15T15:46:07+00:00", "releaseTime": "2016-02-15T15:48:46+00:00", "sha1": "7bf8e32bbdb481d7349f1cd283b17946827e8b60", "complianceLevel": 0}, {"id": "16w06a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/58edeaa7b22cb9866aba1c9615b732a3db354add/16w06a.json", "time": "2021-12-15T15:46:07+00:00", "releaseTime": "2016-02-10T15:06:41+00:00", "sha1": "58edeaa7b22cb9866aba1c9615b732a3db354add", "complianceLevel": 0}, {"id": "16w05b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c24ea3b03a91bdbc7d8120eaf633acac101cd76c/16w05b.json", "time": "2021-12-15T15:46:06+00:00", "releaseTime": "2016-02-04T15:28:02+00:00", "sha1": "c24ea3b03a91bdbc7d8120eaf633acac101cd76c", "complianceLevel": 0}, {"id": "16w05a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a9d135bfd2b15c171528a08f149de202f036e157/16w05a.json", "time": "2021-12-15T15:46:06+00:00", "releaseTime": "2016-02-03T15:48:38+00:00", "sha1": "a9d135bfd2b15c171528a08f149de202f036e157", "complianceLevel": 0}, {"id": "16w04a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/aa0ba9a603bca8afa4b680a792aaabb88c93d016/16w04a.json", "time": "2021-12-15T15:46:05+00:00", "releaseTime": "2016-01-28T15:37:24+00:00", "sha1": "aa0ba9a603bca8afa4b680a792aaabb88c93d016", "complianceLevel": 0}, {"id": "16w03a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6a8c9b966ed051967cf2448924b6f73094b90ef2/16w03a.json", "time": "2021-12-15T15:46:05+00:00", "releaseTime": "2016-01-20T14:29:24+00:00", "sha1": "6a8c9b966ed051967cf2448924b6f73094b90ef2", "complianceLevel": 0}, {"id": "16w02a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7f1b36e80f5ede72bc1662e9b48036a1e2292abf/16w02a.json", "time": "2021-12-15T15:46:04+00:00", "releaseTime": "2016-01-13T15:15:16+00:00", "sha1": "7f1b36e80f5ede72bc1662e9b48036a1e2292abf", "complianceLevel": 0}, {"id": "15w51b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/16cb387f44a1585e64f8103142bd2910385ae65b/15w51b.json", "time": "2021-12-15T15:46:04+00:00", "releaseTime": "2015-12-17T15:30:41+00:00", "sha1": "16cb387f44a1585e64f8103142bd2910385ae65b", "complianceLevel": 0}, {"id": "15w51a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8c15fe5539b86e5c17b227c08f2d4849638fbb5d/15w51a.json", "time": "2021-12-15T15:46:03+00:00", "releaseTime": "2015-12-17T14:02:37+00:00", "sha1": "8c15fe5539b86e5c17b227c08f2d4849638fbb5d", "complianceLevel": 0}, {"id": "15w50a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/233d5225a6c9538d47179c781583df31433d3008/15w50a.json", "time": "2021-12-15T15:46:03+00:00", "releaseTime": "2015-12-09T15:35:57+00:00", "sha1": "233d5225a6c9538d47179c781583df31433d3008", "complianceLevel": 0}, {"id": "15w49b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dc7c054bb890de4300429b712957c429ea72ac74/15w49b.json", "time": "2021-12-15T15:46:02+00:00", "releaseTime": "2015-12-03T15:23:22+00:00", "sha1": "dc7c054bb890de4300429b712957c429ea72ac74", "complianceLevel": 0}, {"id": "1.8.9", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/d546f1707a3f2b7d034eece5ea2e311eda875787/1.8.9.json", "time": "2021-12-15T15:44:12+00:00", "releaseTime": "2015-12-03T09:24:39+00:00", "sha1": "d546f1707a3f2b7d034eece5ea2e311eda875787", "complianceLevel": 0}, {"id": "15w49a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5bd5085de9b7920da92423049be9c50e29ee492b/15w49a.json", "time": "2021-12-15T15:46:02+00:00", "releaseTime": "2015-12-02T15:09:37+00:00", "sha1": "5bd5085de9b7920da92423049be9c50e29ee492b", "complianceLevel": 0}, {"id": "15w47c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/aa59e973582a88e9437238aabec2a13b27a343ec/15w47c.json", "time": "2021-12-15T15:46:01+00:00", "releaseTime": "2015-11-20T12:46:56+00:00", "sha1": "aa59e973582a88e9437238aabec2a13b27a343ec", "complianceLevel": 0}, {"id": "15w47b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b11b494533bebdf2e261ac25c85fff5847b3ce82/15w47b.json", "time": "2021-12-15T15:46:01+00:00", "releaseTime": "2015-11-19T14:48:03+00:00", "sha1": "b11b494533bebdf2e261ac25c85fff5847b3ce82", "complianceLevel": 0}, {"id": "15w47a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5e26942d1168acd1722b51c9d7736f14e66954dc/15w47a.json", "time": "2021-12-15T15:46:00+00:00", "releaseTime": "2015-11-18T15:53:41+00:00", "sha1": "5e26942d1168acd1722b51c9d7736f14e66954dc", "complianceLevel": 0}, {"id": "15w46a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/bdd0e0ba0769c4008cf7bc70df561657308d91c6/15w46a.json", "time": "2021-12-15T15:46:00+00:00", "releaseTime": "2015-11-12T12:11:47+00:00", "sha1": "bdd0e0ba0769c4008cf7bc70df561657308d91c6", "complianceLevel": 0}, {"id": "15w45a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/de6ed42afdcc54fbcd63436f82874a0656582d01/15w45a.json", "time": "2021-12-15T15:45:59+00:00", "releaseTime": "2015-11-05T13:04:07+00:00", "sha1": "de6ed42afdcc54fbcd63436f82874a0656582d01", "complianceLevel": 0}, {"id": "15w44b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4b68abcd91cd5f8edeb200fe78f2b832f719d8dd/15w44b.json", "time": "2021-12-15T15:45:59+00:00", "releaseTime": "2015-10-30T11:23:17+00:00", "sha1": "4b68abcd91cd5f8edeb200fe78f2b832f719d8dd", "complianceLevel": 0}, {"id": "15w44a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5ac6e4336887fc3300955e705f395095855b1a65/15w44a.json", "time": "2021-12-15T15:45:58+00:00", "releaseTime": "2015-10-28T15:09:36+00:00", "sha1": "5ac6e4336887fc3300955e705f395095855b1a65", "complianceLevel": 0}, {"id": "15w43c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1cd6c443b67d6bcf7e4e0fee3aa0288a3497cd88/15w43c.json", "time": "2021-12-15T15:45:58+00:00", "releaseTime": "2015-10-23T15:35:55+00:00", "sha1": "1cd6c443b67d6bcf7e4e0fee3aa0288a3497cd88", "complianceLevel": 0}, {"id": "15w43b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dbcdd656e950cb52b9eb933094eaf94e415a151e/15w43b.json", "time": "2021-12-15T15:45:57+00:00", "releaseTime": "2015-10-22T14:11:58+00:00", "sha1": "dbcdd656e950cb52b9eb933094eaf94e415a151e", "complianceLevel": 0}, {"id": "15w43a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5da66280e34f0cdbb5fe0d463deb4916d0181a64/15w43a.json", "time": "2021-12-15T15:45:57+00:00", "releaseTime": "2015-10-21T15:28:52+00:00", "sha1": "5da66280e34f0cdbb5fe0d463deb4916d0181a64", "complianceLevel": 0}, {"id": "15w42a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7b18842c1dbbe945d991851ffaff49ecd7efbb91/15w42a.json", "time": "2021-12-15T15:45:57+00:00", "releaseTime": "2015-10-14T13:25:14+00:00", "sha1": "7b18842c1dbbe945d991851ffaff49ecd7efbb91", "complianceLevel": 0}, {"id": "15w41b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/27ab9716cf332d457be085a808eb170fe1a1ff2c/15w41b.json", "time": "2021-12-15T15:45:56+00:00", "releaseTime": "2015-10-07T14:07:26+00:00", "sha1": "27ab9716cf332d457be085a808eb170fe1a1ff2c", "complianceLevel": 0}, {"id": "15w41a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/38026b4c0301e3441d8e0d5b367392d3212dc51d/15w41a.json", "time": "2021-12-15T15:45:56+00:00", "releaseTime": "2015-10-07T13:19:53+00:00", "sha1": "38026b4c0301e3441d8e0d5b367392d3212dc51d", "complianceLevel": 0}, {"id": "15w40b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a00ee3d8eb0c1cb38d928f5efb4a473768fa5286/15w40b.json", "time": "2021-12-15T15:45:56+00:00", "releaseTime": "2015-09-30T14:13:54+00:00", "sha1": "a00ee3d8eb0c1cb38d928f5efb4a473768fa5286", "complianceLevel": 0}, {"id": "15w40a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f5c9662f1674f7f0e55e82a77109ad88a6bc9df7/15w40a.json", "time": "2021-12-15T15:45:55+00:00", "releaseTime": "2015-09-30T13:13:54+00:00", "sha1": "f5c9662f1674f7f0e55e82a77109ad88a6bc9df7", "complianceLevel": 0}, {"id": "15w39c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/134e9d87a2c86e82d98bc7f8ba5965b7fd6634c1/15w39c.json", "time": "2021-12-15T15:45:55+00:00", "releaseTime": "2015-09-23T13:13:54+00:00", "sha1": "134e9d87a2c86e82d98bc7f8ba5965b7fd6634c1", "complianceLevel": 0}, {"id": "15w39b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/909aa3af2988edc58b1e9c66f906a883d9a34815/15w39b.json", "time": "2021-12-15T15:45:54+00:00", "releaseTime": "2015-09-21T15:09:52+00:00", "sha1": "909aa3af2988edc58b1e9c66f906a883d9a34815", "complianceLevel": 0}, {"id": "15w39a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7f914aa1098b8b35e20e9c021b57378d0a8fcc6c/15w39a.json", "time": "2021-12-15T15:45:54+00:00", "releaseTime": "2015-09-21T13:16:32+00:00", "sha1": "7f914aa1098b8b35e20e9c021b57378d0a8fcc6c", "complianceLevel": 0}, {"id": "15w38b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/222a234de865b9147cad4e3b2545d8af93c35065/15w38b.json", "time": "2021-12-15T15:45:54+00:00", "releaseTime": "2015-09-17T14:22:31+00:00", "sha1": "222a234de865b9147cad4e3b2545d8af93c35065", "complianceLevel": 0}, {"id": "15w38a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1b4dcb4c07599a5e5571d9d71570641086e821e4/15w38a.json", "time": "2021-12-15T15:45:53+00:00", "releaseTime": "2015-09-16T14:22:31+00:00", "sha1": "1b4dcb4c07599a5e5571d9d71570641086e821e4", "complianceLevel": 0}, {"id": "15w37a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9459c8c56ba4577b69cc12a9034769f0d20fb3dc/15w37a.json", "time": "2021-12-15T15:45:53+00:00", "releaseTime": "2015-09-10T14:22:31+00:00", "sha1": "9459c8c56ba4577b69cc12a9034769f0d20fb3dc", "complianceLevel": 0}, {"id": "15w36d", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/49a6f0d3a233d103b5fdeb6aefd7a6b0208b5ce9/15w36d.json", "time": "2021-12-15T15:45:53+00:00", "releaseTime": "2015-09-04T14:22:31+00:00", "sha1": "49a6f0d3a233d103b5fdeb6aefd7a6b0208b5ce9", "complianceLevel": 0}, {"id": "15w36c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b1a04bae902de441013791777aa0461d446bd2c7/15w36c.json", "time": "2021-12-15T15:45:52+00:00", "releaseTime": "2015-09-02T16:07:22+00:00", "sha1": "b1a04bae902de441013791777aa0461d446bd2c7", "complianceLevel": 0}, {"id": "15w36b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2268159e9ce438054660ac86d55db3ddfed1e20b/15w36b.json", "time": "2021-12-15T15:45:52+00:00", "releaseTime": "2015-09-02T15:36:25+00:00", "sha1": "2268159e9ce438054660ac86d55db3ddfed1e20b", "complianceLevel": 0}, {"id": "15w36a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c185b19d54c00e43f85c610858909c40f9163708/15w36a.json", "time": "2021-12-15T15:45:51+00:00", "releaseTime": "2015-09-02T14:46:40+00:00", "sha1": "c185b19d54c00e43f85c610858909c40f9163708", "complianceLevel": 0}, {"id": "15w35e", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1fade4fe9d2587106ac3fa14775f9126d3198103/15w35e.json", "time": "2021-12-15T15:45:51+00:00", "releaseTime": "2015-08-28T18:14:02+00:00", "sha1": "1fade4fe9d2587106ac3fa14775f9126d3198103", "complianceLevel": 0}, {"id": "15w35d", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f7eeb1ed37ec5c38cd8a04ba1fe90a50a1b24752/15w35d.json", "time": "2021-12-15T15:45:51+00:00", "releaseTime": "2015-08-28T16:25:35+00:00", "sha1": "f7eeb1ed37ec5c38cd8a04ba1fe90a50a1b24752", "complianceLevel": 0}, {"id": "15w35c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/362bda12d03da60b8481c79f4779bba7a2602c89/15w35c.json", "time": "2021-12-15T15:45:50+00:00", "releaseTime": "2015-08-28T11:21:00+00:00", "sha1": "362bda12d03da60b8481c79f4779bba7a2602c89", "complianceLevel": 0}, {"id": "15w35b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/fda82bd0b305eba9607742b9314a41a54f0a9952/15w35b.json", "time": "2021-12-15T15:45:50+00:00", "releaseTime": "2015-08-24T15:39:18+00:00", "sha1": "fda82bd0b305eba9607742b9314a41a54f0a9952", "complianceLevel": 0}, {"id": "15w35a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/801ae2c7be9b3e558a7aef6c766bddb74d4be66a/15w35a.json", "time": "2021-12-15T15:45:50+00:00", "releaseTime": "2015-08-24T14:19:31+00:00", "sha1": "801ae2c7be9b3e558a7aef6c766bddb74d4be66a", "complianceLevel": 0}, {"id": "15w34d", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3c0d347369147a3951163679127a5bf8e78be5a2/15w34d.json", "time": "2021-12-15T15:45:49+00:00", "releaseTime": "2015-08-21T15:27:55+00:00", "sha1": "3c0d347369147a3951163679127a5bf8e78be5a2", "complianceLevel": 0}, {"id": "15w34c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/44d6774de6881154fd5f9ac6154dee5f202eb121/15w34c.json", "time": "2021-12-15T15:45:49+00:00", "releaseTime": "2015-08-21T12:45:20+00:00", "sha1": "44d6774de6881154fd5f9ac6154dee5f202eb121", "complianceLevel": 0}, {"id": "15w34b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3bec850cf623d63b26caf462b0be177bc9954a0a/15w34b.json", "time": "2021-12-15T15:45:48+00:00", "releaseTime": "2015-08-20T14:00:03+00:00", "sha1": "3bec850cf623d63b26caf462b0be177bc9954a0a", "complianceLevel": 0}, {"id": "15w34a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b8fdc5838196c33646a68610ed7586678a53b085/15w34a.json", "time": "2021-12-15T15:45:48+00:00", "releaseTime": "2015-08-19T12:56:01+00:00", "sha1": "b8fdc5838196c33646a68610ed7586678a53b085", "complianceLevel": 0}, {"id": "15w33c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c602ac43b9f2c23b4c39999a6368bbccd31852ca/15w33c.json", "time": "2021-12-15T15:45:48+00:00", "releaseTime": "2015-08-14T13:10:46+00:00", "sha1": "c602ac43b9f2c23b4c39999a6368bbccd31852ca", "complianceLevel": 0}, {"id": "15w33b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c108562e1ee548ba05439441e97fdcb0b01d1194/15w33b.json", "time": "2021-12-15T15:45:47+00:00", "releaseTime": "2015-08-12T15:29:11+00:00", "sha1": "c108562e1ee548ba05439441e97fdcb0b01d1194", "complianceLevel": 0}, {"id": "15w33a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e5b7e666fd697aef925f539d3983d03ed5d4457e/15w33a.json", "time": "2021-12-15T15:45:47+00:00", "releaseTime": "2015-08-12T14:05:07+00:00", "sha1": "e5b7e666fd697aef925f539d3983d03ed5d4457e", "complianceLevel": 0}, {"id": "15w32c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2bf1e36bc4099bba503063ec507b7600ca088e4c/15w32c.json", "time": "2021-12-15T15:45:46+00:00", "releaseTime": "2015-08-07T14:08:17+00:00", "sha1": "2bf1e36bc4099bba503063ec507b7600ca088e4c", "complianceLevel": 0}, {"id": "15w32b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/59db82d67a0a1c6b6a31d22cd050ff6c43f1611b/15w32b.json", "time": "2021-12-15T15:45:46+00:00", "releaseTime": "2015-08-06T13:51:47+00:00", "sha1": "59db82d67a0a1c6b6a31d22cd050ff6c43f1611b", "complianceLevel": 0}, {"id": "15w32a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4e80b2295bdb531e64326c4d3fbf93e8d330c5e7/15w32a.json", "time": "2021-12-15T15:45:45+00:00", "releaseTime": "2015-08-05T12:22:42+00:00", "sha1": "4e80b2295bdb531e64326c4d3fbf93e8d330c5e7", "complianceLevel": 0}, {"id": "15w31c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1345a9c6d2442415e5980c12fd7bd838072dbd90/15w31c.json", "time": "2021-12-15T15:45:44+00:00", "releaseTime": "2015-07-31T13:45:08+00:00", "sha1": "1345a9c6d2442415e5980c12fd7bd838072dbd90", "complianceLevel": 0}, {"id": "15w31b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c9c8d4b706ebb7a057bb91ac69d84ea060b8d174/15w31b.json", "time": "2021-12-15T15:45:44+00:00", "releaseTime": "2015-07-30T13:38:32+00:00", "sha1": "c9c8d4b706ebb7a057bb91ac69d84ea060b8d174", "complianceLevel": 0}, {"id": "15w31a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4c9ad2064a55148bef3da7d98dde8d9ff50596ea/15w31a.json", "time": "2021-12-15T15:45:43+00:00", "releaseTime": "2015-07-29T13:24:33+00:00", "sha1": "4c9ad2064a55148bef3da7d98dde8d9ff50596ea", "complianceLevel": 0}, {"id": "1.8.8", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/690172f1227e1c1d2fa8fceadd0f578f7851a69e/1.8.8.json", "time": "2021-12-15T15:44:12+00:00", "releaseTime": "2015-07-27T10:31:28+00:00", "sha1": "690172f1227e1c1d2fa8fceadd0f578f7851a69e", "complianceLevel": 0}, {"id": "1.8.7", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/7152f102903cd3ce7514d84c8ac98efecac30839/1.8.7.json", "time": "2021-12-15T15:44:11+00:00", "releaseTime": "2015-06-05T10:10:44+00:00", "sha1": "7152f102903cd3ce7514d84c8ac98efecac30839", "complianceLevel": 0}, {"id": "1.8.6", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/acccbb056a3e8f3086c4614974fb3a894317853a/1.8.6.json", "time": "2021-12-15T15:44:11+00:00", "releaseTime": "2015-05-25T10:31:19+00:00", "sha1": "acccbb056a3e8f3086c4614974fb3a894317853a", "complianceLevel": 0}, {"id": "1.8.5", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/16da5f5be7478f3602c25182a90f2057ba2f60d8/1.8.5.json", "time": "2021-12-15T15:44:11+00:00", "releaseTime": "2015-05-22T11:15:28+00:00", "sha1": "16da5f5be7478f3602c25182a90f2057ba2f60d8", "complianceLevel": 0}, {"id": "1.8.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/043b84efde9fc25d849e979329c03101ac9b7795/1.8.4.json", "time": "2021-12-15T15:44:10+00:00", "releaseTime": "2015-04-17T11:37:50+00:00", "sha1": "043b84efde9fc25d849e979329c03101ac9b7795", "complianceLevel": 0}, {"id": "15w14a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e9349d236c0f4b8760f75621b3eaad538055c759/15w14a.json", "time": "2021-12-15T15:45:43+00:00", "releaseTime": "2015-04-01T07:08:00+00:00", "sha1": "e9349d236c0f4b8760f75621b3eaad538055c759", "complianceLevel": 0}, {"id": "1.8.3", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/413ad8fdcf8f29f4f1c2b4425aa0dade00f75dd4/1.8.3.json", "time": "2021-12-15T15:44:10+00:00", "releaseTime": "2015-02-20T14:00:09+00:00", "sha1": "413ad8fdcf8f29f4f1c2b4425aa0dade00f75dd4", "complianceLevel": 0}, {"id": "1.8.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/1e35829856a71261f5a7b2b3a83012c7434b2203/1.8.2.json", "time": "2021-12-15T15:44:09+00:00", "releaseTime": "2015-02-19T15:47:29+00:00", "sha1": "1e35829856a71261f5a7b2b3a83012c7434b2203", "complianceLevel": 0}, {"id": "1.8.2-pre7", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d07a07da8ffe68a0929710c72346073a02a94933/1.8.2-pre7.json", "time": "2021-12-15T15:45:10+00:00", "releaseTime": "2015-02-16T13:01:35+00:00", "sha1": "d07a07da8ffe68a0929710c72346073a02a94933", "complianceLevel": 0}, {"id": "1.8.2-pre6", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/07ae1d9b67d36399e239a2ff46d43a1f96ef141a/1.8.2-pre6.json", "time": "2021-12-15T15:45:10+00:00", "releaseTime": "2015-01-30T11:58:24+00:00", "sha1": "07ae1d9b67d36399e239a2ff46d43a1f96ef141a", "complianceLevel": 0}, {"id": "1.8.2-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/08e7827173655705e938233cf148f955b4f67901/1.8.2-pre5.json", "time": "2021-12-15T15:45:09+00:00", "releaseTime": "2015-01-26T15:03:24+00:00", "sha1": "08e7827173655705e938233cf148f955b4f67901", "complianceLevel": 0}, {"id": "1.8.2-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/db4c583ec25447f7418fc8e458798905a1282b88/1.8.2-pre4.json", "time": "2021-12-15T15:45:09+00:00", "releaseTime": "2015-01-16T14:19:59+00:00", "sha1": "db4c583ec25447f7418fc8e458798905a1282b88", "complianceLevel": 0}, {"id": "1.8.2-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3d371e2f8c3b04b44c76ae908a1e6f800895aa80/1.8.2-pre3.json", "time": "2021-12-15T15:45:08+00:00", "releaseTime": "2015-01-15T16:44:33+00:00", "sha1": "3d371e2f8c3b04b44c76ae908a1e6f800895aa80", "complianceLevel": 0}, {"id": "1.8.2-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/50c6a27cbbd2df6adff74aaea9953a00c6ce38e3/1.8.2-pre2.json", "time": "2021-12-15T15:45:08+00:00", "releaseTime": "2015-01-15T15:07:31+00:00", "sha1": "50c6a27cbbd2df6adff74aaea9953a00c6ce38e3", "complianceLevel": 0}, {"id": "1.8.2-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/79c984156100e076c37316d6d6377ced3daaf379/1.8.2-pre1.json", "time": "2021-12-15T15:45:07+00:00", "releaseTime": "2014-12-18T11:29:41+00:00", "sha1": "79c984156100e076c37316d6d6377ced3daaf379", "complianceLevel": 0}, {"id": "1.8.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/62f9f77f67fd7d6c92cfae57cecd445be14ccd4e/1.8.1.json", "time": "2021-12-15T15:44:09+00:00", "releaseTime": "2014-11-24T14:13:31+00:00", "sha1": "62f9f77f67fd7d6c92cfae57cecd445be14ccd4e", "complianceLevel": 0}, {"id": "1.8.1-pre5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4460bbf278fef1312f03c1483599c31149fe85a7/1.8.1-pre5.json", "time": "2021-12-15T15:45:07+00:00", "releaseTime": "2014-11-19T14:30:48+00:00", "sha1": "4460bbf278fef1312f03c1483599c31149fe85a7", "complianceLevel": 0}, {"id": "1.8.1-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e9024ad2831e3fc4450977aafe7bb3d07ff0d542/1.8.1-pre4.json", "time": "2021-12-15T15:45:06+00:00", "releaseTime": "2014-11-06T14:10:50+00:00", "sha1": "e9024ad2831e3fc4450977aafe7bb3d07ff0d542", "complianceLevel": 0}, {"id": "1.8.1-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c2f5fb57bf63012594b3dc34a80a8d888ec017ac/1.8.1-pre3.json", "time": "2021-12-15T15:45:06+00:00", "releaseTime": "2014-10-23T12:59:42+00:00", "sha1": "c2f5fb57bf63012594b3dc34a80a8d888ec017ac", "complianceLevel": 0}, {"id": "1.8.1-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8e77dedf93135552e1aaaebcec4c8d154d4dea04/1.8.1-pre2.json", "time": "2021-12-15T15:45:05+00:00", "releaseTime": "2014-10-16T14:19:27+00:00", "sha1": "8e77dedf93135552e1aaaebcec4c8d154d4dea04", "complianceLevel": 0}, {"id": "1.8.1-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f470d54d32f9e1d1a02e4e84e33b2407325ab62d/1.8.1-pre1.json", "time": "2021-12-15T15:45:05+00:00", "releaseTime": "2014-10-15T13:25:11+00:00", "sha1": "f470d54d32f9e1d1a02e4e84e33b2407325ab62d", "complianceLevel": 0}, {"id": "1.8", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/9eb165eef46294062d8698c8a78e8ac914949e7a/1.8.json", "time": "2021-12-15T15:44:13+00:00", "releaseTime": "2014-09-02T08:24:35+00:00", "sha1": "9eb165eef46294062d8698c8a78e8ac914949e7a", "complianceLevel": 0}, {"id": "1.8-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7fd3abc53ee1f813f5b574c8dc758fd694b6abd3/1.8-pre3.json", "time": "2021-12-15T15:45:05+00:00", "releaseTime": "2014-08-28T09:40:54+00:00", "sha1": "7fd3abc53ee1f813f5b574c8dc758fd694b6abd3", "complianceLevel": 0}, {"id": "1.8-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2c6bf8e3d17565117bbb5e188544439518d3ad95/1.8-pre2.json", "time": "2021-12-15T15:45:04+00:00", "releaseTime": "2014-08-25T14:52:18+00:00", "sha1": "2c6bf8e3d17565117bbb5e188544439518d3ad95", "complianceLevel": 0}, {"id": "1.8-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/00ddc59925abc10e08047c94657e3365b1e031d6/1.8-pre1.json", "time": "2021-12-15T15:45:04+00:00", "releaseTime": "2014-08-21T13:56:26+00:00", "sha1": "00ddc59925abc10e08047c94657e3365b1e031d6", "complianceLevel": 0}, {"id": "14w34d", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e314c5316fc189a9883f5a786a6b9daffcff8e84/14w34d.json", "time": "2021-12-15T15:45:43+00:00", "releaseTime": "2014-08-20T12:46:59+00:00", "sha1": "e314c5316fc189a9883f5a786a6b9daffcff8e84", "complianceLevel": 0}, {"id": "14w34c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a82b66557f41c05f41477481e39be5d5ceec3c62/14w34c.json", "time": "2021-12-15T15:45:42+00:00", "releaseTime": "2014-08-19T15:31:24+00:00", "sha1": "a82b66557f41c05f41477481e39be5d5ceec3c62", "complianceLevel": 0}, {"id": "14w34b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f24d3b4a363411fec20a36c2bd92a62b628be003/14w34b.json", "time": "2021-12-15T15:45:42+00:00", "releaseTime": "2014-08-18T15:14:28+00:00", "sha1": "f24d3b4a363411fec20a36c2bd92a62b628be003", "complianceLevel": 0}, {"id": "14w34a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dc020c20f2d7a79dee3f601317a8a7cb191c0538/14w34a.json", "time": "2021-12-15T15:45:41+00:00", "releaseTime": "2014-08-18T14:14:11+00:00", "sha1": "dc020c20f2d7a79dee3f601317a8a7cb191c0538", "complianceLevel": 0}, {"id": "14w33c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/de314d5c6f011057f764e546a212731d40736c1f/14w33c.json", "time": "2021-12-15T15:45:41+00:00", "releaseTime": "2014-08-15T18:00:26+00:00", "sha1": "de314d5c6f011057f764e546a212731d40736c1f", "complianceLevel": 0}, {"id": "14w33b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dec4943bf73d83402d455243f83c8c550e36f7b5/14w33b.json", "time": "2021-12-15T15:45:40+00:00", "releaseTime": "2014-08-15T16:23:51+00:00", "sha1": "dec4943bf73d83402d455243f83c8c550e36f7b5", "complianceLevel": 0}, {"id": "14w33a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a1a938359171774c96e06f54bc0c12352a9d7992/14w33a.json", "time": "2021-12-15T15:45:40+00:00", "releaseTime": "2014-08-13T15:08:14+00:00", "sha1": "a1a938359171774c96e06f54bc0c12352a9d7992", "complianceLevel": 0}, {"id": "14w32d", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/414e33e7d03d873966c199d959426da08aa5aebc/14w32d.json", "time": "2021-12-15T15:45:40+00:00", "releaseTime": "2014-08-08T15:13:41+00:00", "sha1": "414e33e7d03d873966c199d959426da08aa5aebc", "complianceLevel": 0}, {"id": "14w32c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3dfa2c82cd06c89735ac0c9b05151b4994efab19/14w32c.json", "time": "2021-12-15T15:45:39+00:00", "releaseTime": "2014-08-08T14:11:20+00:00", "sha1": "3dfa2c82cd06c89735ac0c9b05151b4994efab19", "complianceLevel": 0}, {"id": "14w32b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6230265cc1f324689cb5fbb7df21235ca0013e70/14w32b.json", "time": "2021-12-15T15:45:39+00:00", "releaseTime": "2014-08-07T14:45:17+00:00", "sha1": "6230265cc1f324689cb5fbb7df21235ca0013e70", "complianceLevel": 0}, {"id": "14w32a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3af16f96e7800a8f0c31500a873b008c00036c84/14w32a.json", "time": "2021-12-15T15:45:38+00:00", "releaseTime": "2014-08-06T14:01:16+00:00", "sha1": "3af16f96e7800a8f0c31500a873b008c00036c84", "complianceLevel": 0}, {"id": "14w31a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1243fe2c047064613e42a2c0e7ce0018fdf94035/14w31a.json", "time": "2021-12-15T15:45:38+00:00", "releaseTime": "2014-07-30T15:38:05+00:00", "sha1": "1243fe2c047064613e42a2c0e7ce0018fdf94035", "complianceLevel": 0}, {"id": "14w30c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6327714a7712f723bb74492e2f5fcbb92b8b12a9/14w30c.json", "time": "2021-12-15T15:45:37+00:00", "releaseTime": "2014-07-24T14:39:09+00:00", "sha1": "6327714a7712f723bb74492e2f5fcbb92b8b12a9", "complianceLevel": 0}, {"id": "14w30b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/dddd3cf12b88f179baa286a9bb51f4c3902c3780/14w30b.json", "time": "2021-12-15T15:45:37+00:00", "releaseTime": "2014-07-23T15:03:03+00:00", "sha1": "dddd3cf12b88f179baa286a9bb51f4c3902c3780", "complianceLevel": 0}, {"id": "14w30a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f73b4bad130911c1ef16066aeb740574f20f90f7/14w30a.json", "time": "2021-12-15T15:45:36+00:00", "releaseTime": "2014-07-23T13:15:42+00:00", "sha1": "f73b4bad130911c1ef16066aeb740574f20f90f7", "complianceLevel": 0}, {"id": "14w29b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/02a4803aa427d29f9db910d22b263686d0135fcc/14w29b.json", "time": "2021-12-15T15:45:36+00:00", "releaseTime": "2014-07-16T17:27:40+00:00", "sha1": "02a4803aa427d29f9db910d22b263686d0135fcc", "complianceLevel": 0}, {"id": "14w29a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5219707e16e90d57fbba55f635b68e4725d72b8f/14w29a.json", "time": "2021-12-15T15:45:36+00:00", "releaseTime": "2014-07-16T15:18:17+00:00", "sha1": "5219707e16e90d57fbba55f635b68e4725d72b8f", "complianceLevel": 0}, {"id": "14w28b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c18437b0194fb2af464b5fe0cb67ed0eeafb44e8/14w28b.json", "time": "2021-12-15T15:45:35+00:00", "releaseTime": "2014-07-10T14:28:48+00:00", "sha1": "c18437b0194fb2af464b5fe0cb67ed0eeafb44e8", "complianceLevel": 0}, {"id": "14w28a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6d6b755e76453633e464ed09f82b0979a414d8e4/14w28a.json", "time": "2021-12-15T15:45:35+00:00", "releaseTime": "2014-07-09T15:42:36+00:00", "sha1": "6d6b755e76453633e464ed09f82b0979a414d8e4", "complianceLevel": 0}, {"id": "14w27b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/08f1ed6374fb9d87e34fe81ab50d19d01e3055dc/14w27b.json", "time": "2021-12-15T15:45:34+00:00", "releaseTime": "2014-07-02T18:34:56+00:00", "sha1": "08f1ed6374fb9d87e34fe81ab50d19d01e3055dc", "complianceLevel": 0}, {"id": "14w27a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c0e594ad64eac1b322e4724b92292daad80951eb/14w27a.json", "time": "2021-12-15T15:45:34+00:00", "releaseTime": "2014-07-02T16:07:20+00:00", "sha1": "c0e594ad64eac1b322e4724b92292daad80951eb", "complianceLevel": 0}, {"id": "14w26c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2fe002bebd830ccb5328329ed7efb7e9fb555519/14w26c.json", "time": "2021-12-15T15:45:34+00:00", "releaseTime": "2014-06-26T15:05:03+00:00", "sha1": "2fe002bebd830ccb5328329ed7efb7e9fb555519", "complianceLevel": 0}, {"id": "14w26b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/288aa1d9c5840c8d0f75ee682bc8a0a1564babfb/14w26b.json", "time": "2021-12-15T15:45:33+00:00", "releaseTime": "2014-06-25T15:08:39+00:00", "sha1": "288aa1d9c5840c8d0f75ee682bc8a0a1564babfb", "complianceLevel": 0}, {"id": "14w26a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6d41ffb32ced2eaaeed6b0ddd38b5e53f849c25f/14w26a.json", "time": "2021-12-15T15:45:33+00:00", "releaseTime": "2014-06-25T13:59:27+00:00", "sha1": "6d41ffb32ced2eaaeed6b0ddd38b5e53f849c25f", "complianceLevel": 0}, {"id": "14w25b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d8b348b5a412e809748f35f694fe5dc395a83b3a/14w25b.json", "time": "2021-12-15T15:45:32+00:00", "releaseTime": "2014-06-19T12:29:48+00:00", "sha1": "d8b348b5a412e809748f35f694fe5dc395a83b3a", "complianceLevel": 0}, {"id": "14w25a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0091b9fe0f95dc765e187840ff41235638ce22d6/14w25a.json", "time": "2021-12-15T15:45:32+00:00", "releaseTime": "2014-06-18T15:52:28+00:00", "sha1": "0091b9fe0f95dc765e187840ff41235638ce22d6", "complianceLevel": 0}, {"id": "14w21b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c5791a666929e783ba360e98247ff744b5ac3520/14w21b.json", "time": "2021-12-15T15:45:32+00:00", "releaseTime": "2014-05-22T15:17:55+00:00", "sha1": "c5791a666929e783ba360e98247ff744b5ac3520", "complianceLevel": 0}, {"id": "14w21a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f8c912b97cf684f4339571b27592e85b7ec84a19/14w21a.json", "time": "2021-12-15T15:45:31+00:00", "releaseTime": "2014-05-22T14:44:33+00:00", "sha1": "f8c912b97cf684f4339571b27592e85b7ec84a19", "complianceLevel": 0}, {"id": "14w20b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b4b682e67ff47a689cf3ff0b405eeec5758dfa9a/14w20b.json", "time": "2021-12-15T15:45:31+00:00", "releaseTime": "2014-05-15T16:47:21+00:00", "sha1": "b4b682e67ff47a689cf3ff0b405eeec5758dfa9a", "complianceLevel": 0}, {"id": "14w20a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/674e75f032a5473ad91531dfbfa7b8cb42f55f71/14w20a.json", "time": "2021-12-15T15:45:31+00:00", "releaseTime": "2014-05-15T14:01:20+00:00", "sha1": "674e75f032a5473ad91531dfbfa7b8cb42f55f71", "complianceLevel": 0}, {"id": "1.7.10", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/ed5d8789ed29872ea2ef1c348302b0c55e3f3468/1.7.10.json", "time": "2021-12-15T15:44:05+00:00", "releaseTime": "2014-05-14T17:29:23+00:00", "sha1": "ed5d8789ed29872ea2ef1c348302b0c55e3f3468", "complianceLevel": 0}, {"id": "1.7.10-pre4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f2a4a03329bb07d70d4837b6893c435f97deed25/1.7.10-pre4.json", "time": "2021-12-15T15:45:02+00:00", "releaseTime": "2014-05-14T16:29:23+00:00", "sha1": "f2a4a03329bb07d70d4837b6893c435f97deed25", "complianceLevel": 0}, {"id": "1.7.10-pre3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/94578a1fc1db7cb804a9be044008e54367f4905e/1.7.10-pre3.json", "time": "2021-12-15T15:45:01+00:00", "releaseTime": "2014-05-14T15:29:23+00:00", "sha1": "94578a1fc1db7cb804a9be044008e54367f4905e", "complianceLevel": 0}, {"id": "1.7.10-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2e8103cd14fc9a7db0c56f68cbc41a0b26ea9954/1.7.10-pre2.json", "time": "2021-12-15T15:45:01+00:00", "releaseTime": "2014-05-14T14:29:23+00:00", "sha1": "2e8103cd14fc9a7db0c56f68cbc41a0b26ea9954", "complianceLevel": 0}, {"id": "1.7.10-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a526665974dfa717f79f15529ae294d0a6074fc5/1.7.10-pre1.json", "time": "2021-12-15T15:45:00+00:00", "releaseTime": "2014-05-14T13:29:23+00:00", "sha1": "a526665974dfa717f79f15529ae294d0a6074fc5", "complianceLevel": 0}, {"id": "14w19a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ffcc6382a2d12310c42cc1b654ea8d88117cf105/14w19a.json", "time": "2021-12-15T15:45:30+00:00", "releaseTime": "2014-05-08T14:24:19+00:00", "sha1": "ffcc6382a2d12310c42cc1b654ea8d88117cf105", "complianceLevel": 0}, {"id": "14w18b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0aa29d5bc6915b347505a194260bd51bc8bf2960/14w18b.json", "time": "2021-12-15T15:45:30+00:00", "releaseTime": "2014-05-02T11:38:17+00:00", "sha1": "0aa29d5bc6915b347505a194260bd51bc8bf2960", "complianceLevel": 0}, {"id": "14w18a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c64cc4d300948e2d8e5fb05e394b69edc680027e/14w18a.json", "time": "2021-12-15T15:45:29+00:00", "releaseTime": "2014-04-30T10:25:35+00:00", "sha1": "c64cc4d300948e2d8e5fb05e394b69edc680027e", "complianceLevel": 0}, {"id": "14w17a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2d857ecd809f15008f4718dc3b11ef5b60548ce4/14w17a.json", "time": "2021-12-15T15:45:29+00:00", "releaseTime": "2014-04-24T15:44:49+00:00", "sha1": "2d857ecd809f15008f4718dc3b11ef5b60548ce4", "complianceLevel": 0}, {"id": "14w11b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/022ade13058d98289f1625857462994a6ec40110/14w11b.json", "time": "2021-12-15T15:45:28+00:00", "releaseTime": "2014-04-14T14:36:19+00:00", "sha1": "022ade13058d98289f1625857462994a6ec40110", "complianceLevel": 0}, {"id": "1.7.9", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/5579bc9e25a9bb5e3187a2570693a0c6658edce4/1.7.9.json", "time": "2021-12-15T15:44:08+00:00", "releaseTime": "2014-04-14T13:29:23+00:00", "sha1": "5579bc9e25a9bb5e3187a2570693a0c6658edce4", "complianceLevel": 0}, {"id": "1.7.8", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/03d8e2e1c192d48ffc406cb7a483861cf26dfe25/1.7.8.json", "time": "2021-12-15T15:44:08+00:00", "releaseTime": "2014-04-09T07:58:16+00:00", "sha1": "03d8e2e1c192d48ffc406cb7a483861cf26dfe25", "complianceLevel": 0}, {"id": "1.7.7", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/47e228263557da73d0e2be803e5d83e5b246ae75/1.7.7.json", "time": "2021-12-15T15:44:07+00:00", "releaseTime": "2014-04-09T07:52:16+00:00", "sha1": "47e228263557da73d0e2be803e5d83e5b246ae75", "complianceLevel": 0}, {"id": "1.7.6", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/b6dd3e6496ad415f0c57867dea1f97710d5f184c/1.7.6.json", "time": "2021-12-15T15:44:07+00:00", "releaseTime": "2014-04-09T07:52:06+00:00", "sha1": "b6dd3e6496ad415f0c57867dea1f97710d5f184c", "complianceLevel": 0}, {"id": "14w11a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ed3b597f26c1140b31d4c7421054e83398270e19/14w11a.json", "time": "2021-12-15T15:45:28+00:00", "releaseTime": "2014-03-13T14:02:50+00:00", "sha1": "ed3b597f26c1140b31d4c7421054e83398270e19", "complianceLevel": 0}, {"id": "1.7.6-pre2", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/977e8f92b306b2d798a7b69858f48fbe06dbb302/1.7.6-pre2.json", "time": "2021-12-15T15:45:03+00:00", "releaseTime": "2014-03-08T11:00:01+00:00", "sha1": "977e8f92b306b2d798a7b69858f48fbe06dbb302", "complianceLevel": 0}, {"id": "1.7.6-pre1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/39e402b6972475126fa9e8a883915a9ce5d7722a/1.7.6-pre1.json", "time": "2021-12-15T15:45:02+00:00", "releaseTime": "2014-03-08T11:00:00+00:00", "sha1": "39e402b6972475126fa9e8a883915a9ce5d7722a", "complianceLevel": 0}, {"id": "14w10c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/312e632fcac49d7c1dca6a7fa58824eace01d91d/14w10c.json", "time": "2021-12-15T15:45:28+00:00", "releaseTime": "2014-03-07T13:49:55+00:00", "sha1": "312e632fcac49d7c1dca6a7fa58824eace01d91d", "complianceLevel": 0}, {"id": "14w10b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/90e924daa64f7908e69d4c7c4451599d2592f9d3/14w10b.json", "time": "2021-12-15T15:45:27+00:00", "releaseTime": "2014-03-06T16:25:39+00:00", "sha1": "90e924daa64f7908e69d4c7c4451599d2592f9d3", "complianceLevel": 0}, {"id": "14w10a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/643d852decb88d011fab20df4cb14ed100362f0f/14w10a.json", "time": "2021-12-15T15:45:27+00:00", "releaseTime": "2014-03-06T14:23:04+00:00", "sha1": "643d852decb88d011fab20df4cb14ed100362f0f", "complianceLevel": 0}, {"id": "14w08a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4c04557fe5dcfe1c2e302265b677549384008c14/14w08a.json", "time": "2021-12-15T15:45:26+00:00", "releaseTime": "2014-02-26T17:00:00+00:00", "sha1": "4c04557fe5dcfe1c2e302265b677549384008c14", "complianceLevel": 0}, {"id": "1.7.5", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/4afb628501a6a206b80a47ab4f29ea6b98caac90/1.7.5.json", "time": "2021-12-15T15:44:06+00:00", "releaseTime": "2014-02-26T09:22:17+00:00", "sha1": "4afb628501a6a206b80a47ab4f29ea6b98caac90", "complianceLevel": 0}, {"id": "14w07a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/23dbadcc03150f2e3447d528f7f546ba8fd62246/14w07a.json", "time": "2021-12-15T15:45:26+00:00", "releaseTime": "2014-02-14T11:05:07+00:00", "sha1": "23dbadcc03150f2e3447d528f7f546ba8fd62246", "complianceLevel": 0}, {"id": "14w06b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2c381742bc9cadc33fbc0a823b4fb734aad78533/14w06b.json", "time": "2021-12-15T15:45:26+00:00", "releaseTime": "2014-02-06T17:30:42+00:00", "sha1": "2c381742bc9cadc33fbc0a823b4fb734aad78533", "complianceLevel": 0}, {"id": "14w06a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a59d77f95afc36421554f9fee3a930d6cca9209d/14w06a.json", "time": "2021-12-15T15:45:25+00:00", "releaseTime": "2014-02-06T14:30:17+00:00", "sha1": "a59d77f95afc36421554f9fee3a930d6cca9209d", "complianceLevel": 0}, {"id": "14w05b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/0b0f0bb2354640bfeeafe8611a8cfb53e9c3edff/14w05b.json", "time": "2021-12-15T15:45:25+00:00", "releaseTime": "2014-01-31T14:05:50+00:00", "sha1": "0b0f0bb2354640bfeeafe8611a8cfb53e9c3edff", "complianceLevel": 0}, {"id": "14w05a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e9d4eb5a505a84dda9c36ea32f7ebb998a60a97f/14w05a.json", "time": "2021-12-15T15:45:24+00:00", "releaseTime": "2014-01-30T15:32:41+00:00", "sha1": "e9d4eb5a505a84dda9c36ea32f7ebb998a60a97f", "complianceLevel": 0}, {"id": "14w04b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/62524cf35e56e365a5dff36353fb77a38ff35269/14w04b.json", "time": "2021-12-15T15:45:24+00:00", "releaseTime": "2014-01-24T15:48:46+00:00", "sha1": "62524cf35e56e365a5dff36353fb77a38ff35269", "complianceLevel": 0}, {"id": "14w04a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/41307eaf3c2bf6b526986d8bb2fd698099298a2f/14w04a.json", "time": "2021-12-15T15:45:23+00:00", "releaseTime": "2014-01-23T15:26:13+00:00", "sha1": "41307eaf3c2bf6b526986d8bb2fd698099298a2f", "complianceLevel": 0}, {"id": "14w03b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4c3bab6bfb32daed6b30d298818e4b8bb37c4bb2/14w03b.json", "time": "2021-12-15T15:45:23+00:00", "releaseTime": "2014-01-16T16:36:19+00:00", "sha1": "4c3bab6bfb32daed6b30d298818e4b8bb37c4bb2", "complianceLevel": 0}, {"id": "14w03a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/73f7ed186402ac8927ce415507cfa70b6aa5483f/14w03a.json", "time": "2021-12-15T15:45:23+00:00", "releaseTime": "2014-01-16T14:45:13+00:00", "sha1": "73f7ed186402ac8927ce415507cfa70b6aa5483f", "complianceLevel": 0}, {"id": "14w02c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e7c40fe6754d5912ebc119a53e752c9024130e3f/14w02c.json", "time": "2021-12-15T15:45:22+00:00", "releaseTime": "2014-01-10T15:42:36+00:00", "sha1": "e7c40fe6754d5912ebc119a53e752c9024130e3f", "complianceLevel": 0}, {"id": "14w02b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/25f758976bfd97d533af7e38680db8983d2cf82d/14w02b.json", "time": "2021-12-15T15:45:22+00:00", "releaseTime": "2014-01-09T15:45:55+00:00", "sha1": "25f758976bfd97d533af7e38680db8983d2cf82d", "complianceLevel": 0}, {"id": "14w02a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/1348f531ae51631ea0ee1e467348440c5f93dcc2/14w02a.json", "time": "2021-12-15T15:45:22+00:00", "releaseTime": "2014-01-09T14:44:41+00:00", "sha1": "1348f531ae51631ea0ee1e467348440c5f93dcc2", "complianceLevel": 0}, {"id": "1.7.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/5db9d89cb6b89573384f324aa585b4c99525b37a/1.7.4.json", "time": "2021-12-15T15:44:06+00:00", "releaseTime": "2013-12-09T12:28:10+00:00", "sha1": "5db9d89cb6b89573384f324aa585b4c99525b37a", "complianceLevel": 0}, {"id": "1.7.3", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/cd133f90b7e339c50b3cdce64188a51469c6a128/1.7.3.json", "time": "2021-12-15T15:44:06+00:00", "releaseTime": "2013-12-06T13:55:34+00:00", "sha1": "cd133f90b7e339c50b3cdce64188a51469c6a128", "complianceLevel": 0}, {"id": "13w49a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/82cf56aabfbca3a1f1d13b4404050d9877b5a67e/13w49a.json", "time": "2021-12-15T15:45:21+00:00", "releaseTime": "2013-12-05T14:34:41+00:00", "sha1": "82cf56aabfbca3a1f1d13b4404050d9877b5a67e", "complianceLevel": 0}, {"id": "13w48b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/55543db98b2df3a986c99d8cdc8278a70493eb4a/13w48b.json", "time": "2021-12-15T15:45:21+00:00", "releaseTime": "2013-11-26T18:36:08+00:00", "sha1": "55543db98b2df3a986c99d8cdc8278a70493eb4a", "complianceLevel": 0}, {"id": "13w48a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/329f28adfb5ba0d6d81ceae27209e1eb97c4ef88/13w48a.json", "time": "2021-12-15T15:45:20+00:00", "releaseTime": "2013-11-25T16:53:39+00:00", "sha1": "329f28adfb5ba0d6d81ceae27209e1eb97c4ef88", "complianceLevel": 0}, {"id": "13w47e", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/81b30470c108a201cfb2dfd548dc0a52afa20a0a/13w47e.json", "time": "2021-12-15T15:45:20+00:00", "releaseTime": "2013-11-22T15:16:38+00:00", "sha1": "81b30470c108a201cfb2dfd548dc0a52afa20a0a", "complianceLevel": 0}, {"id": "13w47d", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ccf62018b2e8aa9628dab2353ed85a4239368f63/13w47d.json", "time": "2021-12-15T15:45:20+00:00", "releaseTime": "2013-11-22T13:51:15+00:00", "sha1": "ccf62018b2e8aa9628dab2353ed85a4239368f63", "complianceLevel": 0}, {"id": "13w47c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ca7616ffa259c99e2350f010555eba54e54b4dd0/13w47c.json", "time": "2021-12-15T15:45:19+00:00", "releaseTime": "2013-11-21T17:10:33+00:00", "sha1": "ca7616ffa259c99e2350f010555eba54e54b4dd0", "complianceLevel": 0}, {"id": "13w47b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ac1dc3c3a7d33aa47e97c7b3d419510696f57cde/13w47b.json", "time": "2021-12-15T15:45:19+00:00", "releaseTime": "2013-11-21T16:57:41+00:00", "sha1": "ac1dc3c3a7d33aa47e97c7b3d419510696f57cde", "complianceLevel": 0}, {"id": "13w47a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/9bb727f63e3af146df7cbd47a6a3332d31550855/13w47a.json", "time": "2021-12-15T15:45:18+00:00", "releaseTime": "2013-11-21T15:59:58+00:00", "sha1": "9bb727f63e3af146df7cbd47a6a3332d31550855", "complianceLevel": 0}, {"id": "1.7.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/c2e8ecbf355760a74c93d7210767fa043d53f27c/1.7.2.json", "time": "2021-12-15T15:44:05+00:00", "releaseTime": "2013-10-25T13:00:00+00:00", "sha1": "c2e8ecbf355760a74c93d7210767fa043d53f27c", "complianceLevel": 0}, {"id": "1.7.1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/744a42505862bea0bdd5da55ff06e09536f36d66/1.7.1.json", "time": "2021-12-15T15:45:00+00:00", "releaseTime": "2013-10-23T12:01:07+00:00", "sha1": "744a42505862bea0bdd5da55ff06e09536f36d66", "complianceLevel": 0}, {"id": "1.7", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/7a5aa5f3e3fba022efe0752660a5c7cd2dff2d16/1.7.json", "time": "2021-12-15T15:45:03+00:00", "releaseTime": "2013-10-22T15:04:05+00:00", "sha1": "7a5aa5f3e3fba022efe0752660a5c7cd2dff2d16", "complianceLevel": 0}, {"id": "13w43a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d7c2d81bf137c9e1a84576241767de08909c29fd/13w43a.json", "time": "2021-12-15T15:45:18+00:00", "releaseTime": "2013-10-21T16:34:47+00:00", "sha1": "d7c2d81bf137c9e1a84576241767de08909c29fd", "complianceLevel": 0}, {"id": "13w42b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/ce863b81986e6acce2983be040d2071711f6403c/13w42b.json", "time": "2021-12-15T15:45:17+00:00", "releaseTime": "2013-10-18T16:34:08+00:00", "sha1": "ce863b81986e6acce2983be040d2071711f6403c", "complianceLevel": 0}, {"id": "13w42a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8cef2eee33892b62b3a28559246c750c3487dd8f/13w42a.json", "time": "2021-12-15T15:45:17+00:00", "releaseTime": "2013-10-17T18:33:05+00:00", "sha1": "8cef2eee33892b62b3a28559246c750c3487dd8f", "complianceLevel": 0}, {"id": "13w41b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/015456cd8b5c68d0076536d4e867111cd36b541a/13w41b.json", "time": "2021-12-15T15:45:17+00:00", "releaseTime": "2013-10-11T15:09:17+00:00", "sha1": "015456cd8b5c68d0076536d4e867111cd36b541a", "complianceLevel": 0}, {"id": "13w41a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/43622c17e011441b8b63b0a27d79887c2f7516aa/13w41a.json", "time": "2021-12-15T15:45:16+00:00", "releaseTime": "2013-10-10T14:21:43+00:00", "sha1": "43622c17e011441b8b63b0a27d79887c2f7516aa", "complianceLevel": 0}, {"id": "13w39b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b2d2f0b1fd23b08f8d2fb9beff39f173a2d65160/13w39b.json", "time": "2021-12-13T14:56:26+00:00", "releaseTime": "2013-09-27T12:15:58+00:00", "sha1": "b2d2f0b1fd23b08f8d2fb9beff39f173a2d65160", "complianceLevel": 0}, {"id": "13w39a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/61b88e8c311ed6a647ebf13d9934c6a1e3a94a75/13w39a.json", "time": "2021-12-13T14:56:26+00:00", "releaseTime": "2013-09-26T15:11:19+00:00", "sha1": "61b88e8c311ed6a647ebf13d9934c6a1e3a94a75", "complianceLevel": 0}, {"id": "13w38c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/a9e87e0699f19fea280878f5deb744c5d5d3ccb1/13w38c.json", "time": "2019-06-28T07:08:09+00:00", "releaseTime": "2013-09-20T15:11:34+00:00", "sha1": "a9e87e0699f19fea280878f5deb744c5d5d3ccb1", "complianceLevel": 0}, {"id": "13w38b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/6f426be1993b140ab5d10459c91eb1f542d58c82/13w38b.json", "time": "2019-06-28T07:08:09+00:00", "releaseTime": "2013-09-20T13:45:40+00:00", "sha1": "6f426be1993b140ab5d10459c91eb1f542d58c82", "complianceLevel": 0}, {"id": "13w38a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e6dc1d9f9c8efeec67af438d5bf61be082f6e8a4/13w38a.json", "time": "2019-06-28T07:08:09+00:00", "releaseTime": "2013-09-19T16:34:21+00:00", "sha1": "e6dc1d9f9c8efeec67af438d5bf61be082f6e8a4", "complianceLevel": 0}, {"id": "1.6.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/b71bae449192fbbe1582ff32fb3765edf0b9b0a8/1.6.4.json", "time": "2019-06-28T07:06:16+00:00", "releaseTime": "2013-09-19T15:52:37+00:00", "sha1": "b71bae449192fbbe1582ff32fb3765edf0b9b0a8", "complianceLevel": 0}, {"id": "13w37b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b8d28154ee056af6af3c8c37815418fe0e9f34f8/13w37b.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-09-13T10:54:41+00:00", "sha1": "b8d28154ee056af6af3c8c37815418fe0e9f34f8", "complianceLevel": 0}, {"id": "1.6.3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/903d6ba1bc87c301d88fa418f8b33446201c7d4e/1.6.3.json", "time": "2019-06-28T07:07:47+00:00", "releaseTime": "2013-09-13T10:54:41+00:00", "sha1": "903d6ba1bc87c301d88fa418f8b33446201c7d4e", "complianceLevel": 0}, {"id": "13w37a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2f33c613a4bb81ef5f56be03a8f578208ada382a/13w37a.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-09-12T14:23:14+00:00", "sha1": "2f33c613a4bb81ef5f56be03a8f578208ada382a", "complianceLevel": 0}, {"id": "13w36b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/4a538e23057a596fc8c7e04d8a7738d866467f51/13w36b.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-09-06T12:31:58+00:00", "sha1": "4a538e23057a596fc8c7e04d8a7738d866467f51", "complianceLevel": 0}, {"id": "13w36a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/bc915c4dc167dfba92fcc0ae3aa051ae0f9f089b/13w36a.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-09-05T13:05:40+00:00", "sha1": "bc915c4dc167dfba92fcc0ae3aa051ae0f9f089b", "complianceLevel": 0}, {"id": "1.6.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/c0729761bf65dc58138ce508645dba1442fa78b8/1.6.2.json", "time": "2019-06-28T07:06:16+00:00", "releaseTime": "2013-07-05T13:09:02+00:00", "sha1": "c0729761bf65dc58138ce508645dba1442fa78b8", "complianceLevel": 0}, {"id": "1.6.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/7fd8e0c76f62813eb0465e31bb74b160c01472d6/1.6.1.json", "time": "2019-06-28T07:06:16+00:00", "releaseTime": "2013-06-28T14:48:41+00:00", "sha1": "7fd8e0c76f62813eb0465e31bb74b160c01472d6", "complianceLevel": 0}, {"id": "1.6", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/20116297638f7c70cd046e25a6ac90fee4cae61a/1.6.json", "time": "2019-06-28T07:07:47+00:00", "releaseTime": "2013-06-25T13:08:56+00:00", "sha1": "20116297638f7c70cd046e25a6ac90fee4cae61a", "complianceLevel": 0}, {"id": "13w26a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b349702aef5e3adaebec30c79338300423943930/13w26a.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-06-24T16:06:06+00:00", "sha1": "b349702aef5e3adaebec30c79338300423943930", "complianceLevel": 0}, {"id": "13w25c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/934788bc580ef0a19725ee5bd31f02a0b866e0bf/13w25c.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-06-20T15:23:37+00:00", "sha1": "934788bc580ef0a19725ee5bd31f02a0b866e0bf", "complianceLevel": 0}, {"id": "13w25b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8b7870ddd0d0b38779479ad782d65ad80e688cf7/13w25b.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-06-18T15:13:27+00:00", "sha1": "8b7870ddd0d0b38779479ad782d65ad80e688cf7", "complianceLevel": 0}, {"id": "13w25a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/65c0e5fff89b477ac6f8ddb336f0e718d525d311/13w25a.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-06-17T14:08:06+00:00", "sha1": "65c0e5fff89b477ac6f8ddb336f0e718d525d311", "complianceLevel": 0}, {"id": "13w24b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/e1294b52803771cfb06767c4c40dced70475cb25/13w24b.json", "time": "2019-06-28T07:08:08+00:00", "releaseTime": "2013-06-14T12:19:13+00:00", "sha1": "e1294b52803771cfb06767c4c40dced70475cb25", "complianceLevel": 0}, {"id": "13w24a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/74666ab85cc5539f08aec638eabd63a552ed4125/13w24a.json", "time": "2019-06-28T07:08:07+00:00", "releaseTime": "2013-06-13T15:32:23+00:00", "sha1": "74666ab85cc5539f08aec638eabd63a552ed4125", "complianceLevel": 0}, {"id": "13w23b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/f17829f3e412b9b727437ec9f8433bdfc6c7b9a7/13w23b.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-06-08T00:32:01+00:00", "sha1": "f17829f3e412b9b727437ec9f8433bdfc6c7b9a7", "complianceLevel": 0}, {"id": "13w23a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8234057ec006c5bb62a28ca4f6787323968438e6/13w23a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-06-07T16:04:20+00:00", "sha1": "8234057ec006c5bb62a28ca4f6787323968438e6", "complianceLevel": 0}, {"id": "13w22a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/597a15f27cc0913a77ff7e1e9c62c3affc627fe8/13w22a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-05-30T14:38:40+00:00", "sha1": "597a15f27cc0913a77ff7e1e9c62c3affc627fe8", "complianceLevel": 0}, {"id": "13w21b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/98f66e115fbab6dcd05f2e5e0a23dd78c0a5e7a3/13w21b.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-05-27T08:50:42+00:00", "sha1": "98f66e115fbab6dcd05f2e5e0a23dd78c0a5e7a3", "complianceLevel": 0}, {"id": "13w21a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/aebfb9b82f0712de3e6ef78bc2cafe5dcb742130/13w21a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-05-23T15:38:28+00:00", "sha1": "aebfb9b82f0712de3e6ef78bc2cafe5dcb742130", "complianceLevel": 0}, {"id": "13w19a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/8bb131515d6b483baa76f1b42ea5a1018d11bb22/13w19a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-05-10T14:48:02+00:00", "sha1": "8bb131515d6b483baa76f1b42ea5a1018d11bb22", "complianceLevel": 0}, {"id": "13w18c", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/93738bf22f33d9ba5e2980bec849b097a5050c8f/13w18c.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-05-03T09:19:35+00:00", "sha1": "93738bf22f33d9ba5e2980bec849b097a5050c8f", "complianceLevel": 0}, {"id": "13w18b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/2cab9aae9eef3558d6abe8ac2708ea19322a1594/13w18b.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-05-02T17:12:25+00:00", "sha1": "2cab9aae9eef3558d6abe8ac2708ea19322a1594", "complianceLevel": 0}, {"id": "13w18a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/79bef69b5542046e705a57784cc63574748effe2/13w18a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-05-02T15:45:59+00:00", "sha1": "79bef69b5542046e705a57784cc63574748effe2", "complianceLevel": 0}, {"id": "13w17a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d21e61b16b0e446b5062e8ee72c9d0ff3bfbd155/13w17a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-04-25T15:50:00+00:00", "sha1": "d21e61b16b0e446b5062e8ee72c9d0ff3bfbd155", "complianceLevel": 0}, {"id": "1.5.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/924a2dcd8bdc31f8e9d36229811c298b3537bbc7/1.5.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-04-25T15:45:00+00:00", "sha1": "924a2dcd8bdc31f8e9d36229811c298b3537bbc7", "complianceLevel": 0}, {"id": "13w16b", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/5f4e1c860d1c79d346f3e4574615ca1fd9da01ed/13w16b.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-04-23T21:51:22+00:00", "sha1": "5f4e1c860d1c79d346f3e4574615ca1fd9da01ed", "complianceLevel": 0}, {"id": "13w16a", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/c355e2ee0495dfcc8ec9806955c8d2993179b40c/13w16a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-04-21T12:49:30+00:00", "sha1": "c355e2ee0495dfcc8ec9806955c8d2993179b40c", "complianceLevel": 0}, {"id": "1.5.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/3c514114d9c2a3ea78f72c4f9fb4eeb56747135a/1.5.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-03-20T10:00:00+00:00", "sha1": "3c514114d9c2a3ea78f72c4f9fb4eeb56747135a", "complianceLevel": 0}, {"id": "1.5", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/bb882e3d97bee9c5b5e486da04b85f977e770150/1.5.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2013-03-06T22:00:00+00:00", "sha1": "bb882e3d97bee9c5b5e486da04b85f977e770150", "complianceLevel": 0}, {"id": "1.4.7", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/7aa8e9aeacf4e1076bfd81c096f78de9b883ebe6/1.4.7.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-12-27T22:00:00+00:00", "sha1": "7aa8e9aeacf4e1076bfd81c096f78de9b883ebe6", "complianceLevel": 0}, {"id": "1.4.6", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/09832797138da79745ade734da775f44c254066b/1.4.6.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-12-19T22:00:00+00:00", "sha1": "09832797138da79745ade734da775f44c254066b", "complianceLevel": 0}, {"id": "1.4.5", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/d64a902a48a6a618f9a0a82c183be454e7a1f23b/1.4.5.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-12-19T22:00:00+00:00", "sha1": "d64a902a48a6a618f9a0a82c183be454e7a1f23b", "complianceLevel": 0}, {"id": "1.4.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/f7de827181036b09444abb6b64c1fcc663b8e98e/1.4.4.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-12-13T22:00:00+00:00", "sha1": "f7de827181036b09444abb6b64c1fcc663b8e98e", "complianceLevel": 0}, {"id": "1.4.3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/3ab416ac64dac1a6123402a8aabd8ef3caeef087/1.4.3.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-11-30T22:00:00+00:00", "sha1": "3ab416ac64dac1a6123402a8aabd8ef3caeef087", "complianceLevel": 0}, {"id": "1.4.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/2fd77aa19aba2860bbf4c1fd9f84f232703dd287/1.4.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-11-24T22:00:00+00:00", "sha1": "2fd77aa19aba2860bbf4c1fd9f84f232703dd287", "complianceLevel": 0}, {"id": "1.4.1", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/14c3ba517b5baabdfc61b60eb49d9aa7da012906/1.4.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-11-22T22:00:00+00:00", "sha1": "14c3ba517b5baabdfc61b60eb49d9aa7da012906", "complianceLevel": 0}, {"id": "1.4", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/d979a4671611bf8704c0a2a0cf09964ca25eefd7/1.4.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-11-18T22:00:00+00:00", "sha1": "d979a4671611bf8704c0a2a0cf09964ca25eefd7", "complianceLevel": 0}, {"id": "1.3.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/598eedd6f67db4aefbae6ed119029e3d7373ecf5/1.3.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-08-15T22:00:00+00:00", "sha1": "598eedd6f67db4aefbae6ed119029e3d7373ecf5", "complianceLevel": 0}, {"id": "1.3.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/637aa8466c4dac462b88682caaf753290f37798f/1.3.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-07-31T22:00:00+00:00", "sha1": "637aa8466c4dac462b88682caaf753290f37798f", "complianceLevel": 0}, {"id": "1.3", "type": "snapshot", "url": "https://launchermeta.mojang.com/v1/packages/b384219c6d4879e56b92eea01a0d986e20d55dea/1.3.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-07-25T22:00:00+00:00", "sha1": "b384219c6d4879e56b92eea01a0d986e20d55dea", "complianceLevel": 0}, {"id": "1.2.5", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/5158765caf1ca14958cb6c45d52c8e09ed9b046c/1.2.5.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-03-29T22:00:00+00:00", "sha1": "5158765caf1ca14958cb6c45d52c8e09ed9b046c", "complianceLevel": 0}, {"id": "1.2.4", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/69a67fcf11ed1298c6b43a00d64461908a318749/1.2.4.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-03-21T22:00:00+00:00", "sha1": "69a67fcf11ed1298c6b43a00d64461908a318749", "complianceLevel": 0}, {"id": "1.2.3", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/2f7eaec33e3017a413c677eefa59df2e5919e536/1.2.3.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-03-01T22:00:00+00:00", "sha1": "2f7eaec33e3017a413c677eefa59df2e5919e536", "complianceLevel": 0}, {"id": "1.2.2", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/4e2e449ba0b8b5da7055f0decea1a3257b282f17/1.2.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-02-29T22:00:01+00:00", "sha1": "4e2e449ba0b8b5da7055f0decea1a3257b282f17", "complianceLevel": 0}, {"id": "1.2.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/1a45c035ebb969dbac4e0c39582e974ad7f74a9e/1.2.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-02-29T22:00:00+00:00", "sha1": "1a45c035ebb969dbac4e0c39582e974ad7f74a9e", "complianceLevel": 0}, {"id": "1.1", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/c0cb9368dbdbb1e8dbcb9363a28d8da74cf6fc5e/1.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2012-01-11T22:00:00+00:00", "sha1": "c0cb9368dbdbb1e8dbcb9363a28d8da74cf6fc5e", "complianceLevel": 0}, {"id": "1.0", "type": "release", "url": "https://launchermeta.mojang.com/v1/packages/75062586b830dd5160f13f1c9130eb365e01f1b9/1.0.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-11-17T22:00:00+00:00", "sha1": "75062586b830dd5160f13f1c9130eb365e01f1b9", "complianceLevel": 0}, {"id": "b1.8.1", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/440e3b845c3991492a3d0c5f0ccfda78ab90d9b6/b1.8.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-09-18T22:00:00+00:00", "sha1": "440e3b845c3991492a3d0c5f0ccfda78ab90d9b6", "complianceLevel": 0}, {"id": "b1.8", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/e5b20b1a15daa60effefd86da94b118086214e8b/b1.8.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-09-14T22:00:00+00:00", "sha1": "e5b20b1a15daa60effefd86da94b118086214e8b", "complianceLevel": 0}, {"id": "b1.7.3", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/44f6969326bd45aa00dcd3c4ca3a7c05ebb24c04/b1.7.3.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-07-07T22:00:00+00:00", "sha1": "44f6969326bd45aa00dcd3c4ca3a7c05ebb24c04", "complianceLevel": 0}, {"id": "b1.7.2", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/00f5aff7cbf6ce109ecf2c8e1a5dc1bcbadb5680/b1.7.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-06-30T22:00:00+00:00", "sha1": "00f5aff7cbf6ce109ecf2c8e1a5dc1bcbadb5680", "complianceLevel": 0}, {"id": "b1.7", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/f3a725f9f27e90f2a2622ad82c182c1a1178572f/b1.7.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-06-29T22:00:00+00:00", "sha1": "f3a725f9f27e90f2a2622ad82c182c1a1178572f", "complianceLevel": 0}, {"id": "b1.6.6", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/16cb1aa6f7c9c3953fa2f53abd8f57558efd3e71/b1.6.6.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-05-30T22:00:00+00:00", "sha1": "16cb1aa6f7c9c3953fa2f53abd8f57558efd3e71", "complianceLevel": 0}, {"id": "b1.6.5", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/eae37053bb49092ce93d40e762f4c3a573ee2880/b1.6.5.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-05-27T22:00:00+00:00", "sha1": "eae37053bb49092ce93d40e762f4c3a573ee2880", "complianceLevel": 0}, {"id": "b1.6.4", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/ac98b5e9e44038f3a311736111c16fc30006e1fd/b1.6.4.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-05-25T22:00:04+00:00", "sha1": "ac98b5e9e44038f3a311736111c16fc30006e1fd", "complianceLevel": 0}, {"id": "b1.6.3", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/87785f4386cd308defcc876fb6d62bf3681be6bc/b1.6.3.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-05-25T22:00:03+00:00", "sha1": "87785f4386cd308defcc876fb6d62bf3681be6bc", "complianceLevel": 0}, {"id": "b1.6.2", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/23f8e2f1634d1db8875521d8e0d3fb5340623fd2/b1.6.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-05-25T22:00:02+00:00", "sha1": "23f8e2f1634d1db8875521d8e0d3fb5340623fd2", "complianceLevel": 0}, {"id": "b1.6.1", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/1958ecd7b20c5a2849b4e1e7a9921891e49178da/b1.6.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-05-25T22:00:01+00:00", "sha1": "1958ecd7b20c5a2849b4e1e7a9921891e49178da", "complianceLevel": 0}, {"id": "b1.6", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/7442961ad4f23c60787ab2a3c97a5037c40a92f2/b1.6.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-05-25T22:00:00+00:00", "sha1": "7442961ad4f23c60787ab2a3c97a5037c40a92f2", "complianceLevel": 0}, {"id": "b1.5_01", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/0f0b24408e6ca445e9c4a3ea2a676f71f96f5d35/b1.5_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-04-19T22:00:00+00:00", "sha1": "0f0b24408e6ca445e9c4a3ea2a676f71f96f5d35", "complianceLevel": 0}, {"id": "b1.5", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/3fa704bd73444368f04351d6d4add8a3eead9b4e/b1.5.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-04-18T22:00:00+00:00", "sha1": "3fa704bd73444368f04351d6d4add8a3eead9b4e", "complianceLevel": 0}, {"id": "b1.4_01", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/d47fcb0e4d9b7169fbb26c0bce56ed2082c3bb1d/b1.4_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-04-04T22:00:00+00:00", "sha1": "d47fcb0e4d9b7169fbb26c0bce56ed2082c3bb1d", "complianceLevel": 0}, {"id": "b1.4", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/2cf34d1caca87b68ee104e348480e38f45eb7621/b1.4.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-03-30T22:00:00+00:00", "sha1": "2cf34d1caca87b68ee104e348480e38f45eb7621", "complianceLevel": 0}, {"id": "b1.3_01", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/a0e0a27d8f7d4c23b6441e473a3e44b45a958284/b1.3_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-02-22T22:00:00+00:00", "sha1": "a0e0a27d8f7d4c23b6441e473a3e44b45a958284", "complianceLevel": 0}, {"id": "b1.3b", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/16ffb877701f7b41c6f27fb09def7a8e5d667df1/b1.3b.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-02-21T22:00:00+00:00", "sha1": "16ffb877701f7b41c6f27fb09def7a8e5d667df1", "complianceLevel": 0}, {"id": "b1.2_02", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/5352763f0a944e7940e718fd66aae03bc57dc2ef/b1.2_02.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-01-20T22:00:00+00:00", "sha1": "5352763f0a944e7940e718fd66aae03bc57dc2ef", "complianceLevel": 0}, {"id": "b1.2_01", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/0fea71dc8c4199581753d8ecb3ae69039a302340/b1.2_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-01-13T22:00:00+00:00", "sha1": "0fea71dc8c4199581753d8ecb3ae69039a302340", "complianceLevel": 0}, {"id": "b1.2", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/67bcdada56d272e4508ecb8e35827ffa4a4c18d1/b1.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2011-01-12T22:00:00+00:00", "sha1": "67bcdada56d272e4508ecb8e35827ffa4a4c18d1", "complianceLevel": 0}, {"id": "b1.1_02", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/01042e0ecdd894894ebc6f45300ae306010c154f/b1.1_02.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-12-21T22:00:01+00:00", "sha1": "01042e0ecdd894894ebc6f45300ae306010c154f", "complianceLevel": 0}, {"id": "b1.1_01", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/1bab185d888a549a3fcb4e528557caa3e7884290/b1.1_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-12-21T22:00:00+00:00", "sha1": "1bab185d888a549a3fcb4e528557caa3e7884290", "complianceLevel": 0}, {"id": "b1.0.2", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/e0a317286013bdd8e6de6da5e709422af61597d8/b1.0.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-12-20T22:00:00+00:00", "sha1": "e0a317286013bdd8e6de6da5e709422af61597d8", "complianceLevel": 0}, {"id": "b1.0_01", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/d3eec813918ee87826f7bca65dd1558b33841798/b1.0_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-12-19T22:00:01+00:00", "sha1": "d3eec813918ee87826f7bca65dd1558b33841798", "complianceLevel": 0}, {"id": "b1.0", "type": "old_beta", "url": "https://launchermeta.mojang.com/v1/packages/e5348beaf3d3c366c522b1c70044f8b7be168b02/b1.0.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-12-19T22:00:00+00:00", "sha1": "e5348beaf3d3c366c522b1c70044f8b7be168b02", "complianceLevel": 0}, {"id": "a1.2.6", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/1c888e4d8aed380db25aeb3835f5918297bb5e3a/a1.2.6.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-12-02T22:00:00+00:00", "sha1": "1c888e4d8aed380db25aeb3835f5918297bb5e3a", "complianceLevel": 0}, {"id": "a1.2.5", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/a925f00e3f7f1bde95240152bef4d15f36971394/a1.2.5.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-30T22:00:00+00:00", "sha1": "a925f00e3f7f1bde95240152bef4d15f36971394", "complianceLevel": 0}, {"id": "a1.2.4_01", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/64d6a749cb24ddd8a27546f9555ac7c2853c5943/a1.2.4_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-29T22:00:00+00:00", "sha1": "64d6a749cb24ddd8a27546f9555ac7c2853c5943", "complianceLevel": 0}, {"id": "a1.2.3_04", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/59c7719b82023e5b71e334a69d3c13137014a2bc/a1.2.3_04.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-25T22:00:00+00:00", "sha1": "59c7719b82023e5b71e334a69d3c13137014a2bc", "complianceLevel": 0}, {"id": "a1.2.3_02", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/b22eadebb4bfb63cdc42e9811da8fd2234eaaa6e/a1.2.3_02.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-24T22:00:00+00:00", "sha1": "b22eadebb4bfb63cdc42e9811da8fd2234eaaa6e", "complianceLevel": 0}, {"id": "a1.2.3_01", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/da7b740e70324be7e189c65f1f834f0a30c0588f/a1.2.3_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-23T22:00:01+00:00", "sha1": "da7b740e70324be7e189c65f1f834f0a30c0588f", "complianceLevel": 0}, {"id": "a1.2.3", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/6c059b7bdb14b29c8d5cca2b250472962fe3b0b1/a1.2.3.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-23T22:00:00+00:00", "sha1": "6c059b7bdb14b29c8d5cca2b250472962fe3b0b1", "complianceLevel": 0}, {"id": "a1.2.2b", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/2aba3e114e0a7190ea3dff1553787d5044e1c420/a1.2.2b.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-09T22:00:01+00:00", "sha1": "2aba3e114e0a7190ea3dff1553787d5044e1c420", "complianceLevel": 0}, {"id": "a1.2.2a", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/03cde2f4856b59adab177ab10673b6d951bfd7c8/a1.2.2a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-09T22:00:00+00:00", "sha1": "03cde2f4856b59adab177ab10673b6d951bfd7c8", "complianceLevel": 0}, {"id": "a1.2.1_01", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/f20e3a7757a92e1d429dcf45fa545cc84a9699da/a1.2.1_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-04T22:00:01+00:00", "sha1": "f20e3a7757a92e1d429dcf45fa545cc84a9699da", "complianceLevel": 0}, {"id": "a1.2.1", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/05773451d61d92c0e8fa73cdb2e4c0fd23c4e1d4/a1.2.1.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-04T22:00:00+00:00", "sha1": "05773451d61d92c0e8fa73cdb2e4c0fd23c4e1d4", "complianceLevel": 0}, {"id": "a1.2.0_02", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/57ff567f186230b303af60241fbce283dad44bb2/a1.2.0_02.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-11-03T22:00:00+00:00", "sha1": "57ff567f186230b303af60241fbce283dad44bb2", "complianceLevel": 0}, {"id": "a1.2.0_01", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/bed2a50ed2f9ce6a920394916ea66ce41b09b166/a1.2.0_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-10-30T22:00:00+00:00", "sha1": "bed2a50ed2f9ce6a920394916ea66ce41b09b166", "complianceLevel": 0}, {"id": "a1.2.0", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/abc94a77d94b94042d01476ee0c2e4b8c4eb08e1/a1.2.0.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-10-29T22:00:00+00:00", "sha1": "abc94a77d94b94042d01476ee0c2e4b8c4eb08e1", "complianceLevel": 0}, {"id": "a1.1.2_01", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/8730e3503e352fc03cca8a6c6ee614a17d66d8c6/a1.1.2_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-09-22T22:00:00+00:00", "sha1": "8730e3503e352fc03cca8a6c6ee614a17d66d8c6", "complianceLevel": 0}, {"id": "a1.1.2", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/b515b79ecaba79a0b5a4a5a03bf2b077f6c53334/a1.1.2.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-09-19T22:00:00+00:00", "sha1": "b515b79ecaba79a0b5a4a5a03bf2b077f6c53334", "complianceLevel": 0}, {"id": "a1.1.0", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/6054271bd0a275ed3030be97b6e9f81977abdf5d/a1.1.0.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-09-12T22:00:00+00:00", "sha1": "6054271bd0a275ed3030be97b6e9f81977abdf5d", "complianceLevel": 0}, {"id": "a1.0.17_04", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/fc2b6231e945558df29b6ed12522758860c511ab/a1.0.17_04.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-08-22T22:00:00+00:00", "sha1": "fc2b6231e945558df29b6ed12522758860c511ab", "complianceLevel": 0}, {"id": "a1.0.17_02", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/a261074f38c555d9770ba7f1a4cae9351af19d73/a1.0.17_02.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-08-19T22:00:00+00:00", "sha1": "a261074f38c555d9770ba7f1a4cae9351af19d73", "complianceLevel": 0}, {"id": "a1.0.16", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/205ed38d0d1e135b467702a746e64ed2623b4679/a1.0.16.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-08-11T22:00:00+00:00", "sha1": "205ed38d0d1e135b467702a746e64ed2623b4679", "complianceLevel": 0}, {"id": "a1.0.15", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/05a0fdef0a0d62273290eb1c145ad10501941f75/a1.0.15.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-08-03T22:00:00+00:00", "sha1": "05a0fdef0a0d62273290eb1c145ad10501941f75", "complianceLevel": 0}, {"id": "a1.0.14", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/5af21fa2467997914940beb39279f0d545a48335/a1.0.14.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-07-29T22:00:00+00:00", "sha1": "5af21fa2467997914940beb39279f0d545a48335", "complianceLevel": 0}, {"id": "a1.0.11", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/b98235f44e6422741df02c781cf0016fce1c4a84/a1.0.11.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-07-22T22:00:00+00:00", "sha1": "b98235f44e6422741df02c781cf0016fce1c4a84", "complianceLevel": 0}, {"id": "a1.0.5_01", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/4adaca651189d96ea9d7aa031038ab7b7d3fd807/a1.0.5_01.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-07-12T22:00:00+00:00", "sha1": "4adaca651189d96ea9d7aa031038ab7b7d3fd807", "complianceLevel": 0}, {"id": "a1.0.4", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/16c818f17af9e8560589f9e3cae57f0931011c25/a1.0.4.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-07-08T22:00:00+00:00", "sha1": "16c818f17af9e8560589f9e3cae57f0931011c25", "complianceLevel": 0}, {"id": "inf-20100618", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/51a5c512af384d3d2a79a3efb93f7d4b9a1c6ec2/inf-20100618.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2010-06-15T22:00:00+00:00", "sha1": "51a5c512af384d3d2a79a3efb93f7d4b9a1c6ec2", "complianceLevel": 0}, {"id": "c0.30_01c", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/9392d3f635770ac4dfd3f8c9444f319b00b08945/c0.30_01c.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-12-21T22:00:00+00:00", "sha1": "9392d3f635770ac4dfd3f8c9444f319b00b08945", "complianceLevel": 0}, {"id": "c0.0.13a", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/5ef11c52e02c27f40924ea0c323efee716de568d/c0.0.13a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-30T22:00:00+00:00", "sha1": "5ef11c52e02c27f40924ea0c323efee716de568d", "complianceLevel": 0}, {"id": "c0.0.13a_03", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/21122dee2365147033ef6214702098cf7b2549bd/c0.0.13a_03.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-21T22:00:00+00:00", "sha1": "21122dee2365147033ef6214702098cf7b2549bd", "complianceLevel": 0}, {"id": "c0.0.11a", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/2339fd5639204675b9f18dff6055dae83fc91c7e/c0.0.11a.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-16T22:00:00+00:00", "sha1": "2339fd5639204675b9f18dff6055dae83fc91c7e", "complianceLevel": 0}, {"id": "rd-161348", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/f22a3882d124ef4468f6eb50b12836c53286e18a/rd-161348.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-16T11:48:00+00:00", "sha1": "f22a3882d124ef4468f6eb50b12836c53286e18a", "complianceLevel": 0}, {"id": "rd-160052", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/0cac2ceab812568826c6e5aeb4cf980397550479/rd-160052.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-15T22:52:00+00:00", "sha1": "0cac2ceab812568826c6e5aeb4cf980397550479", "complianceLevel": 0}, {"id": "rd-20090515", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/a3165080e2b0bf20519eac5f55ee841f3491e277/rd-20090515.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-14T22:00:00+00:00", "sha1": "a3165080e2b0bf20519eac5f55ee841f3491e277", "complianceLevel": 0}, {"id": "rd-132328", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/4ec49ff663f96e78a5cf0d9538adb9d1358fc485/rd-132328.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-13T21:28:00+00:00", "sha1": "4ec49ff663f96e78a5cf0d9538adb9d1358fc485", "complianceLevel": 0}, {"id": "rd-132211", "type": "old_alpha", "url": "https://launchermeta.mojang.com/v1/packages/d090f5d3766a28425316473d9ab6c37234d48b02/rd-132211.json", "time": "2022-03-10T09:51:38+00:00", "releaseTime": "2009-05-13T20:11:00+00:00", "sha1": "d090f5d3766a28425316473d9ab6c37234d48b02", "complianceLevel": 0}]} \ No newline at end of file diff --git a/test/helper/test_data.dart b/test/helper/test_data.dart new file mode 100644 index 000000000..3f10090f6 --- /dev/null +++ b/test/helper/test_data.dart @@ -0,0 +1,23 @@ +import 'dart:io'; + +import 'package:path/path.dart'; + +enum TestData { + versionManifestV2, + version1193Meta, + versionRd132211Meta; + + String getFileName() { + switch (this) { + case TestData.versionManifestV2: + return 'version_manifest_v2.json'; + case TestData.version1193Meta: + return '1.19.3_version_meta.json'; + case TestData.versionRd132211Meta: + return 'rd-132211_version_meta.json'; + } + } + + File getFile() => + File(join(Directory.current.path, 'test', 'data', getFileName())); +} diff --git a/test/helper/test_helper.dart b/test/helper/test_helper.dart new file mode 100644 index 000000000..7f07c3226 --- /dev/null +++ b/test/helper/test_helper.dart @@ -0,0 +1,45 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:no_context_navigation/no_context_navigation.dart'; +import 'package:rpmlauncher/main.dart'; +import 'package:rpmlauncher/route/generate_route.dart'; +import 'package:rpmlauncher/ui/theme/theme_provider.dart'; +import 'package:rpmlauncher/util/launcher_info.dart'; + +class TestHelper { + static Future _pump( + WidgetTester tester, + Widget child, + ) async { + await tester.pumpWidget(ThemeProvider( + builder: (context, theme) => MaterialApp( + navigatorKey: NavigationService.navigationKey, + home: child, + onGenerateRoute: onGenerateRoute, + ), + )); + } + + static Future baseTestWidget(WidgetTester tester, Widget child, + {bool async = false, + Duration asyncDuration = const Duration(seconds: 2)}) async { + if (async) { + await tester.runAsync(() async { + await _pump(tester, child); + await Future.delayed(asyncDuration); + }); + await tester.pumpAndSettle(); + } else { + await _pump(tester, child); + await tester.pump(); + } + } + + static Future init() async { + LauncherInfo.isDebugMode = kDebugMode; + kTestMode = true; + TestWidgetsFlutterBinding.ensureInitialized(); + await initBeforeRunApp(); + } +} diff --git a/test/launcher_home_test.dart b/test/launcher_home_test.dart deleted file mode 100644 index adaeba601..000000000 --- a/test/launcher_home_test.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'dart:io'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:rpmlauncher/screen/home_page.dart'; -import 'package:rpmlauncher/screen/main_screen.dart'; - -import 'script/test_helper.dart'; - -void main() { - setUpAll(() => TestHelper.init()); - testWidgets('Launcher Home', (WidgetTester tester) async { - await tester.pumpWidget(const MainScreen()); - }); - testWidgets('Home Page', (WidgetTester tester) async { - await TestHelper.baseTestWidget(tester, const HomePage(), async: true); - }, skip: Platform.isMacOS); -} diff --git a/test/plugin_test.dart b/test/plugin_test.dart deleted file mode 100644 index dac409c16..000000000 --- a/test/plugin_test.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher_plugin/rpmlauncher_plugin.dart'; - -void main() { - const MethodChannel channel = MethodChannel('rpmlauncher_plugin'); - - TestWidgetsFlutterBinding.ensureInitialized(); - - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - if (methodCall.method == 'getPlatformVersion') { - return 'Linux Ubuntu 21.10'; - } - }); - }); - - setUpAll(() => kTestMode = true); - - tearDown(() { - channel.setMockMethodCallHandler(null); - }); - - test('getPlatformVersion', () async { - expect(await RPMLauncherPlugin.platformVersion, 'Linux Ubuntu 21.10'); - }); -} diff --git a/test/screen_test.dart b/test/screen_test.dart deleted file mode 100644 index 065228781..000000000 --- a/test/screen_test.dart +++ /dev/null @@ -1,454 +0,0 @@ -import 'dart:convert'; - -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:line_icons/line_icons.dart'; -import 'package:oauth2/oauth2.dart'; -import 'package:rpmlauncher/launcher/apis.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/pages/curseforge_modpack_page.dart'; -import 'package:rpmlauncher/screen/about.dart'; -import 'package:rpmlauncher/screen/account.dart'; -import 'package:rpmlauncher/screen/ftb_modpack.dart'; -import 'package:rpmlauncher/screen/instance_independent_setting.dart'; -import 'package:rpmlauncher/screen/ms_oauth_login.dart'; -import 'package:rpmlauncher/screen/RecommendedModpackScreen.dart'; -import 'package:rpmlauncher/screen/settings.dart'; -import 'package:rpmlauncher/screen/version_selection.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/RPMHttpClient.dart'; -import 'package:rpmlauncher/widget/dialog/download_java.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/OkClose.dart'; - -import 'script/test_helper.dart'; - -void main() { - setUpAll(() => TestHelper.init()); - - group('RPMLauncher Screen Test -', () { - testWidgets('Settings Screen', (WidgetTester tester) async { - await TestHelper.baseTestWidget(tester, SettingScreen()); - - expect(find.text(I18n.format('settings.title')), findsOneWidget); - - final Finder appearancePage = - find.text(I18n.format('settings.appearance.title')); - - await tester.tap(appearancePage); - await tester.pumpAndSettle(); - - expect( - find.text(I18n.format('settings.appearance.theme')), findsOneWidget); - }); - testWidgets('About Screen', (WidgetTester tester) async { - await TestHelper.baseTestWidget(tester, AboutScreen()); - - final Finder showLicense = find.byIcon(Icons.book_outlined); - - await tester.tap(showLicense); - await tester.pumpAndSettle(); - - expect(find.text(LauncherInfo.getUpperCaseName()), findsOneWidget); - expect(find.text(LauncherInfo.getFullVersion()), findsOneWidget); - expect(find.text('Powered by Flutter'), findsOneWidget); - - final Finder back = find.byType(BackButton); - - await tester.tap(back); - await tester.pumpAndSettle(); - - final Finder discord = find.byIcon(LineIcons.discord); - final Finder github = find.byIcon(LineIcons.github); - final Finder rpmtwWebsite = find.byIcon(LineIcons.home); - - await tester.tap(discord); - await tester.tap(github); - await tester.tap(rpmtwWebsite); - - await tester.pumpAndSettle(); - }); - testWidgets('Account Screen', (WidgetTester tester) async { - await TestHelper.baseTestWidget(tester, AccountScreen(), async: true); - await tester.pumpAndSettle(); - - final Finder mojangLogin = - find.text(I18n.format('account.add.mojang.title')); - - expect(mojangLogin, findsOneWidget); - - await tester.tap(mojangLogin); - await tester.pumpAndSettle(); - - expect(find.text(I18n.format('account.mojang.title')), findsOneWidget); - }); - testWidgets('VersionSelection Screen (Client)', - (WidgetTester tester) async { - rpmHttpClientAdapter = (RequestOptions requestOptions) { - if (requestOptions.method == 'GET' && - requestOptions.uri.toString() == - '$mojangMetaAPI/version_manifest_v2.json') { - return Future.value(Response( - requestOptions: requestOptions, - data: json.decode(TestData.versionManifest.getFileString()) as T, - statusCode: 200)); - } - return null; - }; - - await TestHelper.baseTestWidget( - tester, const VersionSelection(side: MinecraftSide.client)); - expect(find.text('1.18.1'), findsOneWidget); - - Finder showSnapshot = find.byType(Checkbox).last; - Finder showRelease = find.byType(Checkbox).first; - - await tester.tap(showRelease); - await tester.tap(showSnapshot); - await tester.pumpAndSettle(); - - Finder snapshot = find.text('21w44a'); - - await tester.dragUntilVisible( - snapshot, find.byType(ListView), const Offset(0.0, -300)); - await tester.pumpAndSettle(); - - expect(find.text('1.18.1'), findsNothing); - - expect(snapshot, findsOneWidget); - - Finder modloader = find.byType(DropdownButton); - - await tester.tap(modloader); - await tester.pumpAndSettle(); - - expect(find.text(ModLoader.forge.i18nString), findsWidgets); - expect(find.text(ModLoader.fabric.i18nString), findsWidgets); - expect(find.text(ModLoader.vanilla.i18nString), findsWidgets); - }); - testWidgets('VersionSelection Screen (Server)', - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const VersionSelection(side: MinecraftSide.server), - async: true); - expect(find.text('1.18.1'), findsOneWidget); - - Finder modloader = find.byType(DropdownButton); - - await tester.tap(modloader); - await tester.pumpAndSettle(); - - expect(find.text(ModLoader.forge.i18nString), findsNothing); - expect(find.text(ModLoader.fabric.i18nString), findsWidgets); - expect(find.text(ModLoader.paper.i18nString), findsWidgets); - expect(find.text(ModLoader.vanilla.i18nString), findsWidgets); - }); - testWidgets('CurseForge ModPack Screen', (WidgetTester tester) async { - rpmHttpClientAdapter = (RequestOptions requestOptions) { - if (requestOptions.uri.toString() == - 'https://api.rpmtw.com:2096/curseforge/?path=v1/mods/search?gameId=432%26classId=4471%26searchFilter=%26sortField=2%26sortOrder=d%E2%80%A6' && - requestOptions.method == 'GET') { - return Future.value(Response( - requestOptions: requestOptions, - data: (json.decode(TestData.curseforgeModpack.getFileString())) - as T, - statusCode: 200)); - } - // else if (requestOptions.uri.toString() == - // '$curseForgeModAPI/minecraft/version' && - // requestOptions.method == 'GET') { - // return Future.value(Response( - // requestOptions: requestOptions, - // data: (json.decode(TestData.curseforgeVersion.getFileString())) - // as T, - // statusCode: 200)); - // } - return null; - }; - - await TestHelper.baseTestWidget(tester, const CurseForgeModpackPage(), - async: true); - - final Finder modPack = find.text('RLCraft'); - - await tester.dragUntilVisible( - modPack, - find.byType(SingleChildScrollView), - const Offset(0, 50), - ); - - expect(modPack, findsOneWidget); - - await tester.tap(modPack); - await tester.pumpAndSettle(); - - expect( - find.text( - 'A modpack specially designed to bring an incredibly hardcore and semi-realism challenge revolving around survival, RPG elements, and adventure-like exploration.'), - findsOneWidget); - - await tester.sendKeyEvent(LogicalKeyboardKey.escape); - await tester.pumpAndSettle(); - - final Finder installButton = find.text(I18n.format('gui.install')); - expect(installButton, findsWidgets); - await tester.tap(installButton.first); - await tester.pumpAndSettle( - const Duration(milliseconds: 100), EnginePhase.build); - - // TODO: Install ModPack - }, skip: true); - testWidgets('FTB ModPack Screen', (WidgetTester tester) async { - rpmHttpClientAdapter = (RequestOptions requestOptions) { - if (requestOptions.uri.toString() == '$ftbModPackAPI/tag/popular/100' && - requestOptions.method == 'GET') { - return Future.value(Response( - requestOptions: requestOptions, - data: (json.decode(TestData.ftbTags.getFileString())) as T, - statusCode: 200)); - } else if (requestOptions.uri.toString() == - '$ftbModPackAPI/modpack/popular/installs/FTB/all' && - requestOptions.method == 'GET') { - return Future.value(Response( - requestOptions: requestOptions, - data: (json.decode(TestData.ftbModpack.getFileString())) as T, - statusCode: 200)); - } else if (requestOptions.uri.toString() == - '$ftbModPackAPI/modpack/35' && - requestOptions.method == 'GET') { - return Future.value(Response( - requestOptions: requestOptions, - data: (json.decode(TestData.ftbModpack35.getFileString())) as T, - statusCode: 200)); - } - return null; - }; - - await TestHelper.baseTestWidget(tester, FTBModPack(), async: true); - - expect(find.text('FTB Revelation'), findsOneWidget); - expect( - find.text( - 'Revelation is a general all-purpose modpack with optimal FPS, server performance and stability.'), - findsOneWidget); - }); - - testWidgets('Add Vanilla 1.17.1 Instance', (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const VersionSelection(side: MinecraftSide.client), - async: true); - - final Finder versionText = find.text('1.17.1'); - - await tester.tap(versionText); - - await tester.pumpAndSettle(); - - final Finder confirm = find.text(I18n.format('gui.confirm')); - expect(confirm, findsOneWidget); - expect(find.text(I18n.format('gui.cancel')), findsOneWidget); - - await tester.tap(confirm); - - // TODO: Add Vanilla 1.17.1 Instance - - // await TestUttily.pumpAndSettle(tester); - }, skip: true); - testWidgets('Download Java Dialog', (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const DownloadJava(javaVersions: [8]), - async: true); - - final Finder autoInstall = - find.text(I18n.format('launcher.java.install.auto')); - - await tester.tap(autoInstall); - await tester.pumpAndSettle(); - - expect(find.text('0.00%'), findsOneWidget); - - await tester.runAsync(() async { - await Future.delayed(const Duration(seconds: 3)); - }); - - await tester.pump(); - - expect(find.text('0.00%').evaluate().length, 0); - - expect(find.text(I18n.format('launcher.java.install.auto.download.done')), - findsOneWidget); - - if (find - .text(I18n.format('launcher.java.install.auto.download.done')) - .evaluate() - .isNotEmpty) { - final Finder close = find.byType(OkClose); - await tester.tap(close); - await tester.pumpAndSettle(); - } - }); - - testWidgets('Add Microsoft Account', (WidgetTester tester) async { - String mockToken = - 'eyJhbGciOiJIUzI1NiIsImxhbmciOiJkYXJ0IiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwidGVzdCI6IlJQTVRXIn0.Nd1lXCNoXIqQivebe5Sj4Y7LEt0oSTkbOYIThIZl_II'; - String mockRefreshToken = - 'M.R3_BAY.-CS1snzEaQsj1AUl6sp1!4UIxuAJEXwSc!BCsAsjahoWGxRgYoCad!ltICMc80mBT33tbHmBpioDPc722coOnNF3nItthH8CL4uSbHaRv4!nzYDmZdtN9QsLAPs24mSsxn*EISkg4vWziNi9GhmXFZ6qqZrwq8pFbCn3CxGPc9QgqdyAh6T9Smkwxxw26duFRKajIBDR86B6Y5jRjE8EiLhCbq9IFZUo9cniQQd2Su20*mRIRPya8pUvrIzADvDIJy1!0Cnff!MVLB0vLvdngKRLErHPmaiMldYEtCTr1*zeg'; - - String mockUUID = '896a07c6-7a99-4e4d-9c53-608cfa4fd581'; - - Credentials mockCredentials = Credentials(mockToken, - refreshToken: mockRefreshToken, - tokenEndpoint: Uri.parse('https://login.live.com/oauth20_token.srf'), - scopes: ['XboxLive.signin'], - expiration: DateTime.parse('2021-12-04')); - - microsoftOauthMock = () => Future.value(Client(mockCredentials)); - - rpmHttpClientAdapter = (RequestOptions requestOptions) { - if (requestOptions.uri.toString() == - 'https://user.auth.xboxlive.com/user/authenticate' && - requestOptions.method == 'POST') { - return Future.value(Response( - requestOptions: requestOptions, - data: { - 'IssueInstant': '2021-12-04T19:52:08.4463796Z', - 'NotAfter': '2032-1-1T19:52:08.4463796Z', - 'Token': 'xbl_token', - 'DisplayClaims': { - 'xui': [ - {'uhs': 'xbl_user_hash'} - ] - } - } as T, - statusCode: 200)); - } else if (requestOptions.uri.toString() == - 'https://xsts.auth.xboxlive.com/xsts/authorize' && - requestOptions.method == 'POST') { - return Future.value(Response( - requestOptions: requestOptions, - data: { - 'IssueInstant': '2021-12-04T19:52:08.4463796Z', - 'NotAfter': '2032-1-1T19:52:08.4463796Z', - 'Token': 'xsts_token', - 'DisplayClaims': { - 'xui': [ - {'uhs': 'xsts_user_hash'} - ] - } - } as T, - statusCode: 200)); - } else if (requestOptions.uri.toString() == - 'https://api.minecraftservices.com/launcher/login' && - requestOptions.method == 'POST') { - return Future.value(Response( - requestOptions: requestOptions, - data: { - 'username': mockUUID, - 'roles': [], - 'access_token': mockToken, - 'token_type': 'Bearer', - 'expires_in': 86400 - } as T, - statusCode: 200)); - } else if (requestOptions.uri.toString().startsWith( - 'https://api.minecraftservices.com/entitlements/license') && - requestOptions.method == 'GET') { - return Future.value(Response( - requestOptions: requestOptions, - data: { - 'items': [ - {'name': 'product_minecraft_bedrock', 'source': 'PURCHASE'}, - {'name': 'game_minecraft_bedrock', 'source': 'PURCHASE'}, - {'name': 'product_minecraft', 'source': 'MC_PURCHASE'}, - {'name': 'game_minecraft', 'source': 'MC_PURCHASE'} - ], - 'signature': - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEiLCJ4NXQiOiJJVXRXd1l0clNfSXpJS0piaTZzNGtWaF9FNXMifQ.ewogICJlbnRpdGxlbWVudHMiIDogWyB7CiAgICAibmFtZSIgOiAicHJvZHVjdF9taW5lY3JhZnRfYmVkcm9jayIsCiAgICAic291cmNlIiA6ICJQVVJDSEFTRSIKICB9LCB7CiAgICAibmFtZSIgOiAiZ2FtZV9taW5lY3JhZnRfYmVkcm9jayIsCiAgICAic291cmNlIiA6ICJQVVJDSEFTRSIKICB9LCB7CiAgICAibmFtZSIgOiAicHJvZHVjdF9taW5lY3JhZnQiLAogICAgInNvdXJjZSIgOiAiTUNfUFVSQ0hBU0UiCiAgfSwgewogICAgIm5hbWUiIDogImdhbWVfbWluZWNyYWZ0IiwKICAgICJzb3VyY2UiIDogIk1DX1BVUkNIQVNFIgogIH0gXSwKICAic2lnbmVySWQiIDogIjI1MzU0NTczMDk1Nzg4NjQiLAogICJuYmYiIDogMTYzODU4NjQ1MywKICAicmVxdWVzdElkIiA6ICJjOGEwOWUyNS05ZjI1LTQwYmMtYTNiZi0zMzdkY2U4MGQ1NDIiLAogICJyaWRjciIgOiAiZGZjMDUwMTVhZTIwM2IyNiIsCiAgImV4cCIgOiAxNjM4NzU5NDMzLAogICJpYXQiIDogMTYzODU4NjYzMywKICAicGxhdGZvcm0iIDogIlBDX0xBVU5DSEVSIgp9.EA51R3SsPpcN9GLwX_T1g7hJ0Z0vcvUSOZb9c-4vBliY3EfvgH7y3hcUzPLu40kazkmE2hsRuG-TmgWYIdSqmprZZd390r4tCDtmo4wXqGrZ1OUDK3wdQLSBU0F2LLc2wqYTj0e1aehlYhHe3FfCSWP90gsmm__IoBgkKaMJkDT7R_7dqQCvwARvzwuN9XoFzakKuKRb1Lz7vMnstWCXqtwCeaZhOUs12A0mZvce4721Www3OVneRURf35wADV4cGNCzO91AqVzHjshLk0HehPMjzaO-gRAw_TiDxAQm2Md48Cf08OlNMdHzppMt04vg4FZh_HlqzIFhgi2L2Drq4uTHS_8SS4y1Zou10PPser0AmX5Uz3V_OaipRVgd4BQ0xnx4Q4DZkgVX0gh-FbBQ4X307-RGjl4AvnCG6yyx6tsctKeIrsmPSwcJYzGWxAk3A6VDgrXnvtMkw9bDNHrVzgwAl54BhvdxFRJl5knal9rc0-WVesf3wUc-h2lKO7vLz_e9lBhwf_4zFirkvrwjr_67mMp-a498GnvCuznTf633C4ygN-RTvaX51tgnR6PUwjdMlsqqTk4VsFAr3Ljl-rc526-EEQ6GPRk6tXZyUSfYMiL98J9Btc9rYNbDeJlNM2rM3Zd_okqyD1_xtPYjjuvwogxxM7t69oi9hM_v8Wc', - 'keyId': '1', - 'requestId': 'c8a09e25-9f25-40bc-a3bf-337dce80d542' - } as T, - statusCode: 200)); - } else if (requestOptions.uri.toString() == - 'https://api.minecraftservices.com/minecraft/profile' && - requestOptions.method == 'GET') { - return Future.value(Response( - requestOptions: requestOptions, - data: { - 'id': mockUUID, - 'name': 'RPMTW', - 'skins': [ - { - 'id': '6a6e65e5-76dd-4c3c-a625-162924514568', - 'state': 'ACTIVE', - 'url': - 'http://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b', - 'variant': 'CLASSIC', - 'alias': 'STEVE' - } - ], - 'capes': [] - } as T, - statusCode: 200)); - } - return null; - }; - - await TestHelper.baseTestWidget(tester, MSLoginWidget()); - await tester.pumpAndSettle(); - - expect(find.text(I18n.format('account.add.microsoft.state.title')), - findsOneWidget); - expect(find.text(I18n.format('account.add.successful')), findsOneWidget); - - // TODO:處理各種 Microsoft 帳號登入例外錯誤 - }); - testWidgets('Recommended Modpack Screen', (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const Material(child: RecommendedModpackScreen()), - async: true); - - expect(find.text(I18n.format('version.recommended_modpack.title')), - findsOneWidget); - - Finder linkButton = - find.text(I18n.format('version.recommended_modpack.link')); - - Finder installButton = find.text(I18n.format('gui.install')); - - await tester.tap(linkButton.first); - await tester.pumpAndSettle(); - - rpmHttpClientAdapter = (RequestOptions requestOptions) { - if (requestOptions.method == 'GET' && - requestOptions.uri.toString() == - '$mojangMetaAPI/version_manifest_v2.json') { - return Future.value(Response( - requestOptions: requestOptions, - data: json.decode(TestData.versionManifest.getFileString()) as T, - statusCode: 200)); - } - return null; - }; - - await tester.tap(installButton.first); - await tester.pumpAndSettle(); - }); - testWidgets( - 'Instance Independent Setting', - (WidgetTester tester) async { - InstanceConfig config = InstanceConfig.unknown(); - - await TestHelper.baseTestWidget( - tester, - Material( - child: InstanceIndependentSetting(instanceConfig: config))); - - expect(find.text(I18n.format('gui.default')), findsOneWidget); - }, - ); - }); -} diff --git a/test/script/test_helper.dart b/test/script/test_helper.dart deleted file mode 100644 index 23237bf57..000000000 --- a/test/script/test_helper.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:no_context_navigation/no_context_navigation.dart'; -import 'package:path/path.dart'; -import 'package:provider/provider.dart'; -import 'package:rpmlauncher/function/counter.dart'; -import 'package:rpmlauncher/main.dart'; -import 'package:rpmlauncher/route/generate_route.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/util/theme.dart'; - -enum TestData { - minecraftNews, - minecraftMeta, - forge112Args, - fabric117Args, - fabric118Log, - versionManifest, - curseforgeModpack, - curseforgeVersion, - ftbTags, - ftbModpack, - ftbModpack35, - fabricInstallerVersion, - rpmlauncherLogo, - rpmtwModJar -} - -extension TestDataExtension on TestData { - String toFileName() { - switch (this) { - case TestData.minecraftNews: - return 'MinecraftNews-2022-12-11.json'; - case TestData.minecraftMeta: - return 'Minecraft-1.18-meta.json'; - case TestData.forge112Args: - return 'Forge-1.12.2-args.json'; - case TestData.fabric117Args: - return 'Fabric-1.17.1-args.json'; - case TestData.fabric118Log: - return 'Fabric-1.18-log.txt'; - case TestData.versionManifest: - return 'Minecraft-Version-Manifest-V2.json'; - case TestData.curseforgeModpack: - return 'CurseForge-Modpack.json'; - case TestData.curseforgeVersion: - return 'CurseForge-MCVersion.json'; - case TestData.ftbTags: - return 'FTB-Tags.json'; - case TestData.ftbModpack: - return 'FTB-Modpack.json'; - case TestData.ftbModpack35: - return 'FTB-Modpack-35.json'; - case TestData.fabricInstallerVersion: - return 'Fabric-Installer-Version.json'; - case TestData.rpmlauncherLogo: - return 'RPMLauncher-Logo.png'; - case TestData.rpmtwModJar: - return 'RPMTW-Update-Mod-Fabric-1.18.1-1.3.1.jar'; - default: - return name; - } - } - - File getFile() => - File(join(Directory.current.path, 'test', 'data', toFileName())); - - String getFileString() => getFile().readAsStringSync(); - - Uint8List getBytesString() => getFile().readAsBytesSync(); -} - -class TestHelper { - static Future _pump( - WidgetTester tester, - Widget child, - ) async { - await tester.pumpWidget(Provider( - create: (context) { - return Counter.create(); - }, - child: DynamicThemeBuilder( - builder: (context, theme) => MaterialApp( - navigatorKey: NavigationService.navigationKey, - home: child, - theme: theme, - onGenerateRoute: onGenerateRoute, - ), - ), - )); - } - - static Future pumpAndSettle(WidgetTester tester) async { - return await TestAsyncUtils.guard(() async { - final TestWidgetsFlutterBinding binding = tester.binding; - int count = 0; - do { - await binding.pump( - const Duration(milliseconds: 100), EnginePhase.sendSemanticsUpdate); - count += 1; - } while (binding.hasScheduledFrame); - return count; - }); - } - - static Future baseTestWidget(WidgetTester tester, Widget child, - {bool async = false, - Duration asyncDuration = const Duration(seconds: 2)}) async { - if (async) { - await tester.runAsync(() async { - await _pump(tester, child); - await Future.delayed(asyncDuration); - }); - await tester.pumpAndSettle(); - // await pumpAndSettle(tester); - } else { - await _pump(tester, child); - await tester.pump(); - } - } - - static Future init() async { - LauncherInfo.isDebugMode = kDebugMode; - kTestMode = true; - TestWidgetsFlutterBinding.ensureInitialized(); - await initBeforeRunApp(); - HttpOverrides.global = null; - } -} diff --git a/test/tests/model/game/version/mc_version_manifest_test.dart b/test/tests/model/game/version/mc_version_manifest_test.dart new file mode 100644 index 000000000..fb4bcb54e --- /dev/null +++ b/test/tests/model/game/version/mc_version_manifest_test.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_manifest.dart'; +import 'package:rpmlauncher/model/game/version/mc_version_type.dart'; + +import '../../../../helper/test_data.dart'; + +void main() { + group(MCVersionManifest, () { + final jsonFile = TestData.versionManifestV2.getFile(); + final Map map = json.decode(jsonFile.readAsStringSync()); + final manifest = MCVersionManifest.fromJson(map); + + test('Release and snapshot should be 1.19.3', () { + expect(manifest.latest.release, '1.19.3'); + expect(manifest.latest.snapshot, '1.19.3'); + + expect(manifest.latest.toJson(), { + 'release': '1.19.3', + 'snapshot': '1.19.3', + }); + expect(manifest.latest.toString(), 'MCLatestVersion(1.19.3, 1.19.3)'); + }); + + test('Check version count', () { + expect(manifest.versions.length, 666); + + expect( + manifest.toString(), + allOf([ + contains('MCVersionManifest'), + contains('1.19.3'), + contains('1.19.2'), + contains('1.19.1'), + contains('rd-132211'), + ])); + }); + + test('Check first version meta', () { + final firstVersion = manifest.versions.first; + expect(firstVersion.id, '1.19.3'); + expect(firstVersion.type, MCVersionType.release); + expect(firstVersion.url, + 'https://piston-meta.mojang.com/v1/packages/6607feafdb2f96baad9314f207277730421a8e76/1.19.3.json'); + expect(firstVersion.time, DateTime.parse('2022-12-07T08:58:43+00:00')); + expect(firstVersion.releaseTime, + DateTime.parse('2022-12-07T08:17:18+00:00')); + expect(firstVersion.sha1, '6607feafdb2f96baad9314f207277730421a8e76'); + expect(firstVersion.complianceLevel, 1); + + expect(firstVersion.toJson(), { + 'id': '1.19.3', + 'type': 'release', + 'url': + 'https://piston-meta.mojang.com/v1/packages/6607feafdb2f96baad9314f207277730421a8e76/1.19.3.json', + 'time': '2022-12-07T08:58:43.000Z', + 'releaseTime': '2022-12-07T08:17:18.000Z', + 'sha1': '6607feafdb2f96baad9314f207277730421a8e76', + 'complianceLevel': 1 + }); + expect(firstVersion.toString(), + 'MCVersion(1.19.3, MCVersionType.release, https://piston-meta.mojang.com/v1/packages/6607feafdb2f96baad9314f207277730421a8e76/1.19.3.json, 2022-12-07 08:58:43.000Z, 2022-12-07 08:17:18.000Z, 6607feafdb2f96baad9314f207277730421a8e76, 1)'); + }); + + test('Check last version meta', () { + final lastVersion = manifest.versions.last; + expect(lastVersion.id, 'rd-132211'); + expect(lastVersion.type, MCVersionType.oldAlpha); + expect(lastVersion.url, + 'https://launchermeta.mojang.com/v1/packages/d090f5d3766a28425316473d9ab6c37234d48b02/rd-132211.json'); + expect(lastVersion.time, DateTime.parse('2022-03-10T09:51:38+00:00')); + expect( + lastVersion.releaseTime, DateTime.parse('2009-05-13T20:11:00+00:00')); + expect(lastVersion.sha1, 'd090f5d3766a28425316473d9ab6c37234d48b02'); + expect(lastVersion.complianceLevel, 0); + + expect(lastVersion.toJson(), { + 'id': 'rd-132211', + 'type': 'old_alpha', + 'url': + 'https://launchermeta.mojang.com/v1/packages/d090f5d3766a28425316473d9ab6c37234d48b02/rd-132211.json', + 'time': '2022-03-10T09:51:38.000Z', + 'releaseTime': '2009-05-13T20:11:00.000Z', + 'sha1': 'd090f5d3766a28425316473d9ab6c37234d48b02', + 'complianceLevel': 0 + }); + expect(lastVersion.toString(), + 'MCVersion(rd-132211, MCVersionType.oldAlpha, https://launchermeta.mojang.com/v1/packages/d090f5d3766a28425316473d9ab6c37234d48b02/rd-132211.json, 2022-03-10 09:51:38.000Z, 2009-05-13 20:11:00.000Z, d090f5d3766a28425316473d9ab6c37234d48b02, 0)'); + }); + }); +} diff --git a/test/util/window_handler_test.dart b/test/tests/util/window_handler_test.dart similarity index 96% rename from test/util/window_handler_test.dart rename to test/tests/util/window_handler_test.dart index a430a14fc..b4f3bfe3b 100644 --- a/test/util/window_handler_test.dart +++ b/test/tests/util/window_handler_test.dart @@ -3,7 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:rpmlauncher/handler/window_handler.dart'; -import '../script/test_helper.dart'; +import '../../helper/test_helper.dart'; void main() { setUpAll(() => TestHelper.init()); @@ -33,7 +33,7 @@ void main() { final button = find.text('click me'); expect(button, findsOneWidget); await tester.tap(button); - await TestHelper.pumpAndSettle(tester); + await tester.pumpAndSettle(); expect(WindowHandler.windows, [0, 1]); await WindowHandler.close(); diff --git a/test/unit_tests/base_unit_test.dart b/test/unit_tests/base_unit_test.dart deleted file mode 100644 index d0cd12789..000000000 --- a/test/unit_tests/base_unit_test.dart +++ /dev/null @@ -1,216 +0,0 @@ -import 'package:crypto/crypto.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:rpmlauncher/model/Game/jvm_args.dart'; -import 'package:rpmlauncher/model/IO/Properties.dart'; -import 'package:rpmlauncher/util/launcher_info.dart'; -import 'package:rpmlauncher/mod/mod_loader.dart'; -import 'package:rpmlauncher/model/Game/mod_info.dart'; -import 'package:rpmlauncher/function/analytics.dart'; -import 'package:rpmlauncher/util/logger.dart'; -import 'package:rpmlauncher/util/updater.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/util/util.dart'; -import 'package:rpmlauncher_plugin/rpmlauncher_plugin.dart'; -import 'dart:developer'; - -import '../script/test_helper.dart'; - -void main() async { - setUpAll(() => TestHelper.init()); - - const MethodChannel channel = MethodChannel('rpmlauncher_plugin'); - - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - if (methodCall.method == 'getTotalPhysicalMemory') { - return 8589934592.00; // 8GB - } - }); - }); - - tearDown(() { - channel.setMockMethodCallHandler(null); - }); - - group('RPMLauncher Unit Test -', () { - test( - 'i18n', - () async { - I18n.getSystemLanguage(); - log(I18n.format('init.quick_setup.content')); - }, - ); - test('Launcher info', () async { - log("Launcher Version: ${LauncherInfo.version}"); - log("Launcher Version Type (i18n): ${Updater.toI18nString(LauncherInfo.getVersionType())}"); - log("Launcher Executing File: ${LauncherInfo.getExecutingFile()}"); - log("Launcher DataHome: $dataHome"); - log("PhysicalMemory: ${await RPMLauncherPlugin.getTotalPhysicalMemory()} MB"); - }); - testWidgets('Check dev updater', (WidgetTester tester) async { - LauncherInfo.isDebugMode = false; - late VersionInfo dev; - await tester.runAsync(() async { - dev = await Updater.checkForUpdate(VersionTypes.dev); - }); - - await TestHelper.baseTestWidget(tester, Container()); - - if (dev.needUpdate) { - log("Dev channel need update"); - await Updater.download(dev); - } else { - log("Dev channel not need update"); - } - }); - test('Check debug updater', () async { - /// 如果更新通道是 debug ,將不會收到更新資訊,因此返回 false - expect( - (await Updater.checkForUpdate(VersionTypes.debug)).needUpdate, false); - }); - // test('Check stable updater', () async { - // LauncherInfo.isDebugMode = false; - // bool Stable = - // (await Updater.checkForUpdate(VersionTypes.stable)).needUpdate; - // print("Stable channel ${Stable ? "need update" : "not need update"}"); - // }); - test('log test', () { - Logger.current.info('Hello World'); - Logger.current.error(ErrorType.unknown, "Test Unknown Error", - stackTrace: StackTrace.current); - }); - test('Google Analytics', () async { - Analytics ga = Analytics(); - await ga.ping(); - }); - test('Check Minecraft Fabric Mod Conflicts', () async { - ModInfo myMod = ModInfo( - loader: ModLoader.fabric, - name: "RPMTW", - description: "Hello RPMTW World", - version: "1.0.1", - curseID: null, - namespace: "rpmtw", - conflicts: [], - md5Hash: md5.convert(TestData.rpmtwModJar.getBytesString()).toString(), - murmur2Hash: Util.getMurmur2Hash(TestData.rpmtwModJar.getFile()), - ); - - ModInfo conflictsMod = ModInfo( - loader: ModLoader.forge, - name: "Conflicts Mod", - description: "", - version: "1.0.0", - curseID: null, - namespace: "conflicts_mod", - conflicts: [ - const ConflictMod(namespace: "rpmtw", versionID: "1.0.1") - ], - md5Hash: "", - murmur2Hash: 1234567890); - - expect(conflictsMod.conflicts.first.isConflict(myMod), true); - }); - }); - test("Properties parsing", () { - String propertiesText = ''' - # 測試註解 - name=RPMTW - version=1.0.0 - # 作者 - author=SiongSng,The RPMTW Team - language=zh_TW - '''; - - Properties properties = Properties.decode(propertiesText); - - expect(properties['name'], "RPMTW"); - expect(properties['version'], "1.0.0"); - expect(properties['author'], "SiongSng,The RPMTW Team"); - expect(properties['language'], "zh_TW"); - expect(properties.comments, [" 測試註解", " 作者"]); - expect(properties.keys, ["name", "version", "author", "language"]); - - String propertiesText2 = - "name=RPMTW\nversion=1.0.0\nauthor=SiongSng,The RPMTW Team\nlanguage=zh_TW"; - - expect(Properties.encode(properties), propertiesText2); - - String rpmtw = properties.remove('name')!; - expect(rpmtw, "RPMTW"); - expect(properties.length, 3); - properties.clear(); - expect(properties.length, 0); - }); - test("JVM args parsing", () { - String jvmArgs = - "-XX:+AggressiveOpts -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSConcurrentMTEnabled -XX:ParallelGCThreads=8 -Dsun.rmi.dgc.server.gcInterval=1800000 -XX:+UnlockExperimentalVMOptions -XX:+ExplicitGCInvokesConcurrent -XX:MaxGCPauseMillis=50 -XX:+AlwaysPreTouch -XX:+UseStringDeduplication -Dfml.ignorePatchDiscrepancies=true -Dfml.ignoreInvalidMinecraftCertificates=true -XX:-OmitStackTraceInFastThrow -XX:+OptimizeStringConcat -XX:+UseAdaptiveGCBoundary -XX:NewRatio=3 -Dfml.readTimeout=90 -XX:+UseFastAccessorMethods -XX:CMSInitiatingOccupancyFraction=75 -XX:+CMSScavengeBeforeRemark -XX:+UseCMSInitiatingOccupancyOnly"; - - JvmArgs args = JvmArgs(args: jvmArgs); - - List list = [ - "-XX:+AggressiveOpts", - "-XX:+UseConcMarkSweepGC", - "-XX:+UseParNewGC", - "-XX:+CMSConcurrentMTEnabled", - "-XX:ParallelGCThreads=8", - "-Dsun.rmi.dgc.server.gcInterval=1800000", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+ExplicitGCInvokesConcurrent", - "-XX:MaxGCPauseMillis=50", - "-XX:+AlwaysPreTouch", - "-XX:+UseStringDeduplication", - "-Dfml.ignorePatchDiscrepancies=true", - "-Dfml.ignoreInvalidMinecraftCertificates=true", - "-XX:-OmitStackTraceInFastThrow", - "-XX:+OptimizeStringConcat", - "-XX:+UseAdaptiveGCBoundary", - "-XX:NewRatio=3", - "-Dfml.readTimeout=90", - "-XX:+UseFastAccessorMethods", - "-XX:CMSInitiatingOccupancyFraction=75", - "-XX:+CMSScavengeBeforeRemark", - "-XX:+UseCMSInitiatingOccupancyOnly" - ]; - - expect(args.toList(), list); - expect(JvmArgs.fromList(list).args, jvmArgs); - }); - test("JVM args parsing", () { - String jvmArgs = - "-XX:+AggressiveOpts -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSConcurrentMTEnabled -XX:ParallelGCThreads=8 -Dsun.rmi.dgc.server.gcInterval=1800000 -XX:+UnlockExperimentalVMOptions -XX:+ExplicitGCInvokesConcurrent -XX:MaxGCPauseMillis=50 -XX:+AlwaysPreTouch -XX:+UseStringDeduplication -Dfml.ignorePatchDiscrepancies=true -Dfml.ignoreInvalidMinecraftCertificates=true -XX:-OmitStackTraceInFastThrow -XX:+OptimizeStringConcat -XX:+UseAdaptiveGCBoundary -XX:NewRatio=3 -Dfml.readTimeout=90 -XX:+UseFastAccessorMethods -XX:CMSInitiatingOccupancyFraction=75 -XX:+CMSScavengeBeforeRemark -XX:+UseCMSInitiatingOccupancyOnly"; - - JvmArgs args = JvmArgs(args: jvmArgs); - - List list = [ - "-XX:+AggressiveOpts", - "-XX:+UseConcMarkSweepGC", - "-XX:+UseParNewGC", - "-XX:+CMSConcurrentMTEnabled", - "-XX:ParallelGCThreads=8", - "-Dsun.rmi.dgc.server.gcInterval=1800000", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+ExplicitGCInvokesConcurrent", - "-XX:MaxGCPauseMillis=50", - "-XX:+AlwaysPreTouch", - "-XX:+UseStringDeduplication", - "-Dfml.ignorePatchDiscrepancies=true", - "-Dfml.ignoreInvalidMinecraftCertificates=true", - "-XX:-OmitStackTraceInFastThrow", - "-XX:+OptimizeStringConcat", - "-XX:+UseAdaptiveGCBoundary", - "-XX:NewRatio=3", - "-Dfml.readTimeout=90", - "-XX:+UseFastAccessorMethods", - "-XX:CMSInitiatingOccupancyFraction=75", - "-XX:+CMSScavengeBeforeRemark", - "-XX:+UseCMSInitiatingOccupancyOnly" - ]; - - expect(args.toList(), list); - expect(JvmArgs.fromList(list).args, jvmArgs); - }); -} diff --git a/test/unit_tests/game_unit_test.dart b/test/unit_tests/game_unit_test.dart deleted file mode 100644 index 5ed01c39e..000000000 --- a/test/unit_tests/game_unit_test.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; -import 'dart:io'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/launcher/Arguments.dart'; -import 'package:rpmlauncher/model/Game/FabricInstallerVersion.dart'; -import 'package:rpmlauncher/model/Game/Libraries.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/util/util.dart'; -import '../script/test_helper.dart'; - -void main() async { - setUpAll(() => TestHelper.init()); - - group('Game -', () { - test("Game library serialization and deserialization ", () { - final Map meta = json.decode(TestData.minecraftMeta.getFileString()); - final Libraries libraries = Libraries.fromList(meta['libraries']); - - expect( - libraries.any((lib) => lib.name == "com.google.code.gson:gson:2.8.8"), - true); - - final Library library = libraries - .firstWhere((lib) => lib.name == "com.google.code.gson:gson:2.8.8"); - - expect(library.name, "com.google.code.gson:gson:2.8.8"); - expect(library.need, true); - expect(library.rules, null); - - final String args = - r'1.18-pre6.jar${separator}${data_home}/libraries/com/mojang/blocklist/1.0.6/blocklist-1.0.6.jar${separator}${data_home}/libraries/com/mojang/patchy/2.1.6/patchy-2.1.6.jar${separator}${data_home}/libraries/com/github/oshi/oshi-core/5.8.2/oshi-core-5.8.2.jar${separator}${data_home}/libraries/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar${separator}${data_home}/libraries/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar${separator}${data_home}/libraries/org/slf4j/slf4j-api/1.8.0-beta4/slf4j-api-1.8.0-beta4.jar${separator}${data_home}/libraries/org/apache/logging/log4j/log4j-slf4j18-impl/2.14.1/log4j-slf4j18-impl-2.14.1.jar${separator}${data_home}/libraries/com/ibm/icu/icu4j/69.1/icu4j-69.1.jar${separator}${data_home}/libraries/com/mojang/javabridge/1.2.24/javabridge-1.2.24.jar${separator}${data_home}/libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar${separator}${data_home}/libraries/io/netty/netty-all/4.1.68.Final/netty-all-4.1.68.Final.jar${separator}${data_home}/libraries/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar${separator}${data_home}/libraries/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar${separator}${data_home}/libraries/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar${separator}${data_home}/libraries/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar${separator}${data_home}/libraries/commons-codec/commons-codec/1.15/commons-codec-1.15.jar${separator}${data_home}/libraries/com/mojang/brigadier/1.0.18/brigadier-1.0.18.jar${separator}${data_home}/libraries/com/mojang/datafixerupper/4.0.26/datafixerupper-4.0.26.jar${separator}${data_home}/libraries/com/google/code/gson/gson/2.8.8/gson-2.8.8.jar${separator}${data_home}/libraries/com/mojang/authlib/3.2.38/authlib-3.2.38.jar${separator}${data_home}/libraries/org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar${separator}${data_home}/libraries/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar${separator}${data_home}/libraries/commons-logging/commons-logging/1.2/commons-logging-1.2.jar${separator}${data_home}/libraries/org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.jar${separator}${data_home}/libraries/it/unimi/dsi/fastutil/8.5.6/fastutil-8.5.6.jar${separator}${data_home}/libraries/org/apache/logging/log4j/log4j-api/2.14.1/log4j-api-2.14.1.jar${separator}${data_home}/libraries/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar${separator}${data_home}/libraries/org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar${separator}${data_home}/libraries/org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar${separator}${data_home}/libraries/org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar${separator}${data_home}/libraries/org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar${separator}${data_home}/libraries/org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar${separator}${data_home}/libraries/org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar${separator}${data_home}/libraries/org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar${separator}${data_home}/libraries/com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar${separator}${data_home}/libraries/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0.jar' - .replaceAll(r"${data_home}", dataHome.absolute.path) - .replaceAll(r"${separator}", Util.getLibrarySeparator()); - - expect(libraries.getLibrariesLauncherArgs(File("1.18-pre6.jar")), args); - - log("Library Files: ${libraries.getLibrariesFiles()}"); - - final Library nativeLibrary = - libraries.firstWhere((lib) => lib.natives?.isNatives ?? false); - - expect(nativeLibrary.natives?.isNatives, true); - }); - test("Forge 1.12.2 arguments parse", () { - Map args = json.decode(TestData.forge112Args.getFileString()); - - Arguments.getForge(args, {}, Version(1, 12, 2)); - }); - test("Fabric 1.17.1 arguments parse", () { - Map args = json.decode(TestData.fabric117Args.getFileString()); - - List parsedArgs = Arguments.getVanilla( - args, {r"${auth_player_name}": "RPMTW"}, Version(1, 17, 1)); - - expect(parsedArgs.contains('RPMTW'), true); - }); - test('Fabric Installer Version', () { - FabricInstallerVersions versions = FabricInstallerVersions.fromJson( - TestData.fabricInstallerVersion.getFileString()); - - expect( - versions.first, - FabricInstallerVersion( - url: - "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.10.2/fabric-installer-0.10.2.jar", - maven: "net.fabricmc:fabric-installer:0.10.2", - version: "0.10.2", - stable: true)); - - expect(versions.firstWhere((e) => e.stable).version, "0.10.2"); - }); - }); -} diff --git a/test/util/launcher_path_test.dart b/test/util/launcher_path_test.dart deleted file mode 100644 index 7dc2ac4c8..000000000 --- a/test/util/launcher_path_test.dart +++ /dev/null @@ -1,98 +0,0 @@ -// ignore_for_file: depend_on_referenced_packages - -import 'dart:io'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:path/path.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:rpmlauncher/util/launcher_path.dart'; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import '../script/test_helper.dart'; - -void main() { - setUpAll(() => TestHelper.init()); - test('old data home (for windows and macOS)', () async { - final Directory oldHome = Directory(join( - (await getApplicationDocumentsDirectory()).path, - 'RPMLauncher', - 'test')); - oldHome.createSync(recursive: true); - - await LauncherPath.init(); - final Directory path = LauncherPath.currentDataHome; - expect(path.path, oldHome.path); - - oldHome.deleteSync(recursive: true); - }, skip: Platform.isLinux); - - test('old data home (for linux)', () async { - final Directory oldHome = Directory( - join(absolute(Platform.environment['HOME']!), 'RPMLauncher', 'test')); - oldHome.createSync(recursive: true); - - await LauncherPath.init(); - final Directory path = LauncherPath.currentDataHome; - expect(path.path, oldHome.path); - }, skip: !Platform.isLinux); -} - -class FakePathProviderPlatform extends Fake - with MockPlatformInterfaceMixin - implements PathProviderPlatform { - @override - Future getApplicationSupportPath() async { - return '${Directory.current.path}/Support'; - } - - @override - Future getApplicationDocumentsPath() async { - return '${Directory.current.path}/Documents'; - } -} - -class AllNullFakePathProviderPlatform extends Fake - implements PathProviderPlatform { - @override - Future getTemporaryPath() async { - return null; - } - - @override - Future getApplicationSupportPath() async { - return null; - } - - @override - Future getLibraryPath() async { - return null; - } - - @override - Future getApplicationDocumentsPath() async { - return null; - } - - @override - Future getExternalStoragePath() async { - return null; - } - - @override - Future?> getExternalCachePaths() async { - return null; - } - - @override - Future?> getExternalStoragePaths({ - StorageDirectory? type, - }) async { - return null; - } - - @override - Future getDownloadsPath() async { - return null; - } -} diff --git a/test/util/util_test.dart b/test/util/util_test.dart deleted file mode 100644 index 37236aed9..000000000 --- a/test/util/util_test.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'dart:io'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:rpmlauncher/util/util.dart'; -import '../script/test_helper.dart'; - -void main() async { - setUpAll(() => TestHelper.init()); - - group('Uttily Class -', () { - test("Minecraft Version Parse", () { - expect(Util.parseMCComparableVersion("1.18-pre1"), Version(1, 18, 0)); - expect(Util.parseMCComparableVersion("21w20a"), Version(1, 17, 0)); - expect(Util.parseMCComparableVersion("18w22c"), Version(1, 13, 0)); - expect(Util.parseMCComparableVersion("11w47a"), Version(1, 1, 0)); - expect(Util.parseMCComparableVersion("1.16.5-rc1"), - Version(1, 16, 5, pre: "rc1")); - }); - test("Check NetWork", () async { - expect(await Util.hasNetWork(), true); - }); - - test("Get library separator", () async { - expect(Util.getLibrarySeparator(), ":"); - }, skip: !(Platform.isLinux || Platform.isMacOS)); - test("Get library separator", () async { - expect(Util.getLibrarySeparator(), ";"); - }, skip: !(Platform.isWindows)); - }); -} diff --git a/test/view_test.dart b/test/view_test.dart deleted file mode 100644 index 2ca07de0f..000000000 --- a/test/view_test.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:rpmlauncher/model/Game/instance.dart'; -import 'package:rpmlauncher/model/Game/MinecraftNews.dart'; -import 'package:rpmlauncher/model/Game/MinecraftSide.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/view/instance_view.dart'; -import 'package:rpmlauncher/view/MinecraftNewsView.dart'; - -import 'script/test_helper.dart'; - -void main() async { - setUpAll(() => TestHelper.init()); - - group("RPMLauncher View Test -", () { - testWidgets('Minecraft News View', (WidgetTester tester) async { - MinecraftNews news = MinecraftNews.formMap( - json.decode(TestData.minecraftNews.getFileString())); - - await TestHelper.baseTestWidget( - tester, Material(child: MinecraftNewsView(news: news)), - async: true); - - expect(find.text("Minecraft Snapshot 21w44a"), findsWidgets); - expect(find.text("A Minecraft Java Snapshot"), findsWidgets); - - await tester.runAsync(() async { - expect( - find.image(Image.network( - "https://minecraft.net/content/dam/games/minecraft/screenshots/snapshot-21w44a-1x1.jpg", - fit: BoxFit.contain, - ).image), - findsWidgets); - }); - - await tester.runAsync( - () async => await Future.delayed(const Duration(seconds: 5))); - - Finder newsWidget = find.byType(ListTile); - - await tester.tap(newsWidget.first); - await tester.pumpAndSettle(); - - Finder newsLinkWidget = find.byIcon(Icons.open_in_browser); - - await tester.tap(newsLinkWidget.first); - await tester.pumpAndSettle(); - - Finder newsImage = find.byType(InkWell); - - await tester.tap(newsImage.first); - await tester.pumpAndSettle(); - }); - testWidgets( - "Instance View", - (WidgetTester tester) async { - await TestHelper.baseTestWidget(tester, - const Material(child: InstanceView(side: MinecraftSide.client)), - async: true); - - final Finder notFoundText = - find.text(I18n.format('homepage.instance.found')); - - expect(notFoundText, findsOneWidget); - - /// 建立一個安裝檔 - final InstanceConfig config = InstanceConfig.unknown() - ..createConfigFile(); - await tester.pumpAndSettle(); - - final Instance instance = Instance.fromUUID(config.uuid)!; - expect(instance.uuid, config.uuid); - }, - ); - }); -} diff --git a/test/widget/launcher_shortcuts_test.dart b/test/widget/launcher_shortcuts_test.dart deleted file mode 100644 index 0d37cfb51..000000000 --- a/test/widget/launcher_shortcuts_test.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:rpmlauncher/widget/launcher_shortcuts.dart'; - -import '../script/test_helper.dart'; - -void main() { - setUpAll(() => TestHelper.init()); - - testWidgets('key down esc (can pop)', (tester) async { - await TestHelper.baseTestWidget(tester, Builder(builder: (context) { - return LauncherShortcuts( - child: TextButton( - child: const Text('Press me'), - onPressed: () { - showDialog( - context: context, - builder: (context) => const AlertDialog(title: Text('Hello'))); - }, - )); - })); - Finder button = find.text('Press me'); - - await tester.tap(button); - await tester.pumpAndSettle(); - - expect(find.text('Hello'), findsOneWidget); - - await tester.sendKeyDownEvent(LogicalKeyboardKey.escape); - await tester.pumpAndSettle(); - - expect(find.text('Hello'), findsNothing); - }); - - testWidgets('key down esc (can\'t pop)', (tester) async { - await TestHelper.baseTestWidget(tester, Builder(builder: (context) { - return LauncherShortcuts( - child: TextButton( - child: const Text('Press me'), - onPressed: () { - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => const AlertDialog(title: Text('Hello'))); - }, - )); - })); - Finder button = find.text('Press me'); - - await tester.tap(button); - await tester.pumpAndSettle(); - - expect(find.text('Hello'), findsOneWidget); - - await tester.sendKeyDownEvent(LogicalKeyboardKey.escape); - await tester.pumpAndSettle(); - - expect(find.text('Hello'), findsOneWidget); - }); -} diff --git a/test/widget/widget_test.dart b/test/widget/widget_test.dart deleted file mode 100644 index 570bdcc11..000000000 --- a/test/widget/widget_test.dart +++ /dev/null @@ -1,385 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:path/path.dart'; -import 'package:rpmlauncher/i18n/language_selector.dart'; -import 'package:rpmlauncher/model/account/Account.dart'; -import 'package:rpmlauncher/model/Game/GameLogs.dart'; -import 'package:rpmlauncher/model/IO/Properties.dart'; -import 'package:rpmlauncher/screen/account.dart'; -import 'package:rpmlauncher/config/config.dart'; -import 'package:rpmlauncher/util/data.dart'; -import 'package:rpmlauncher/i18n/i18n.dart'; -import 'package:rpmlauncher/view/row_scroll_view.dart'; -import 'package:rpmlauncher/widget/AccountManageAction.dart'; -import 'package:rpmlauncher/widget/dialog/agree_eula_dialog.dart'; -import 'package:rpmlauncher/widget/dialog/CheckDialog.dart'; -import 'package:rpmlauncher/widget/dialog/GameCrash.dart'; -import 'package:rpmlauncher/widget/dialog/quick_setup.dart'; -import 'package:rpmlauncher/widget/dialog/UnSupportedForgeVersion.dart'; -import 'package:rpmlauncher/widget/FileDeleteError.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/DynamicImageFile.dart'; -import 'package:rpmlauncher/widget/rpmtw_design/NewFeaturesWidget.dart'; -import 'package:rpmlauncher/widget/settings/java_path.dart'; -import 'package:uuid/uuid.dart'; - -import '../script/test_helper.dart'; - -void main() { - setUpAll(() => TestHelper.init()); - - testWidgets("RowScrollView", (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, - RowScrollView( - child: Row( - children: List.generate( - 100, - (index) => Column( - children: const [ - Text("Hello"), - Text("World"), - ], - )).toList(), - ), - ), - ); - - expect(find.text("Hello"), findsWidgets); - expect(find.text("World"), findsWidgets); - - await tester.drag(find.text('Hello').first, const Offset(0.0, -300)); - await tester.pump(); - }); - testWidgets( - "Java Path Widget", - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const Material(child: JavaPathSettings())); - - expect(find.text("Java 8"), findsOneWidget); - expect(find.text("Java 16"), findsOneWidget); - expect(find.text("Java 17"), findsOneWidget); - }, - ); - - testWidgets( - "I18nText Widget", - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, - Material( - child: Column( - children: [ - I18nText("gui.ok"), - I18nText.errorInfoText(), - I18nText.tipsInfoText() - ], - ))); - - expect(find.text(I18n.format('gui.ok')), findsOneWidget); - expect(find.text(I18n.format('gui.error.info')), findsOneWidget); - expect(find.text(I18n.format('gui.tips.info')), findsOneWidget); - }, - ); - testWidgets( - "UnSupportedForgeVersion Widget", - (WidgetTester tester) async { - await TestHelper.baseTestWidget(tester, - Material(child: UnSupportedForgeVersion(gameVersion: "1.7.10"))); - - expect( - find.text(I18n.format('version.list.mod.loader.forge.error', - args: {"version": "1.7.10"})), - findsOneWidget); - }, - ); - testWidgets( - "GameCrash Widget", - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, - const Material( - child: GameCrash(errorCode: 1, errorLog: "Hello World"))); - - expect(find.text('Hello World'), findsOneWidget); - expect(find.text("${I18n.format("log.game.crash.code")}: 1"), - findsOneWidget); - }, - ); - testWidgets( - "CheckDialog Widget (press ok)", - (WidgetTester tester) async { - bool confirm = false; - await TestHelper.baseTestWidget( - tester, - Material( - child: CheckDialog( - title: "Tips", - message: "Hello World", - onPressedOK: (context) { - confirm = true; - }, - onPressedCancel: (context) { - confirm = false; - }, - ))); - - Finder confirmButton = find.text(I18n.format("gui.confirm")); - - await tester.tap(confirmButton); - await tester.pumpAndSettle(); - - expect(find.text('Hello World'), findsOneWidget); - expect(find.text("Tips"), findsOneWidget); - expect(confirm, true); - }, - ); - - testWidgets( - "CheckDialog Widget (press cancel)", - (WidgetTester tester) async { - bool confirm = false; - await TestHelper.baseTestWidget( - tester, - Material( - child: CheckDialog( - title: "Tips", - message: "Hello World", - onPressedOK: (context) { - confirm = true; - }, - onPressedCancel: (context) { - confirm = false; - }, - ))); - - Finder cancelButton = find.text(I18n.format("gui.cancel")); - - await tester.tap(cancelButton); - await tester.pumpAndSettle(); - - expect(find.text('Hello World'), findsOneWidget); - expect(find.text("Tips"), findsOneWidget); - expect(confirm, false); - }, - ); - - testWidgets( - "QuickSetup widget (agree)", - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const Material(child: QuickSetup())); - - expect(find.text(I18n.format('init.quick_setup.title')), findsOneWidget); - expect(find.byType(LanguageSelectorWidget), findsOneWidget); - - Finder nextButton = find.text(I18n.format("gui.next")); - - await tester.tap(nextButton); - await tester.pumpAndSettle(); - - expect( - find.text(I18n.format('rpmlauncher.privacy.title')), findsOneWidget); - - Finder agreeButton = find.text(I18n.format("gui.agree")); - - await tester.tap(agreeButton); - await tester.pumpAndSettle(); - - expect(launcherConfig.isInit, true); - expect(ConfigHelper.get('init'), true); - - ConfigHelper.set('init', false); - }, - ); - testWidgets( - "QuickSetup widget (disagree)", - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const Material(child: QuickSetup())); - - expect(find.text(I18n.format('init.quick_setup.title')), findsOneWidget); - expect(find.byType(LanguageSelectorWidget), findsOneWidget); - - Finder nextButton = find.text(I18n.format("gui.next")); - - await tester.tap(nextButton); - await tester.pumpAndSettle(); - - expect( - find.text(I18n.format('rpmlauncher.privacy.title')), findsOneWidget); - - Finder disagreeButton = find.text(I18n.format("gui.disagree")); - - await tester.tap(disagreeButton); - await tester.pumpAndSettle(); - - expect(launcherConfig.isInit, false); - }, - ); - - testWidgets("LogView Widget", (WidgetTester tester) async { - String logString = TestData.fabric118Log.getFileString(); - GameLogs logs = GameLogs.empty(); - final List lines = logString.split('\n'); - for (int i = 0; i < lines.length; i++) { - String line = lines[i]; - if (line.isNotEmpty) { - logs.addLog(line); - } - } - - await TestHelper.baseTestWidget( - tester, - Material( - child: ListView(children: logs.map((e) => e.widget).toList()))); - - expect(find.text('Loading for game Minecraft 1.18'), findsOneWidget); - - expect(find.text('main'), findsWidgets); - - await tester.dragUntilVisible(find.text("已中斷宇宙通訊的連線"), - find.byType(ListView), const Offset(0.0, -300)); - await tester.pumpAndSettle(); - - expect(find.text('Render thread'), findsWidgets); - expect(find.text("已中斷宇宙通訊的連線"), findsOneWidget); - }, skip: Platform.isWindows); - - testWidgets( - "Agree EULA Dialog Widget (Agree)", - (WidgetTester tester) async { - Properties properties = Properties({'eula': false.toString()}); - - File eulaFile = File(join( - dataHome.path, - "eula.txt", - )); - - await TestHelper.baseTestWidget( - tester, - Material( - child: - AgreeEulaDialog(properties: properties, eulaFile: eulaFile))); - - expect( - find.text(I18n.format("launcher.server.eula.title")), findsOneWidget); - expect(find.text(I18n.format("launcher.server.eula")), findsOneWidget); - - Finder agreeButton = find.text(I18n.format("gui.agree")); - - await tester.tap(agreeButton); - await tester.pumpAndSettle(); - - Properties eulaProperties = - Properties.decode(eulaFile.readAsStringSync()); - expect(eulaProperties['eula'], true.toString()); - }, - ); - - testWidgets("Agree EULA Dialog Widget (Disagree)", - (WidgetTester tester) async { - Properties properties = Properties({'eula': false.toString()}); - - File eulaFile = File(join( - dataHome.path, - "eula.txt", - )); - - await TestHelper.baseTestWidget( - tester, - Material( - child: - AgreeEulaDialog(properties: properties, eulaFile: eulaFile))); - Finder disagreeButton = find.text(I18n.format("gui.disagree")); - - await tester.tap(disagreeButton); - await tester.pumpAndSettle(); - }); - testWidgets( - "New Features Widget", - (WidgetTester tester) async { - await TestHelper.baseTestWidget(tester, - const Material(child: NewFeaturesWidget(child: Text("Hello World")))); - - expect(find.text('Hello World'), findsOneWidget); - expect(find.byIcon(Icons.star_rate), findsOneWidget); - }, - ); - testWidgets( - "Account Manage Button Widget (None account)", - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, - const Material(child: AccountManageButton()), - ); - - Finder button = find.byIcon(Icons.manage_accounts); - - expect(button, findsOneWidget); - - await tester.tap(button); - await tester.pumpAndSettle(); - - expect(find.byType(AccountScreen), findsOneWidget); - }, - ); - testWidgets( - "Account Manage Button Widget (Has account)", - (WidgetTester tester) async { - AccountStorage() - .add(AccountType.microsoft, "", const Uuid().v4(), "RPMTW"); - - await TestHelper.baseTestWidget( - tester, const Material(child: AccountManageButton())); - - Finder button = find.byType(Tooltip); - - expect(button, findsOneWidget); - expect(find.byType(InkResponse), findsOneWidget); - - await tester.tap(button); - await tester.pumpAndSettle(); - - expect(find.byType(AccountScreen), findsOneWidget); - }, - ); - testWidgets( - "File Delete Error Widget", - (WidgetTester tester) async { - await TestHelper.baseTestWidget( - tester, const Material(child: FileDeleteError())); - - expect(find.text(I18n.format("rpmlauncher.file.delete.error")), - findsOneWidget); - }, - ); - testWidgets("Dynamic Image File Widget", (WidgetTester tester) async { - File imageFile = File(join( - dataHome.path, - "temp", - "icon.png", - )); - imageFile.createSync(recursive: true); - TestData.rpmlauncherLogo.getFile().copySync(imageFile.path); - - await TestHelper.baseTestWidget( - tester, Material(child: DynamicImageFile(imageFile: imageFile))); - - expect(find.byType(Image), findsOneWidget); - - imageFile.deleteSync(recursive: true); - - File imageFile2 = File(join( - dataHome.path, - "temp", - "icon2.png", - )); - imageFile2.createSync(recursive: true); - TestData.rpmlauncherLogo.getFile().copySync(imageFile2.path); - await TestHelper.baseTestWidget( - tester, Material(child: DynamicImageFile(imageFile: imageFile2))); - }, skip: Platform.isWindows); -} diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index f1b861cfd..22072c06b 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -93,7 +93,7 @@ BEGIN VALUE "FileDescription", "RPMLauncher" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "RPMLauncher" "\0" - VALUE "LegalCopyright", "Copyright (C) 2021-2022 The RPMTW Team All rights reserved." "\0" + VALUE "LegalCopyright", "Copyright (C) 2021-2023 The RPMTW Team All rights reserved." "\0" VALUE "OriginalFilename", "RPMLauncher.exe" "\0" VALUE "ProductName", "RPMLauncher" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index d5f311b3f..076cab9fb 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ