├── .editorconfig ├── .github └── workflows │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── arrow.png ├── icon.ase ├── model.bbmodel └── texture.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── icon.png ├── settings.gradle └── src └── main ├── java └── com │ └── sindercube │ └── serverUnpacker │ ├── MixinPlugin.java │ ├── ServerUnpacker.java │ ├── mixin │ ├── ClassicExtractingMixin.java │ └── ModernExtractingMixin.java │ ├── tool │ ├── MainGui.java │ ├── Presenter.java │ └── ServerUnpackerTool.java │ └── util │ ├── NativePackExtractor.java │ └── PackExtractor.java └── resources ├── assets └── server_unpacker │ └── icon.png ├── fabric.mod.json └── server_unpacker.mixins.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: [published] 4 | 5 | concurrency: 6 | group: "build" 7 | cancel-in-progress: true 8 | 9 | env: 10 | MODRINTH_ID: "B3fgRqfr" 11 | CURSEFORGE_ID: "1104191" 12 | 13 | jobs: 14 | publish: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout Repo 18 | uses: actions/checkout@v3 19 | - name: Setup JDK 20 | uses: actions/setup-java@v3 21 | with: 22 | distribution: "temurin" 23 | java-version: 21 24 | cache: "gradle" 25 | - name: Change Version 26 | run: sed -i 's/mod_version=.*/mod_version=${{ github.event.release.tag_name }}/g' gradle.properties 27 | - name: Build 28 | run: ./gradlew clean build 29 | - name: Publish 30 | uses: Kir-Antipov/mc-publish@v3.3 31 | with: 32 | curseforge-id: "${{ env.CURSEFORGE_ID }}" 33 | curseforge-token: "${{ secrets.CURSEFORGE_TOKEN }}" 34 | curseforge-version-type: "${{ steps.get-release.outputs.release-type }}" 35 | modrinth-id: "${{ env.MODRINTH_ID }}" 36 | modrinth-token: "${{ secrets.MODRINTH_TOKEN }}" 37 | modrinth-version-type: "${{ steps.get-release.outputs.release-type }}" 38 | github-tag: "${{ github.event.release.tag_name }}" 39 | github-token: "${{ secrets.GITHUB_TOKEN }}" 40 | 41 | name: "Server Unpacker v${{ github.event.release.tag_name }}" 42 | version: "${{ github.event.release.tag_name }}" 43 | version-type: "${{ github.event.release.prerelease && 'beta' || 'release' }}" 44 | changelog: "${{ github.event.release.body }}" 45 | java: | 46 | 17 47 | game-versions: | 48 | >=1.17 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | build/ 4 | run/ 5 | server/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Github](https://img.shields.io/badge/github-434956?style=for-the-badge&logo=github&logoColor=8994ce)](https://github.com/Sindercube/Server-Unpacker) 2 | [![Modrinth](https://img.shields.io/badge/modrinth-434956?style=for-the-badge&logo=modrinth&logoColor=8994ce)](https://modrinth.com/mod/Server-Unpacker) 3 | [![Curseforge](https://img.shields.io/badge/curseforge-434956?style=for-the-badge&logo=curseforge&logoColor=8994ce)](https://www.curseforge.com/minecraft/mc-mods/Server-Unpacker) 4 | ![Game Versions](https://img.shields.io/modrinth/game-versions/B3fgRqfr?style=for-the-badge&labelColor=434956&color=8994ce) 5 | 6 | # About 7 | 8 | **Server Unpacker** is a **Fabric client-side mod** that extracts any resource pack files that can be loaded by Minecraft. 9 | 10 | *Made for those pesky servers who like to hide their assets using corrupt ZIP file headers.* 11 | 12 | # Mod Usage 13 | 14 | Drag and drop the mod into your `mods/` folder and join any server that has a required resource pack. 15 | 16 | The extracted files will be in your [`.minecraft`](https://minecraft.wiki/.minecraft)`/extracted-packs/` folder. 17 | 18 | # GUI Usage 19 | 20 | Run the .jar file to open the GUI. 21 | 22 | You can select or drag and drop file(s) onto the GUI to extract them in the same directory. 23 | 24 | # CLI Usage 25 | 26 | You can extract multiple files by using the following command: 27 | 28 | `java -jar server-unpacker-x.jar [files]` 29 | 30 | Where `x` is your downloaded version. 31 | 32 | # Credits 33 | 34 | - Thank you to [Zardium](https://gitlab.com/Zardium) for writing the original code and making the GUI part of the mod. 35 | -------------------------------------------------------------------------------- /assets/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sindercube/Server-Unpacker/7cdcd8a71d8dbe9224059b690bd0f40cf4a7420e/assets/arrow.png -------------------------------------------------------------------------------- /assets/icon.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sindercube/Server-Unpacker/7cdcd8a71d8dbe9224059b690bd0f40cf4a7420e/assets/icon.ase -------------------------------------------------------------------------------- /assets/model.bbmodel: -------------------------------------------------------------------------------- 1 | {"meta":{"format_version":"4.5","model_format":"free","box_uv":true},"name":"model","model_identifier":"","visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"timeline_setups":[],"unhandled_root_fields":{},"resolution":{"width":32,"height":32},"elements":[{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,0,-4],"to":[4,5,4],"autouv":0,"color":4,"origin":[-3,0,-3],"uv_offset":[0,12],"faces":{"north":{"uv":[8,20,16,25],"texture":0},"east":{"uv":[0,20,8,25],"texture":0},"south":{"uv":[24,20,32,25],"texture":0},"west":{"uv":[16,20,24,25],"texture":0},"up":{"uv":[16,20,8,12],"texture":0},"down":{"uv":[24,12,16,20],"texture":0}},"type":"cube","uuid":"f7b7f01b-0b0c-f059-8dc5-2b4f67218080"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,1,-1.0000000000000007],"to":[4,5,7],"autouv":0,"color":4,"origin":[0,0,0],"faces":{"north":{"uv":[8,8,16,12],"texture":0},"east":{"uv":[0,8,8,12],"texture":0},"south":{"uv":[24,8,32,12],"texture":0},"west":{"uv":[16,8,24,12],"texture":0},"up":{"uv":[16,8,8,0],"texture":0},"down":{"uv":[24,0,16,8],"texture":0}},"type":"cube","uuid":"c5d76ee9-5a25-7d2b-68b9-bdc9cfacc9b2"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-1,0,-1.9999999999999998],"to":[1,3,-1.0000000000000007],"autouv":0,"color":4,"origin":[0,0,0],"uv_offset":[24,0],"faces":{"north":{"uv":[25,1,27,4],"texture":0},"east":{"uv":[24,1,25,4],"texture":0},"south":{"uv":[28,1,30,4],"texture":0},"west":{"uv":[27,1,28,4],"texture":0},"up":{"uv":[27,1,25,0],"texture":0},"down":{"uv":[29,0,27,1],"texture":0}},"type":"cube","uuid":"0f7440b6-ae28-94ec-4e5b-74c695141483"}],"outliner":["f7b7f01b-0b0c-f059-8dc5-2b4f67218080",{"name":"bone","origin":[3,5,7],"rotation":[90,0,0],"color":0,"uuid":"6901e886-22e9-ccbd-1ccc-0af2578968d4","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["c5d76ee9-5a25-7d2b-68b9-bdc9cfacc9b2","0f7440b6-ae28-94ec-4e5b-74c695141483"]}],"textures":[{"path":"/home/soumeh/Desktop/texture.png","name":"texture.png","folder":"","namespace":"","id":"0","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"aa205ffc-f470-1229-5bba-d6faa880d618","relative_path":"../texture.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAApBJREFUWEdjZCAAXG30/3/6+gOrqhevPzCoKUgw4JN/+OQl49mzZ/9funSJQU9PjwFEJyYmMsIMhDNwuQPkgDTbfwxPPnyAK5FgZWJg4eZjqF/zgUFaXIghVv8LinZW5j9gfvP2fwyLV2wAs0EWgwC6I4hyAMgCmKEgQ37/ZQEb1rHrF9gBIAfO3XwZLPbxFwMDPxsDQ7KvLtiBMAesXLkSLB8eHs4AYnd1dYHtJsoBXrrvGGQEBMAG/Pz5C+7bts3fwA4AyW/Z95iB4T8iIHycZRlm72FgKK1qAgtev34dTGtqaoLZJDkg3gySBl5//4YSDaAgBjkAJL9g+00GFmaINCgUwqAOuHb7MV5PEh0CyJEsyskF5sJCIMnoA8OkHQ/BYqDg//OXgSHZVR6cBqjiAPQ0AHMMLARAaQAEQAkVFFWwBAuKAoodoKUqixSzyOHAwPD1+y8Gbk42VEEkHkgelA1xKgAlQkrzOaFygJA82AHE5vNPn76CPSMsyA6mkaOA3HIC7ABi8vnLJ6/hIfn1008GJS0ZlIKIUDkB0vzn6yeGF7//wbM0qJzAGwWfv0DyOa6ilhryRGVDSusCvIkQnyRIjpQ0AjMLuS6gOBvC0kjc1Lsobl2UrYxSF+CKY6o4AFTWF05/jOKAJUXq8JIQJA8CoEIIVleAim2qFESgEACV9TF9N1EcsDwPUtTC6gJ2dkiBhJwdqeYAQiEAqgtgVTTIETRJA8j5fOTUBdQoaAgVZOCSkJgGx4fXH1GKYjYpHnAqhyVCkCS+BguuRAp2ACwbwWzA1uAAJbLnTyDZTU6eD6UyIlY/toIKa2VESiKDtYrJTaSMlDY4CDVICMkTrIwI1RWUyo86YDQERkMAANjlF86UOR2LAAAAAElFTkSuQmCC"}]} -------------------------------------------------------------------------------- /assets/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sindercube/Server-Unpacker/7cdcd8a71d8dbe9224059b690bd0f40cf4a7420e/assets/texture.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.7-SNAPSHOT' 3 | } 4 | 5 | tasks.withType(JavaCompile).configureEach { 6 | options.release = 16 7 | } 8 | 9 | java { 10 | sourceCompatibility = JavaVersion.VERSION_16 11 | targetCompatibility = JavaVersion.VERSION_16 12 | } 13 | 14 | base.archivesName = mod_id 15 | group = maven_group 16 | version = mod_version 17 | 18 | dependencies { 19 | minecraft "com.mojang:minecraft:$minecraft_version" 20 | mappings "net.fabricmc:yarn:$yarn_mappings:v2" 21 | modImplementation "net.fabricmc:fabric-loader:$loader_version" 22 | } 23 | 24 | jar { 25 | manifest { 26 | attributes 'Main-Class': "com.sindercube.serverUnpacker.tool.ServerUnpackerTool" 27 | } 28 | } 29 | 30 | processResources { 31 | filesMatching("fabric.mod.json") { 32 | expand project.properties 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | mod_id=server-unpacker 2 | mod_version=1.0.0 3 | maven_group=com.sindercube 4 | 5 | minecraft_version=1.21.1 6 | yarn_mappings=1.21.1+build.3 7 | loader_version=0.16.5 8 | 9 | org.gradle.jvmargs=-Xmx2G 10 | org.gradle.parallel=true 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sindercube/Server-Unpacker/7cdcd8a71d8dbe9224059b690bd0f40cf4a7420e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sindercube/Server-Unpacker/7cdcd8a71d8dbe9224059b690bd0f40cf4a7420e/icon.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url "https://maven.fabricmc.net/" } 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "Server Unpacker" 9 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/MixinPlugin.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 6 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | public class MixinPlugin implements IMixinConfigPlugin { 13 | 14 | private static final String GAME_VERSION = FabricLoader.getInstance() 15 | .getModContainer("minecraft") 16 | .orElseThrow(() -> null) 17 | .getMetadata() 18 | .getVersion() 19 | .getFriendlyString(); 20 | 21 | 22 | @Override 23 | public void onLoad(String mixinPackage) {} 24 | 25 | @Override 26 | public String getRefMapperConfig() { 27 | return null; 28 | } 29 | 30 | @Override 31 | public boolean shouldApplyMixin(String target, String mixin) { 32 | return true; 33 | } 34 | 35 | @Override 36 | public void acceptTargets(Set myTargets, Set otherTargets) {} 37 | 38 | @Override 39 | public List getMixins() { 40 | List result = new ArrayList<>(); 41 | 42 | int majorVersion; 43 | int minorVersion; 44 | String[] versions = GAME_VERSION.split("\\."); 45 | try { 46 | majorVersion = Integer.parseInt(versions[1]); 47 | } catch (ArrayIndexOutOfBoundsException exception) { 48 | majorVersion = 0; 49 | } 50 | try { 51 | minorVersion = Integer.parseInt(versions[2]); 52 | } catch (ArrayIndexOutOfBoundsException exception) { 53 | minorVersion = 0; 54 | } 55 | boolean isModern = majorVersion >= 21 || (majorVersion == 20 && minorVersion >= 3); 56 | 57 | if (isModern) result.add("ModernExtractingMixin"); 58 | else result.add("ClassicExtractingMixin"); 59 | return result; 60 | } 61 | 62 | @Override 63 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} 64 | 65 | @Override 66 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/ServerUnpacker.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker; 2 | 3 | import com.sindercube.serverUnpacker.util.NativePackExtractor; 4 | import net.fabricmc.api.ClientModInitializer; 5 | import net.fabricmc.loader.api.FabricLoader; 6 | import net.minecraft.client.MinecraftClient; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | 10 | import java.io.File; 11 | import java.nio.file.Path; 12 | 13 | public class ServerUnpacker implements ClientModInitializer { 14 | 15 | public static final Logger LOGGER = LogManager.getLogger("Server Unpacker"); 16 | 17 | @Override 18 | public void onInitializeClient() { 19 | ServerUnpacker.LOGGER.info("Initialized!"); 20 | } 21 | 22 | 23 | public static void extractServerPack(File file) { 24 | ServerUnpacker.LOGGER.info("Extracting server pack {}", file); 25 | 26 | var info = MinecraftClient.getInstance().getCurrentServerEntry(); 27 | String name = info == null ? file.getName() : info.address; 28 | Path destination = FabricLoader.getInstance().getGameDir().resolve("extracted-packs/"); 29 | try { 30 | NativePackExtractor.INSTANCE.extractPack(destination, file, name); 31 | } catch (Exception exception) { 32 | LOGGER.error(exception); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/mixin/ClassicExtractingMixin.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker.mixin; 2 | 3 | import com.sindercube.serverUnpacker.ServerUnpacker; 4 | import net.minecraft.resource.ResourcePackSource; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Pseudo; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 | 11 | import java.io.File; 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | @Pseudo 15 | @Mixin(targets = "net.minecraft.class_1066") 16 | public class ClassicExtractingMixin { 17 | 18 | @Inject(at = @At("TAIL"), method = "method_4638", remap = false) 19 | public void loadServerPack(File file, ResourcePackSource source, CallbackInfoReturnable> cir) { 20 | ServerUnpacker.extractServerPack(file); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/mixin/ModernExtractingMixin.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker.mixin; 2 | 3 | import com.sindercube.serverUnpacker.ServerUnpacker; 4 | import net.minecraft.client.resource.server.ReloadScheduler; 5 | import net.minecraft.client.resource.server.ServerResourcePackLoader; 6 | import net.minecraft.resource.ResourcePackProfile; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | import java.nio.file.Path; 13 | import java.util.List; 14 | 15 | @Mixin(ServerResourcePackLoader.class) 16 | public class ModernExtractingMixin { 17 | 18 | @Inject(method = "toProfiles", at = @At("TAIL")) 19 | public void loadServerPacks(List packs, CallbackInfoReturnable> cir) { 20 | packs.stream() 21 | .map(ReloadScheduler.PackInfo::path) 22 | .map(Path::toFile) 23 | .forEach(ServerUnpacker::extractServerPack); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/tool/MainGui.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker.tool; 2 | 3 | import javax.imageio.ImageIO; 4 | import javax.swing.*; 5 | import java.awt.dnd.DropTarget; 6 | import java.awt.event.*; 7 | import java.awt.image.BufferedImage; 8 | import java.io.InputStream; 9 | 10 | public class MainGui { 11 | 12 | public static final String ICON_PATH = "assets/server_unpacker/icon.png"; 13 | 14 | private JButton uploadButton; 15 | private JProgressBar progressBar; 16 | 17 | public MainGui() { 18 | JFrame frame = new JFrame("Server Unpacker"); 19 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 20 | frame.setSize(240, 240); 21 | try { 22 | InputStream stream = this.getClass().getClassLoader().getResourceAsStream(ICON_PATH); 23 | assert stream != null; 24 | BufferedImage image = ImageIO.read(stream); 25 | frame.setIconImage(image); 26 | } catch (Exception ignored) {} 27 | frame.add(createMainPanel()); 28 | frame.setVisible(true); 29 | } 30 | 31 | public void addUploadButtonActionListener(ActionListener actionListener) { 32 | uploadButton.addActionListener(actionListener); 33 | } 34 | 35 | public void setUploadButtonDropTarget(DropTarget dropTarget) { 36 | uploadButton.setDropTarget(dropTarget); 37 | } 38 | 39 | public void setProgress(int progress) { 40 | progressBar.setValue(progress); 41 | } 42 | 43 | private JPanel createMainPanel() { 44 | JPanel panel = new JPanel(); 45 | 46 | uploadButton = new JButton("Upload File"); 47 | uploadButton.setSize(300,100); 48 | panel.add(uploadButton); 49 | 50 | JLabel dropInfo = new JLabel("or Drag and Drop"); 51 | panel.add(dropInfo); 52 | 53 | progressBar = new JProgressBar(); 54 | progressBar.setMaximum(100); 55 | progressBar.setValue(0); 56 | progressBar.setStringPainted(true); 57 | panel.add(progressBar); 58 | 59 | return panel; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/tool/Presenter.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker.tool; 2 | 3 | import com.sindercube.serverUnpacker.util.PackExtractor; 4 | 5 | import javax.swing.*; 6 | import java.awt.datatransfer.DataFlavor; 7 | import java.awt.dnd.DnDConstants; 8 | import java.awt.dnd.DropTarget; 9 | import java.awt.dnd.DropTargetDropEvent; 10 | import java.awt.event.ActionEvent; 11 | import java.io.File; 12 | import java.util.List; 13 | import java.util.concurrent.atomic.AtomicLong; 14 | import java.util.logging.Level; 15 | 16 | public class Presenter { 17 | 18 | private final MainGui screen; 19 | 20 | public Presenter(MainGui screen) { 21 | this.screen = screen; 22 | } 23 | 24 | void start() { 25 | screen.addUploadButtonActionListener(this::actionPerformed); 26 | screen.setUploadButtonDropTarget(new PresenterDropTarget()); 27 | } 28 | 29 | public void actionPerformed(ActionEvent ae) { 30 | JFileChooser fileChooser = new JFileChooser(); 31 | int returnValue = fileChooser.showOpenDialog(null); 32 | if (returnValue == JFileChooser.APPROVE_OPTION) { 33 | try { 34 | for (File file : fileChooser.getSelectedFiles()) { 35 | PackExtractorWorker worker = new PackExtractorWorker(file); 36 | worker.addPropertyChangeListener(evt -> { 37 | if ("progress".equals(evt.getPropertyName())) { 38 | screen.setProgress((Integer) evt.getNewValue()); 39 | } 40 | }); 41 | worker.execute(); 42 | } 43 | } catch (Exception exception) { 44 | ServerUnpackerTool.LOGGER.log(Level.WARNING, "an exception was thrown", exception); 45 | } 46 | } 47 | } 48 | 49 | public class PresenterDropTarget extends DropTarget { 50 | 51 | @SuppressWarnings("unchecked") 52 | public void drop(DropTargetDropEvent evt) { 53 | try { 54 | evt.acceptDrop(DnDConstants.ACTION_COPY); 55 | List droppedFiles = (List) evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); 56 | evt.dropComplete(true); 57 | for (File file : droppedFiles) { 58 | PackExtractorWorker worker = new PackExtractorWorker(file); 59 | worker.addPropertyChangeListener(evt1 -> { 60 | if ("progress".equals(evt1.getPropertyName())) { 61 | screen.setProgress((Integer) evt1.getNewValue()); 62 | } 63 | }); 64 | worker.execute(); 65 | } 66 | } catch (Exception exception) { 67 | ServerUnpackerTool.LOGGER.log(Level.WARNING, "an exception was thrown", exception); 68 | } 69 | } 70 | 71 | } 72 | 73 | private static class PackExtractorWorker extends SwingWorker { 74 | 75 | private final File file; 76 | 77 | PackExtractorWorker(File file) { 78 | this.file = file; 79 | } 80 | 81 | @Override 82 | protected Object doInBackground() { 83 | AtomicLong totalItemCount = new AtomicLong(); 84 | AtomicLong currentItemCount = new AtomicLong(0); 85 | PackExtractor.INSTANCE.extractPack( 86 | file.getParentFile().toPath(), 87 | file, 88 | file.getName().replaceFirst("[.][^.]+$", ""), 89 | totalItemCount::set, 90 | () -> { 91 | final long currentCount = currentItemCount.incrementAndGet(); 92 | final float percentComplete = (float)currentCount / totalItemCount.get(); 93 | setProgress((int)(percentComplete * 100)); 94 | } 95 | ); 96 | return null; 97 | } 98 | 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/tool/ServerUnpackerTool.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker.tool; 2 | 3 | import com.sindercube.serverUnpacker.util.PackExtractor; 4 | 5 | import java.awt.*; 6 | import java.io.File; 7 | import java.nio.file.Path; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | public class ServerUnpackerTool implements Runnable { 12 | 13 | public static final Logger LOGGER = Logger.getLogger("Server Unpacker"); 14 | 15 | public static void main(String[] args) { 16 | LOGGER.info("Started!"); 17 | if (args.length < 1) EventQueue.invokeLater(new ServerUnpackerTool()); 18 | for (String filePath : args) { 19 | try { 20 | File file = new File(filePath); 21 | String name = file.getName().replaceFirst("[.][^.]+$", ""); 22 | Path parent = file.getParentFile() != null ? file.getParentFile().toPath() : Path.of("").toAbsolutePath(); 23 | PackExtractor.INSTANCE.extractPack(parent, file, name); 24 | } catch (Exception exception) { 25 | LOGGER.log(Level.WARNING, "an exception was thrown", exception); 26 | } 27 | } 28 | } 29 | 30 | @Override 31 | public void run() { 32 | Presenter presenter = new Presenter(new MainGui()); 33 | presenter.start(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/util/NativePackExtractor.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker.util; 2 | 3 | import net.minecraft.client.texture.NativeImage; 4 | 5 | import java.io.*; 6 | 7 | public class NativePackExtractor extends PackExtractor { 8 | 9 | public static final PackExtractor INSTANCE = new NativePackExtractor(); 10 | 11 | @Override 12 | public void writeFile(InputStream stream, File file) throws IOException { 13 | if (file.getPath().endsWith(".png")) { 14 | try { 15 | NativeImage image = NativeImage.read(stream); 16 | image.writeTo(file); 17 | } catch (Exception exception) { 18 | throw new RuntimeException(exception); 19 | } 20 | } else { 21 | super.writeFile(stream, file); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/sindercube/serverUnpacker/util/PackExtractor.java: -------------------------------------------------------------------------------- 1 | package com.sindercube.serverUnpacker.util; 2 | 3 | import java.io.*; 4 | import java.nio.file.Path; 5 | import java.util.function.LongConsumer; 6 | import java.util.zip.ZipEntry; 7 | import java.util.zip.ZipFile; 8 | 9 | public class PackExtractor { 10 | 11 | public static final PackExtractor INSTANCE = new PackExtractor(); 12 | 13 | public void extractPack(Path destination, File pack, String name) { 14 | extractPack(destination, pack, name, c -> {}, () -> {}); 15 | } 16 | 17 | public void extractPack(Path destination, File pack, String name, LongConsumer itemCountConsumer, Runnable onItemFinished) { 18 | try { 19 | ZipFile zip = new ZipFile(pack.toString()); 20 | itemCountConsumer.accept(zip.size()); 21 | 22 | for (ZipEntry entry : zip.stream().toList()) { 23 | File newFile = new File(destination.resolve(name).toFile(), entry.getName()); 24 | if (entry.isDirectory()) return; 25 | 26 | boolean ignored = newFile.getParentFile().mkdirs(); 27 | BufferedInputStream inputStream = new BufferedInputStream(zip.getInputStream(entry)); 28 | this.writeFile(inputStream, newFile); 29 | inputStream.close(); 30 | onItemFinished.run(); 31 | } 32 | } catch (Exception exception) { 33 | throw new RuntimeException(exception); 34 | } 35 | } 36 | 37 | public void writeFile(InputStream stream, File file) throws IOException { 38 | try (FileOutputStream outputStream = new FileOutputStream(file)) { 39 | byte[] buffer = new byte[4096]; 40 | int read; 41 | while ((read = stream.read(buffer)) >= 0) { 42 | outputStream.write(buffer, 0, read); 43 | } 44 | } catch (IOException exception) { 45 | throw new RuntimeException(exception); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/assets/server_unpacker/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sindercube/Server-Unpacker/7cdcd8a71d8dbe9224059b690bd0f40cf4a7420e/src/main/resources/assets/server_unpacker/icon.png -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "server_unpacker", 4 | "name": "Server Unpacker", 5 | 6 | "version": "${mod_version}", 7 | "description": "Extract those pesky Server Resource Packs", 8 | "authors": [ "Sindercube" ], 9 | "contact": { 10 | "sources": "https://github.com/Sindercube/Server-Unpacker", 11 | "homepage": "https://modrinth.com/mod/Server-Unpacker" 12 | }, 13 | "license": "LGPL-3.0-only", 14 | "entrypoints": { 15 | "client": [ "com.sindercube.serverUnpacker.ServerUnpacker" ] 16 | }, 17 | "icon": "assets/server_unpacker/icon.png", 18 | 19 | "environment": "client", 20 | "mixins": [ "server_unpacker.mixins.json" ], 21 | "depends": { 22 | "java": ">=16", 23 | "minecraft": ">=1.17" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/server_unpacker.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.sindercube.serverUnpacker.mixin", 5 | "plugin": "com.sindercube.serverUnpacker.MixinPlugin", 6 | "compatibilityLevel": "JAVA_8", 7 | "injectors": { 8 | "defaultRequire": 1 9 | } 10 | } 11 | --------------------------------------------------------------------------------