├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── gradle.yml │ └── publish.yml ├── .gitignore ├── LICENSE ├── MCP-CREDITS.TXT ├── README.md ├── build.gradle ├── cli ├── build.gradle └── src │ └── main │ └── java │ └── org │ └── mcphackers │ └── mcp │ └── main │ └── MainCLI.java ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── gui ├── build.gradle └── src │ └── main │ └── java │ └── org │ └── mcphackers │ └── mcp │ ├── Theme.java │ ├── gui │ ├── GridBagConstraintsBuilder.java │ ├── MCPFrame.java │ ├── MenuBar.java │ ├── SideProgressBar.java │ ├── TaskButton.java │ ├── TextAreaContextMenu.java │ ├── TextAreaOutputStream.java │ └── WrapLayout.java │ └── main │ └── MainGUI.java ├── settings.gradle └── src ├── main ├── java │ └── org │ │ └── mcphackers │ │ └── mcp │ │ ├── DownloadListener.java │ │ ├── Language.java │ │ ├── MCP.java │ │ ├── MCPPaths.java │ │ ├── Options.java │ │ ├── TranslatorUtil.java │ │ ├── Update.java │ │ ├── plugin │ │ ├── MCPPlugin.java │ │ ├── PluginClassLoader.java │ │ └── PluginManager.java │ │ ├── tasks │ │ ├── ProgressListener.java │ │ ├── Task.java │ │ ├── TaskApplyPatch.java │ │ ├── TaskBuild.java │ │ ├── TaskCleanup.java │ │ ├── TaskCreatePatch.java │ │ ├── TaskDecompile.java │ │ ├── TaskDownloadUpdate.java │ │ ├── TaskRecompile.java │ │ ├── TaskReobfuscate.java │ │ ├── TaskRun.java │ │ ├── TaskRunnable.java │ │ ├── TaskSetup.java │ │ ├── TaskSourceBackup.java │ │ ├── TaskStaged.java │ │ ├── TaskUpdateMD5.java │ │ └── mode │ │ │ ├── TaskMode.java │ │ │ ├── TaskModeBuilder.java │ │ │ ├── TaskParameter.java │ │ │ └── TaskParameterMap.java │ │ └── tools │ │ ├── ClassUtils.java │ │ ├── FileUtil.java │ │ ├── JSONUtil.java │ │ ├── OS.java │ │ ├── Util.java │ │ ├── fernflower │ │ ├── DecompileLogger.java │ │ ├── Decompiler.java │ │ ├── SimpleJavadocProvider.java │ │ ├── TinyJavadocProvider.java │ │ └── ZipFileCache.java │ │ ├── injector │ │ └── GLConstants.java │ │ ├── mappings │ │ └── MappingUtil.java │ │ ├── project │ │ ├── EclipseProjectWriter.java │ │ ├── IdeaProjectWriter.java │ │ ├── PairWriter.java │ │ ├── ProjectWriter.java │ │ ├── VSCProjectWriter.java │ │ ├── XMLWriter.java │ │ └── eclipse │ │ │ ├── EclipseClasspath.java │ │ │ ├── EclipsePreferences.java │ │ │ ├── EclipseProject.java │ │ │ └── EclipseRunConfig.java │ │ ├── source │ │ └── Source.java │ │ └── versions │ │ ├── DownloadData.java │ │ ├── IDownload.java │ │ ├── VersionParser.java │ │ └── json │ │ ├── Artifact.java │ │ ├── AssetIndex.java │ │ ├── AssetIndexMeta.java │ │ ├── Classifiers.java │ │ ├── DependDownload.java │ │ ├── Downloads.java │ │ ├── Manifest.java │ │ ├── Rule.java │ │ ├── Version.java │ │ └── VersionMetadata.java └── resources │ ├── gl_constants.json │ ├── icon │ ├── rmcp.ico │ └── rmcp.png │ └── lang │ ├── cs_CZ.lang │ ├── de_DE.lang │ ├── en_US.lang │ ├── es_ES.lang │ ├── fr_FR.lang │ ├── nb_NO.lang │ ├── ru_RU.lang │ └── zh_CN.lang └── test ├── java └── org │ └── mcphackers │ └── mcp │ ├── TestPlugin.java │ └── tasks │ └── TaskMergeMappings.java └── resources ├── META-INF └── services │ └── org.mcphackers.mcp.plugin.MCPPlugin └── lang └── en_US.lang /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | tab_width = 4 8 | 9 | [*.gradle] 10 | indent_style = tab 11 | 12 | [*.java] 13 | indent_style = tab 14 | ij_continuation_indent_size = 8 15 | ij_java_imports_layout = $*,|,java.**,|,javax.**,|,*,|org.mcphackers.** 16 | ij_java_class_count_to_use_import_on_demand = 999 17 | 18 | [*.json] 19 | indent_style = space 20 | indent_size = 2 21 | 22 | [*.properties] 23 | indent_style = space 24 | indent_size = 2 25 | 26 | [.editorconfig] 27 | indent_style = space 28 | indent_size = 4 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | *.bat text eol=crlf 6 | 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Screenshots** 17 | If applicable, add screenshots to help explain your problem. 18 | 19 | **RetroMCP Version** 20 | v1.0 21 | 22 | **Minecraft version** 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Gradle 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Set up JDK 8 12 | uses: actions/setup-java@v4 13 | with: 14 | distribution: 'temurin' 15 | java-version: 8 16 | - name: Grant execute permission for gradlew 17 | run: chmod +x gradlew 18 | - name: Build with Gradle 19 | run: ./gradlew build 20 | - name: Upload build artifacts 21 | uses: actions/upload-artifact@v4 22 | with: 23 | name: build-artifacts 24 | path: build/libs 25 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout sources 13 | uses: actions/checkout@v4 14 | - name: Set up JDK 8 15 | uses: actions/setup-java@v4 16 | with: 17 | distribution: 'temurin' 18 | java-version: 8 19 | - name: Grant execute permission for gradlew 20 | run: chmod +x gradlew 21 | - name: Run build 22 | run: ./gradlew build 23 | env: 24 | BUILD_RELEASE: ${{ github.event.prerelease == false }} 25 | - name: Upload assets to GitHub 26 | uses: softprops/action-gh-release@v2 27 | with: 28 | files: 'build/libs/*;LICENSE' 29 | repo-token: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | 4 | out/ 5 | 6 | # Ignore Gradle build output directory 7 | build/ 8 | 9 | # Ignore everything in testing folder 10 | test/* 11 | 12 | # Ignore IDE-specific files 13 | .classpath 14 | .project 15 | .idea 16 | .vscode 17 | .settings 18 | bin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 MCPHackers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MCP-CREDITS.TXT: -------------------------------------------------------------------------------- 1 | Credits: 2 | ======== 3 | Searge 4 | * Creator of MCP 5 | * Fixes all compile errors in the decompiled sourcecode 6 | * Created the MCP Mod Framework system and API 7 | * Created the new RetroGuard deobfuscation module for MCP 3.x 8 | * Created the Exceptor tool for MCP 4.x 9 | * Created the Eclipse workspace for MCP 4.x 10 | ProfMobius 11 | * Creator of the renaming codes and re-obfuscation procedures 12 | * Helped to port scripts to Linux 13 | * Developer and maintainer of the MCP chan bot 14 | * Is now bald after working too much with java constant pool and re-obfuscation 15 | * Created the new workflow scripts and renamer for MCP 3.0 16 | IngisKahn 17 | * Creator of the bytecode compare tool that helps us to update the name mappings quickly for new minecraft versions 18 | * Contributed to the de-obfuscation spreadsheet 19 | * Working hard on creating better internal tools for mapping updates and decompiling 20 | Fesh0r 21 | * php/sql code monkey 22 | * Uses his magic to create the mappings, patches, and general release work 23 | * Has Searge's approval to make official MCP releases ;) 24 | * Makes sure we get proper patches for the sourcecode that JAD generates 25 | ZeuX 26 | * Helps out in the IRC channels - Head of HR 27 | * Did server patches for the most recent versions - if you run into any (patch-related) problems, it's his fault :P 28 | 303 29 | * Wiki contributor 30 | * Tries to help out newbies in the IRC channels 31 | * Created some scripts for mod loader support 32 | Mr_okushama 33 | * Wiki contributor 34 | * Public Support Manager 35 | * IRC Operator 36 | * Savior of the 2011 April fools prank 37 | Generic 38 | * Works on improving IngisKahn's bytecode compare tool 39 | * Added some important features to retroguard 40 | Risugami 41 | * The guy who created the first mods I (Searge) ever used in Minecraft 42 | * The creator of modloader who gave us permission to include files from his system in MCP 43 | fotoply 44 | * Helped to improve the batch files 45 | ScottyDoesKnow 46 | * obfuscathonCharmer, the obfuscathon GUI 47 | Cadde 48 | * Community manager and Wiki manager 49 | * Works on the de-obfuscation spreadsheet 50 | * Mod support (making old mods work with MCP) 51 | * All round handyman 52 | Vaprtek 53 | * Works on the de-obfuscation spreadsheet 54 | * Knows how to make pet creepers 55 | gronk 56 | * Script support 57 | n00bish 58 | * Linux script maintenance 59 | Sage Pourpre 60 | * His thread in the forums inspired me (Searge) to create this toolpack in the first place 61 | Tei 62 | * Supported the MCP project since the first version was released 63 | spec10 64 | * The new linux scripts guy 65 | Head 66 | * Wiki contributor / Administrator 67 | * Explains classes and their members on the Wiki 68 | MissLil 69 | * Various scripting stuff 70 | * Lots of reverse engineering 71 | * OpenGL constants annoting 72 | Chase 73 | * MCP Launcher Work 74 | * External jar loading 75 | * Scrollable mod list. 76 | 77 | and of course: 78 | - Everybody who contributed to the great google spreadsheet or who created some mods (I've got them all :). 79 | - NOTCH for creating a game that is just awesome, I hope he does not feel offended by our decompiling efforts. 80 | Please, Notch, support our ambitions to mod your game. I know people who bought it just because of 81 | some great mods. 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RetroMCP-Java 2 | 3 | RetroMCP is a modification of the Minecraft Coder Pack to create a "Long Term Service" patch for Minecraft. 4 | RetroMCP-Java is a complete re-design of RetroMCP in Java. 5 | 6 | # Using 7 | 8 | Using RetroMCP-Java is simple! 9 | 1. Download and install JDK 8. MCPHackers recommend [Azul Zulu](https://www.azul.com/downloads/?version=java-8-lts&package=jdk). 10 | 2. Run the latest [release](https://github.com/MCPHackers/RetroMCP-Java/releases) from the command line or via double click. If you run it via double click and RMCP errors, make sure your PATH 11 | and your JAR file associations are properly configured. 12 | > Be careful! Using "Open with" context menu on Windows will not use a proper directory, be sure to change the default .jar file associations 13 | 3. Run `setup` and choose the version you wish to decompile. 14 | 4. Run the `decompile` task 15 | 5. Mod away! Now it's Yourcraft! 16 | 17 | For more info you can check [RetroMCP Wiki](https://github.com/MCPHackers/RetroMCP-Java/wiki). 18 | 19 | # Features 20 | 21 | * Automatically download Minecraft .jar and libraries from version JSONs 22 | * An improved launch method using [LaunchWrapper](https://github.com/MCPHackers/LaunchWrapper) 23 | * Reobfuscation for all available versions 24 | * Automatic project creation 25 | * Merged codebase generation 26 | 27 | # Building 28 | 29 | 1. Use a Git client or download the sources as a zip. 30 | > `git clone git@github.com:MCPHackers/RetroMCP-Java.git` 31 | 2. Switch to the repository folder. 32 | > `cd RetroMCP-Java` 33 | 3. Invoke the build task using Gradle or the Gradle wrapper. 34 | > `gradlew build` 35 | 36 | # Contributing 37 | 38 | If you encounter any issues or bugs with RetroMCP, please create an issue and explain it in detail!
39 | If you want to contribute, please keep pull requests about one topic instead of one huge pull request.
40 | We thank everyone who contributes to this project! 41 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java" 3 | } 4 | 5 | allprojects { 6 | apply plugin: "java" 7 | 8 | repositories { 9 | mavenCentral() 10 | maven { url = "https://jitpack.io/" } 11 | maven { url = "https://maven.fabricmc.net/" } 12 | maven { url = "https://mcphackers.org/libraries/" } 13 | maven { url = "https://maven.glass-launcher.net/releases" } 14 | } 15 | 16 | dependencies { 17 | runtimeOnly sourceSets.test.output 18 | 19 | // Required libraries 20 | implementation libs.asm 21 | implementation libs.asm.analysis 22 | implementation libs.asm.commons 23 | implementation libs.asm.tree 24 | implementation libs.asm.util 25 | 26 | implementation libs.rdi.nio 27 | implementation libs.rdi 28 | 29 | implementation libs.fernflower 30 | 31 | implementation libs.json 32 | implementation libs.diffpatch 33 | implementation libs.commons.lang3 34 | implementation libs.mapping.io 35 | } 36 | 37 | tasks.withType(JavaCompile).configureEach { 38 | it.options.encoding = "UTF-8" 39 | 40 | // Use release flag on Java 9+ instead of source & target flags 41 | // This makes the JDK read the boot classpath and use the proper 42 | // libraries to be compliant with Java 8 without issues 43 | if (JavaVersion.current().isJava9Compatible()) { 44 | it.options.release.set(8) 45 | } 46 | } 47 | 48 | java { 49 | sourceCompatibility = JavaVersion.VERSION_1_8 50 | targetCompatibility = JavaVersion.VERSION_1_8 51 | } 52 | } 53 | 54 | subprojects { 55 | apply plugin: "application" 56 | } 57 | -------------------------------------------------------------------------------- /cli/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias libs.plugins.shadow 3 | } 4 | 5 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 6 | 7 | dependencies { 8 | implementation rootProject 9 | 10 | implementation libs.jansi 11 | } 12 | 13 | application { 14 | mainClass = "org.mcphackers.mcp.main.MainCLI" 15 | executableDir = "test" 16 | mkdir(executableDir) 17 | } 18 | 19 | run { 20 | workingDir 'test' 21 | } 22 | 23 | runShadow { 24 | workingDir 'test' 25 | } 26 | 27 | tasks.named("shadowJar", ShadowJar) { 28 | configurations = [project.configurations.compileClasspath] 29 | archiveBaseName.set("RetroMCP-CLI") 30 | archiveClassifier.set("all") 31 | setDestinationDirectory(rootProject.getLayout().getBuildDirectory().dir("libs")) 32 | minimize() 33 | } 34 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | asm = '9.8' 3 | commons-lang3 = '3.17.0' 4 | diffpatch = 'cde1224' 5 | fernflower = '1.0.0' 6 | flatlaf = '3.6' 7 | jansi = '2.4.1' 8 | json = '20250107' 9 | mapping-io = '0.7.1' 10 | rdi = '1.1' 11 | shadow-plugin = '8.3.1' 12 | 13 | [libraries] 14 | asm = { module = "org.ow2.asm:asm", version.ref = "asm" } 15 | asm-analysis = { module = "org.ow2.asm:asm-analysis", version.ref = "asm" } 16 | asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" } 17 | asm-tree = { module = "org.ow2.asm:asm-tree", version.ref = "asm" } 18 | asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" } 19 | commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commons-lang3" } 20 | diffpatch = { module = "com.github.MCPHackers:DiffPatch", version.ref = "diffpatch" } 21 | fernflower = { module = "io.github.lassebq:fernflower", version.ref = "fernflower" } 22 | flatlaf = { module = "com.formdev:flatlaf", version.ref = "flatlaf" } 23 | jansi = { module = "org.fusesource.jansi:jansi", version.ref = "jansi" } 24 | json = { module = "org.json:json", version.ref = "json" } 25 | mapping-io = { module = "net.fabricmc:mapping-io", version.ref = "mapping-io" } 26 | rdi = { module = "org.mcphackers.rdi:rdi", version.ref = "rdi" } 27 | rdi-nio = { module = "org.mcphackers.rdi:rdi-nio", version.ref = "rdi" } 28 | 29 | [plugins] 30 | shadow = { id = "com.gradleup.shadow", version.ref = "shadow-plugin" } 31 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCPHackers/RetroMCP-Java/fd1c4e0cb98176631ce17141fe91610dfc82240e/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.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /gui/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias libs.plugins.shadow 3 | } 4 | 5 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 6 | 7 | dependencies { 8 | implementation rootProject 9 | 10 | implementation libs.flatlaf 11 | } 12 | 13 | application { 14 | mainClass = "org.mcphackers.mcp.main.MainGUI" 15 | executableDir = "test" 16 | mkdir(executableDir) 17 | } 18 | 19 | run { 20 | workingDir "test" 21 | } 22 | 23 | runShadow { 24 | workingDir "test" 25 | } 26 | 27 | tasks.named("shadowJar", ShadowJar) { 28 | configurations = [project.configurations.compileClasspath] 29 | archiveBaseName.set("RetroMCP-GUI") 30 | archiveClassifier.set("all") 31 | setDestinationDirectory(rootProject.getLayout().getBuildDirectory().dir("libs")) 32 | minimize() // This breaks FlatLaf 33 | } 34 | -------------------------------------------------------------------------------- /gui/src/main/java/org/mcphackers/mcp/Theme.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import javax.swing.*; 9 | import javax.swing.UIManager.LookAndFeelInfo; 10 | 11 | import com.formdev.flatlaf.FlatDarculaLaf; 12 | import com.formdev.flatlaf.FlatDarkLaf; 13 | import com.formdev.flatlaf.FlatIntelliJLaf; 14 | import com.formdev.flatlaf.FlatLightLaf; 15 | 16 | public class Theme { 17 | public static final Map THEMES_MAP = new HashMap<>(); 18 | public static final List THEMES = new ArrayList<>(); 19 | 20 | static { 21 | for (LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { 22 | addTheme(laf.getName(), laf.getClassName()); 23 | } 24 | addTheme(FlatLightLaf.NAME, FlatLightLaf.class.getName()); 25 | addTheme(FlatDarkLaf.NAME, FlatDarkLaf.class.getName()); 26 | addTheme(FlatIntelliJLaf.NAME, FlatIntelliJLaf.class.getName()); 27 | addTheme(FlatDarculaLaf.NAME, FlatDarculaLaf.class.getName()); 28 | } 29 | 30 | public final String themeName; 31 | public final String themeClass; 32 | 33 | Theme(String themeName, String themeClassName) { 34 | this.themeName = themeName; 35 | this.themeClass = themeClassName; 36 | } 37 | 38 | public static void addTheme(String name, String className) { 39 | Theme theme = new Theme(name, className); 40 | THEMES_MAP.put(className, theme); 41 | THEMES.add(theme); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /gui/src/main/java/org/mcphackers/mcp/gui/GridBagConstraintsBuilder.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.gui; 2 | 3 | import java.awt.*; 4 | 5 | public final class GridBagConstraintsBuilder { 6 | 7 | private final GridBagConstraints inner; 8 | 9 | public GridBagConstraintsBuilder(GridBagConstraints inner) { 10 | this.inner = inner; 11 | } 12 | 13 | public GridBagConstraintsBuilder pos(int x, int y) { 14 | GridBagConstraintsBuilder copy = this.copy(); 15 | copy.inner.gridx = x; 16 | copy.inner.gridy = y; 17 | return copy; 18 | } 19 | 20 | public GridBagConstraintsBuilder size(int width, int height) { 21 | GridBagConstraintsBuilder copy = this.copy(); 22 | copy.inner.gridwidth = width; 23 | copy.inner.gridheight = height; 24 | return copy; 25 | } 26 | 27 | public GridBagConstraintsBuilder width(int width) { 28 | GridBagConstraintsBuilder copy = this.copy(); 29 | copy.inner.gridwidth = width; 30 | return copy; 31 | } 32 | 33 | public GridBagConstraintsBuilder height(int height) { 34 | GridBagConstraintsBuilder copy = this.copy(); 35 | copy.inner.gridheight = height; 36 | return copy; 37 | } 38 | 39 | public GridBagConstraintsBuilder dimensions(int x, int y, int width, int height) { 40 | return this.pos(x, y).size(width, height); 41 | } 42 | 43 | public GridBagConstraintsBuilder weight(double x, double y) { 44 | GridBagConstraintsBuilder copy = this.copy(); 45 | copy.inner.weightx = x; 46 | copy.inner.weighty = y; 47 | return copy; 48 | } 49 | 50 | public GridBagConstraintsBuilder weightX(double x) { 51 | GridBagConstraintsBuilder copy = this.copy(); 52 | copy.inner.weightx = x; 53 | return copy; 54 | } 55 | 56 | public GridBagConstraintsBuilder weightY(double y) { 57 | GridBagConstraintsBuilder copy = this.copy(); 58 | copy.inner.weighty = y; 59 | return copy; 60 | } 61 | 62 | public GridBagConstraintsBuilder anchor(int anchor) { 63 | GridBagConstraintsBuilder copy = this.copy(); 64 | copy.inner.anchor = anchor; 65 | return copy; 66 | } 67 | 68 | public GridBagConstraintsBuilder fill(int fill) { 69 | GridBagConstraintsBuilder copy = this.copy(); 70 | copy.inner.fill = fill; 71 | return copy; 72 | } 73 | 74 | public GridBagConstraintsBuilder insetsUnscaled(int all) { 75 | return this.insetsUnscaled(all, all, all, all); 76 | } 77 | 78 | public GridBagConstraintsBuilder insetsUnscaled(int vertical, int horizontal) { 79 | return this.insetsUnscaled(vertical, horizontal, vertical, horizontal); 80 | } 81 | 82 | public GridBagConstraintsBuilder insetsUnscaled(int top, int horizontal, int bottom) { 83 | return this.insetsUnscaled(top, horizontal, bottom, horizontal); 84 | } 85 | 86 | public GridBagConstraintsBuilder insetsUnscaled(int top, int right, int bottom, int left) { 87 | GridBagConstraintsBuilder copy = this.copy(); 88 | copy.inner.insets.set(top, left, bottom, right); 89 | return copy; 90 | } 91 | 92 | public GridBagConstraintsBuilder paddingUnscaled(int pad) { 93 | return this.paddingUnscaled(pad, pad); 94 | } 95 | 96 | public GridBagConstraintsBuilder paddingUnscaled(int padX, int padY) { 97 | GridBagConstraintsBuilder copy = this.copy(); 98 | copy.inner.ipadx = padX; 99 | copy.inner.ipady = padY; 100 | return copy; 101 | } 102 | 103 | public GridBagConstraintsBuilder copy() { 104 | GridBagConstraints c = (GridBagConstraints) this.inner.clone(); 105 | return new GridBagConstraintsBuilder(c); 106 | } 107 | 108 | public GridBagConstraints build() { 109 | return (GridBagConstraints) this.inner.clone(); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /gui/src/main/java/org/mcphackers/mcp/gui/SideProgressBar.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.gui; 2 | 3 | import javax.swing.*; 4 | 5 | public class SideProgressBar extends JProgressBar { 6 | 7 | private static final long serialVersionUID = -8002821179520037516L; 8 | 9 | public String progressMsg; 10 | public int progress; 11 | 12 | public SideProgressBar() { 13 | setStringPainted(true); 14 | } 15 | 16 | public void updateProgress() { 17 | setValue(progress); 18 | setString(progress + "% " + progressMsg); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /gui/src/main/java/org/mcphackers/mcp/gui/TaskButton.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.gui; 2 | 3 | import static org.mcphackers.mcp.tools.Util.enqueueRunnable; 4 | 5 | import java.awt.event.ActionListener; 6 | 7 | import javax.swing.*; 8 | 9 | import org.mcphackers.mcp.MCP; 10 | import org.mcphackers.mcp.main.MainGUI; 11 | import org.mcphackers.mcp.tasks.mode.TaskMode; 12 | 13 | public class TaskButton extends JButton { 14 | 15 | private static final long serialVersionUID = -2625827711322112358L; 16 | 17 | private final TaskMode linkedTask; 18 | private final MainGUI mcp; 19 | 20 | public TaskButton(MainGUI owner, TaskMode task) { 21 | super(task.getFullName()); 22 | linkedTask = task; 23 | mcp = owner; 24 | addActionListener(performTask(mcp, linkedTask)); 25 | } 26 | 27 | public TaskButton(MainGUI owner, TaskMode task, ActionListener defaultActionListener) { 28 | super(task.getFullName()); 29 | linkedTask = task; 30 | mcp = owner; 31 | addActionListener(defaultActionListener); 32 | } 33 | 34 | public static ActionListener performTask(MCP mcp, TaskMode mode) { 35 | return event -> enqueueRunnable(() -> mcp.performTask(mode, mcp.getOptions().side)); 36 | } 37 | 38 | public boolean getEnabled() { 39 | return linkedTask.isAvailable(mcp, mcp.getSide()); 40 | } 41 | 42 | public void updateName() { 43 | setText(linkedTask.getFullName()); 44 | setToolTipText(linkedTask.getDesc()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /gui/src/main/java/org/mcphackers/mcp/gui/TextAreaContextMenu.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.gui; 2 | 3 | import org.mcphackers.mcp.MCP; 4 | import org.mcphackers.mcp.main.MainGUI; 5 | 6 | import javax.swing.*; 7 | import javax.swing.text.Style; 8 | import javax.swing.text.StyleContext; 9 | import javax.swing.text.StyledDocument; 10 | 11 | public class TextAreaContextMenu extends JPopupMenu { 12 | private final MCP mcp; 13 | 14 | public TextAreaContextMenu(MCP mcp) { 15 | this.mcp = mcp; 16 | this.addItems(); 17 | } 18 | 19 | private void addItems() { 20 | JMenuItem clearText = new JMenuItem(MCP.TRANSLATOR.translateKey("mcp.clearConsole")); 21 | clearText.addActionListener(actionEvent -> { 22 | if (this.mcp instanceof MainGUI) { 23 | MainGUI mainGUI = (MainGUI) this.mcp; 24 | mainGUI.textPane.setText(""); 25 | 26 | // Clear the styles 27 | StyledDocument doc = mainGUI.textPane.getStyledDocument(); 28 | Style defaultStyle = mainGUI.textPane.getStyle(StyleContext.DEFAULT_STYLE); 29 | doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, true); 30 | } 31 | }); 32 | this.add(clearText); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gui/src/main/java/org/mcphackers/mcp/gui/TextAreaOutputStream.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.gui; 2 | 3 | import java.io.OutputStream; 4 | import java.io.PrintStream; 5 | 6 | import javax.swing.*; 7 | import javax.swing.text.BadLocationException; 8 | 9 | public class TextAreaOutputStream extends PrintStream { 10 | private final JTextPane textPane; 11 | 12 | public TextAreaOutputStream(JTextPane textArea, OutputStream out) { 13 | super(out, true); 14 | this.textPane = textArea; 15 | } 16 | 17 | @Override 18 | public void print(Object o) { 19 | printString(String.valueOf(o)); 20 | super.print(o); 21 | } 22 | 23 | @Override 24 | public void println(Object o) { 25 | super.println(o); 26 | printString("\n"); 27 | } 28 | 29 | @Override 30 | public void print(String s) { 31 | printString(s); 32 | super.print(s); 33 | } 34 | 35 | @Override 36 | public void println(String s) { 37 | super.println(s); 38 | printString("\n"); 39 | } 40 | 41 | private void printString(String msg) { 42 | try { 43 | textPane.getStyledDocument().insertString(textPane.getStyledDocument().getLength(), msg, null); 44 | } catch (BadLocationException ex) { 45 | ex.printStackTrace(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /gui/src/main/java/org/mcphackers/mcp/gui/WrapLayout.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.gui; 2 | 3 | import java.awt.*; 4 | 5 | import javax.swing.*; 6 | 7 | /** 8 | * FlowLayout subclass that fully supports wrapping of components. 9 | */ 10 | public class WrapLayout extends FlowLayout { 11 | 12 | private static final long serialVersionUID = 1080066265713791251L; 13 | 14 | public WrapLayout() { 15 | } 16 | 17 | public WrapLayout(int align) { 18 | super(align); 19 | } 20 | 21 | public WrapLayout(int align, int hgap, int vgap) { 22 | super(align, hgap, vgap); 23 | } 24 | 25 | /** 26 | * Returns the preferred dimensions for this layout given the 27 | * visible components in the specified target container. 28 | * 29 | * @param target the component which needs to be laid out 30 | * @return the preferred dimensions to lay out the 31 | * subcomponents of the specified container 32 | */ 33 | @Override 34 | public Dimension preferredLayoutSize(Container target) { 35 | return layoutSize(target, true); 36 | } 37 | 38 | /** 39 | * Returns the minimum dimensions needed to layout the visible 40 | * components contained in the specified target container. 41 | * 42 | * @param target the component which needs to be laid out 43 | * @return the minimum dimensions to lay out the 44 | * subcomponents of the specified container 45 | */ 46 | @Override 47 | public Dimension minimumLayoutSize(Container target) { 48 | Dimension minimum = layoutSize(target, false); 49 | minimum.width -= (getHgap() + 1); 50 | return minimum; 51 | } 52 | 53 | /** 54 | * Returns the minimum or preferred dimension needed to layout the target 55 | * container. 56 | * 57 | * @param target target to get layout size for 58 | * @param preferred should preferred size be calculated 59 | * @return the dimension to layout the target container 60 | */ 61 | private Dimension layoutSize(Container target, boolean preferred) { 62 | synchronized (target.getTreeLock()) { 63 | // Each row must fit with the width allocated to the containter. 64 | // When the container width = 0, the preferred width of the container 65 | // has not yet been calculated so lets ask for the maximum. 66 | 67 | Container container = target; 68 | 69 | while (container.getSize().width == 0 && container.getParent() != null) { 70 | container = container.getParent(); 71 | } 72 | 73 | int targetWidth = container.getSize().width; 74 | 75 | if (targetWidth == 0) 76 | targetWidth = Integer.MAX_VALUE; 77 | 78 | int hgap = getHgap(); 79 | int vgap = getVgap(); 80 | Insets insets = target.getInsets(); 81 | int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); 82 | int maxWidth = targetWidth - horizontalInsetsAndGap; 83 | 84 | // Fit components into the allowed width 85 | 86 | Dimension dim = new Dimension(0, 0); 87 | int rowWidth = 0; 88 | int rowHeight = 0; 89 | 90 | int nmembers = target.getComponentCount(); 91 | 92 | for (int i = 0; i < nmembers; i++) { 93 | Component m = target.getComponent(i); 94 | 95 | if (m.isVisible()) { 96 | Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); 97 | 98 | // Can't add the component to current row. Start a new row. 99 | 100 | if (rowWidth + d.width > maxWidth) { 101 | addRow(dim, rowWidth, rowHeight); 102 | rowWidth = 0; 103 | rowHeight = 0; 104 | } 105 | 106 | // Add a horizontal gap for all components after the first 107 | 108 | if (rowWidth != 0) { 109 | rowWidth += hgap; 110 | } 111 | 112 | rowWidth += d.width; 113 | rowHeight = Math.max(rowHeight, d.height); 114 | } 115 | } 116 | 117 | addRow(dim, rowWidth, rowHeight); 118 | 119 | dim.width += horizontalInsetsAndGap; 120 | dim.height += insets.top + insets.bottom + vgap * 2; 121 | 122 | // When using a scroll pane or the DecoratedLookAndFeel we need to 123 | // make sure the preferred size is less than the size of the 124 | // target container so shrinking the container size works 125 | // correctly. Removing the horizontal gap is an easy way to do this. 126 | 127 | Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); 128 | 129 | if (scrollPane != null && target.isValid()) { 130 | dim.width -= (hgap + 1); 131 | } 132 | 133 | return dim; 134 | } 135 | } 136 | 137 | /* 138 | * A new row has been completed. Use the dimensions of this row 139 | * to update the preferred size for the container. 140 | * 141 | * @param dim update the width and height when appropriate 142 | * @param rowWidth the width of the row to add 143 | * @param rowHeight the height of the row to add 144 | */ 145 | private void addRow(Dimension dim, int rowWidth, int rowHeight) { 146 | dim.width = Math.max(dim.width, rowWidth); 147 | 148 | if (dim.height > 0) { 149 | dim.height += getVgap(); 150 | } 151 | 152 | dim.height += rowHeight; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'RetroMCP-Java' 2 | 3 | include "gui" 4 | include "cli" 5 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/DownloadListener.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp; 2 | 3 | import org.mcphackers.mcp.tools.versions.IDownload; 4 | 5 | public interface DownloadListener { 6 | void notify(IDownload object, long totalSize); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/Language.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp; 2 | 3 | import java.util.Locale; 4 | 5 | /** 6 | * All available languages 7 | */ 8 | public enum Language { 9 | 10 | ENGLISH(Locale.US), 11 | SPANISH(new Locale("es", "ES")), 12 | RUSSIAN(new Locale("ru", "RU")), 13 | GERMAN(new Locale("de", "DE")), 14 | FRENCH(new Locale("fr", "FR")), 15 | CHINESE(new Locale("zh", "CN")), 16 | CZECH(new Locale("cs", "CZ")), 17 | NORSK_BOKMAL(new Locale("nb", "NO")); 18 | 19 | /** 20 | * Internal locale 21 | */ 22 | public final Locale locale; 23 | 24 | Language(Locale locale) { 25 | this.locale = locale; 26 | } 27 | 28 | /** 29 | * @param locale 30 | * @return A language enum from a locale 31 | */ 32 | public static Language get(Locale locale) { 33 | for (Language lang : Language.values()) { 34 | // Perfect match 35 | if (lang.locale.equals(locale)) { 36 | return lang; 37 | } 38 | 39 | // Language match 40 | if (lang.locale.getLanguage().equals(locale.getLanguage())) { 41 | return lang; 42 | } 43 | } 44 | 45 | // No matches found for locale 46 | return ENGLISH; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/MCPPaths.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp; 2 | 3 | import java.nio.file.Path; 4 | 5 | import org.mcphackers.mcp.tasks.Task.Side; 6 | 7 | public class MCPPaths { 8 | 9 | //Directories 10 | public static final String JARS = "jars/"; 11 | public static final String LIB = "libraries/"; 12 | public static final String CONF = "conf/"; 13 | public static final String BUILD = "build/"; 14 | public static final String PROJECT = "minecraft_%s/"; 15 | 16 | //Files and subdirectories 17 | public static final String JAR_ORIGINAL = JARS + "minecraft_%s.jar"; 18 | 19 | public static final String NATIVES = LIB + "natives"; 20 | 21 | public static final String BUILD_ZIP = BUILD + "minecraft_%s.zip"; 22 | public static final String BUILD_JAR = BUILD + "minecraft_%s.jar"; 23 | 24 | public static final String SOURCE_UNPATCHED = PROJECT + "src_original"; 25 | public static final String SOURCE = PROJECT + "src"; 26 | public static final String BIN = PROJECT + "bin"; 27 | public static final String MD5_DIR = PROJECT + "md5"; 28 | public static final String MD5 = PROJECT + "md5/original.md5"; 29 | public static final String MD5_RO = PROJECT + "md5/modified.md5"; 30 | public static final String JARS_DIR = PROJECT + "jars"; 31 | public static final String REMAPPED = PROJECT + "jars/deobfuscated.jar"; 32 | public static final String REOBF_JAR = PROJECT + "jars/reobfuscated.jar"; 33 | public static final String SOURCE_JAR = PROJECT + "jars/deobfuscated-source.jar"; 34 | public static final String REOBF_SIDE = PROJECT + "reobf"; 35 | public static final String GAMEDIR = PROJECT + "game/"; 36 | 37 | public static final String MAPPINGS = CONF + "mappings.tiny"; 38 | public static final String EXC = CONF + "exceptions.exc"; 39 | public static final String ACCESS = CONF + "%s.access"; 40 | public static final String PATCHES = CONF + "%s.patch"; 41 | public static final String VERSION = CONF + "version.json"; 42 | 43 | public static final String PATCH = "patches/%s.patch"; 44 | 45 | public static final String UPDATE_JAR = "update.jar"; 46 | 47 | public static Path get(MCP mcp, String path, Side side) { 48 | String newPath = path; 49 | if (side == Side.CLIENT) { 50 | newPath = path.replace("minecraft_%s", "minecraft"); 51 | } 52 | return get(mcp, String.format(newPath, side.name)); 53 | } 54 | 55 | public static Path get(MCP mcp, String path) { 56 | return mcp.getWorkingDir().resolve(path); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/TranslatorUtil.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.net.URL; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.Enumeration; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public class TranslatorUtil { 14 | public static final Language DEFAULT_LANG = Language.ENGLISH; 15 | private final Map translations = new HashMap<>(); 16 | public Language currentLang; 17 | 18 | public void changeLang(Language lang) { 19 | translations.clear(); 20 | currentLang = lang; 21 | readTranslation(MCP.class); 22 | MCP.reloadPluginTranslations(); 23 | } 24 | 25 | public void readTranslation(Class cls) { 26 | readTranslation(cls, DEFAULT_LANG); 27 | if (currentLang != DEFAULT_LANG) { 28 | readTranslation(cls, currentLang); 29 | } 30 | } 31 | 32 | private void readTranslation(Class cls, Language lang) { 33 | readTranslation(translations, cls, lang); 34 | } 35 | 36 | private void readTranslation(Map map, Class cls, Language lang) { 37 | String resourceName = "lang/" + lang.locale.toString() + ".lang"; 38 | try { 39 | Enumeration translations = cls.getClassLoader().getResources(resourceName); 40 | while (translations.hasMoreElements()) { 41 | URL url = translations.nextElement(); 42 | this.readTranslation(map, url.openStream()); 43 | } 44 | } catch (IOException ex) { 45 | ex.printStackTrace(); 46 | } 47 | } 48 | 49 | private void readTranslation(Map map, InputStream resource) { 50 | if (resource == null) { 51 | return; 52 | } 53 | try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource, StandardCharsets.UTF_8))) { 54 | bufferedReader.lines().forEach(line -> { 55 | if (line.startsWith("#") || line.trim().isEmpty()) return; 56 | String key = line.split("=")[0].trim(); 57 | String translated = line.split("=")[1].trim(); 58 | map.put(key, translated); 59 | }); 60 | } catch (IOException ex) { 61 | ex.printStackTrace(); 62 | } 63 | } 64 | 65 | public String getLangName(Language lang) { 66 | Map entries = new HashMap<>(); 67 | readTranslation(entries, MCP.class, lang); 68 | String languageName = entries.get("language"); 69 | if (languageName != null) { 70 | return languageName; 71 | } 72 | return "Unknown language"; 73 | } 74 | 75 | public boolean hasKey(String key) { 76 | return translations.containsKey(key); 77 | } 78 | 79 | public String translateKey(String key) { 80 | String translatedString = this.translations.get(key); 81 | if (translatedString == null) { 82 | return key; 83 | } 84 | return translatedString; 85 | } 86 | 87 | public String translateKeyWithFormatting(String key, Object... formatting) { 88 | String translatedString = this.translations.get(key); 89 | if (translatedString == null) { 90 | return key; 91 | } 92 | return String.format(translatedString, formatting); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/Update.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Paths; 6 | 7 | import org.mcphackers.mcp.tools.Util; 8 | 9 | public class Update { 10 | 11 | public static void main(String[] args) { 12 | if (args.length >= 1) { 13 | boolean keepTrying = true; 14 | long startTime = System.currentTimeMillis(); 15 | while (keepTrying) { 16 | try { 17 | Files.deleteIfExists(Paths.get(args[0])); 18 | Files.copy(Paths.get(MCPPaths.UPDATE_JAR), Paths.get(args[0])); 19 | Util.runCommand(new String[]{ 20 | Util.getJava(), 21 | "-jar", 22 | args[0] 23 | }); 24 | keepTrying = false; 25 | } catch (IOException e) { 26 | keepTrying = System.currentTimeMillis() - startTime < 10000; 27 | } 28 | } 29 | } 30 | } 31 | 32 | public static void attemptToDeleteUpdateJar() { 33 | long startTime = System.currentTimeMillis(); 34 | boolean keepTrying = true; 35 | while (keepTrying) { 36 | try { 37 | Files.deleteIfExists(Paths.get(MCPPaths.UPDATE_JAR)); 38 | keepTrying = false; 39 | } catch (IOException e) { 40 | keepTrying = System.currentTimeMillis() - startTime < 10000; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/plugin/MCPPlugin.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.plugin; 2 | 3 | import org.mcphackers.mcp.MCP; 4 | import org.mcphackers.mcp.tasks.Task; 5 | import org.mcphackers.mcp.tasks.TaskStaged; 6 | 7 | public interface MCPPlugin { 8 | 9 | /** 10 | * @return A unique string id 11 | */ 12 | String pluginId(); 13 | 14 | /** 15 | * Called when the plugin is loaded 16 | */ 17 | void init(MCP mcp); 18 | 19 | /** 20 | * Called whenever a certain task event happens inside of task instance 21 | */ 22 | void onTaskEvent(TaskEvent event, Task task); 23 | 24 | /** 25 | * Called whenever a certain task event happens inside of task instance 26 | */ 27 | void onMCPEvent(MCPEvent event, MCP mcp); 28 | 29 | /** 30 | * Called whenever an instance of TaskStaged starts execution. 31 | * Use {@link TaskStaged#overrideStage(int, org.mcphackers.mcp.tasks.TaskRunnable)} 32 | * to replace one of the stages. 33 | * 34 | * @param task the task with stages to override 35 | */ 36 | void setTaskOverrides(TaskStaged task); 37 | 38 | enum TaskEvent { 39 | /** 40 | * Called before a task is executed 41 | */ 42 | PRE_TASK, 43 | /** 44 | * Called after the task has executed 45 | */ 46 | POST_TASK, 47 | /** 48 | * Called each time a task moves to the next execution stage 49 | */ 50 | TASK_STEP 51 | } 52 | 53 | enum MCPEvent { 54 | /** 55 | * Called when a task begins execution 56 | */ 57 | STARTED_TASKS, 58 | /** 59 | * Called when all tasks have finished execution 60 | */ 61 | FINISHED_TASKS, 62 | /** 63 | * Called when RMCP starts up 64 | */ 65 | ENV_STARTUP 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/plugin/PluginClassLoader.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.plugin; 2 | 3 | import java.net.URL; 4 | import java.net.URLClassLoader; 5 | 6 | public class PluginClassLoader extends URLClassLoader { 7 | public PluginClassLoader(ClassLoader parent) { 8 | super(new URL[] {}, parent); 9 | } 10 | 11 | public void addURL(URL url) { 12 | super.addURL(url); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/plugin/PluginManager.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.plugin; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileSystem; 5 | import java.nio.file.FileSystems; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.ServiceLoader; 13 | 14 | import org.mcphackers.mcp.MCP; 15 | import org.mcphackers.mcp.tools.FileUtil; 16 | 17 | public class PluginManager { 18 | private final Map loadedPlugins = new HashMap<>(); 19 | 20 | public Map getLoadedPlugins() { 21 | return this.loadedPlugins; 22 | } 23 | 24 | public void discoverPlugins(MCP mcp) { 25 | ClassLoader classLoader = mcp.getClass().getClassLoader(); 26 | try (PluginClassLoader pluginClassLoader = new PluginClassLoader(classLoader)) { 27 | Path workingDir = mcp.getWorkingDir(); 28 | if (workingDir == null) { 29 | workingDir = Paths.get("."); 30 | } 31 | Path pluginsDir = workingDir.resolve("plugins"); 32 | if (Files.exists(pluginsDir) && Files.isDirectory(pluginsDir)) { 33 | List pluginCandidates = FileUtil.getPathsOfType(pluginsDir, ".jar", ".zip"); 34 | for (Path pluginCandidate : pluginCandidates) { 35 | try (FileSystem fs = FileSystems.newFileSystem(pluginCandidate, pluginClassLoader)) { 36 | Path serviceConfigPath = fs.getPath("META-INF", "services", MCPPlugin.class.getName()); 37 | if (!Files.exists(serviceConfigPath)) { 38 | System.out.println("Plugin candidate (" + pluginCandidate.getFileName() + ") does not contain a valid service configuration. Skipping!"); 39 | } else { 40 | System.out.println("Adding plugin candidate (" + pluginCandidate.getFileName() + ") to classpath!"); 41 | pluginClassLoader.addURL(pluginCandidate.toUri().toURL()); 42 | } 43 | } catch (IOException ex) { 44 | ex.printStackTrace(); 45 | } 46 | } 47 | } 48 | 49 | ServiceLoader serviceLoader = ServiceLoader.load(MCPPlugin.class, pluginClassLoader); 50 | for (MCPPlugin plugin : serviceLoader) { 51 | plugin.init(mcp); 52 | this.loadedPlugins.put(plugin.pluginId(), plugin); 53 | 54 | // Load translations from plugins 55 | MCP.TRANSLATOR.readTranslation(plugin.getClass()); 56 | } 57 | } catch (IOException ex) { 58 | ex.printStackTrace(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/ProgressListener.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | public interface ProgressListener { 4 | 5 | void setProgress(String progressMessage); 6 | 7 | void setProgress(int progress); 8 | 9 | default void setProgress(String progressMessage, int progress) { 10 | setProgress(progressMessage); 11 | setProgress(progress); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/Task.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.mcphackers.mcp.MCP; 9 | import org.mcphackers.mcp.plugin.MCPPlugin.TaskEvent; 10 | 11 | public abstract class Task implements ProgressListener, TaskRunnable { 12 | 13 | public static final Map sides = new HashMap<>(); 14 | public static final byte INFO = 0; 15 | public static final byte WARNING = 1; 16 | public static final byte ERROR = 2; 17 | public final Side side; 18 | protected final MCP mcp; 19 | private final List logMessages = new ArrayList<>(); 20 | private byte result = INFO; 21 | private ProgressListener progressListener; 22 | private int progressBarIndex = -1; 23 | 24 | public Task(Side side, MCP instance, ProgressListener listener) { 25 | this(side, instance); 26 | this.progressListener = listener; 27 | } 28 | 29 | public Task(Side side, MCP instance) { 30 | this.side = side; 31 | this.mcp = instance; 32 | } 33 | 34 | public Task(MCP instance) { 35 | this(Side.ANY, instance); 36 | } 37 | 38 | public final void performTask() throws Exception { 39 | triggerEvent(TaskEvent.PRE_TASK); 40 | doTask(); 41 | triggerEvent(TaskEvent.POST_TASK); 42 | } 43 | 44 | protected final void triggerEvent(TaskEvent event) { 45 | mcp.triggerTaskEvent(event, this); 46 | } 47 | 48 | protected final void addMessage(String msg, byte logLevel) { 49 | if (progressListener != null) { 50 | if (progressListener instanceof Task) { 51 | Task task = (Task) progressListener; 52 | task.addMessage(msg, logLevel); 53 | } 54 | } 55 | logMessages.add(msg); 56 | result = logLevel < result ? result : logLevel; 57 | } 58 | 59 | public final byte getResult() { 60 | return result; 61 | } 62 | 63 | public final List getMessageList() { 64 | return logMessages; 65 | } 66 | 67 | @Override 68 | public void setProgress(String progressString) { 69 | if (progressListener != null) { 70 | progressListener.setProgress(progressString); 71 | } else if (progressBarIndex >= 0) { 72 | mcp.setProgress(progressBarIndex, progressString); 73 | } 74 | } 75 | 76 | @Override 77 | public void setProgress(int progress) { 78 | if (progressListener != null) { 79 | progressListener.setProgress(progress); 80 | } else if (progressBarIndex >= 0) { 81 | mcp.setProgress(progressBarIndex, progress); 82 | } 83 | } 84 | 85 | public void log(String msg) { 86 | mcp.log(msg); 87 | } 88 | 89 | public void setProgressBarIndex(int i) { 90 | progressBarIndex = i; 91 | } 92 | 93 | public final String getLocalizedStage(String stage, Object... formatting) { 94 | return MCP.TRANSLATOR.translateKeyWithFormatting("task.stage." + stage, formatting); 95 | } 96 | 97 | public enum Side { 98 | ANY(-1, "any"), 99 | CLIENT(0, "client"), 100 | SERVER(1, "server"), 101 | MERGED(2, "merged"); 102 | 103 | public static final Side[] ALL = {CLIENT, SERVER, MERGED}; 104 | public static final Side[] VALUES = Side.values(); 105 | 106 | public final int index; 107 | public final String name; 108 | 109 | Side(int index, String name) { 110 | this.index = index; 111 | this.name = name; 112 | sides.put(index, this); 113 | } 114 | 115 | public String getName() { 116 | return MCP.TRANSLATOR.translateKey("side." + name); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskApplyPatch.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.PATCH; 4 | import static org.mcphackers.mcp.MCPPaths.SOURCE; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.io.PrintStream; 9 | import java.nio.file.Path; 10 | 11 | import codechicken.diffpatch.PatchOperation; 12 | import codechicken.diffpatch.util.PatchMode; 13 | import org.mcphackers.mcp.MCP; 14 | import org.mcphackers.mcp.MCPPaths; 15 | 16 | public class TaskApplyPatch extends TaskStaged { 17 | 18 | public TaskApplyPatch(Side side, MCP instance) { 19 | super(side, instance); 20 | } 21 | 22 | @Override 23 | protected Stage[] setStages() { 24 | return new Stage[] { 25 | stage(getLocalizedStage("patching"), () -> { 26 | final Path patchesPath = MCPPaths.get(mcp, PATCH, side); 27 | final Path srcPath = MCPPaths.get(mcp, SOURCE, side); 28 | patch(this, srcPath, srcPath, patchesPath); 29 | }) 30 | }; 31 | } 32 | 33 | public static void patch(Task task, Path base, Path out, Path patches) throws IOException { 34 | ByteArrayOutputStream logger = new ByteArrayOutputStream(); 35 | PatchOperation patchOperation = PatchOperation.builder() 36 | .basePath(base) 37 | .patchesPath(patches) 38 | .outputPath(out) 39 | .mode(PatchMode.OFFSET) 40 | .build(); 41 | boolean success = patchOperation.doPatch(); 42 | patchOperation.getSummary().print(new PrintStream(logger), false); 43 | if (!success) { 44 | task.addMessage(logger.toString(), Task.INFO); 45 | task.addMessage("Patching failed!", Task.ERROR); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskBuild.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.*; 4 | 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.util.List; 8 | 9 | import org.mcphackers.mcp.MCP; 10 | import org.mcphackers.mcp.MCPPaths; 11 | import org.mcphackers.mcp.tasks.mode.TaskParameter; 12 | import org.mcphackers.mcp.tools.FileUtil; 13 | 14 | public class TaskBuild extends TaskStaged { 15 | /* 16 | * Indexes of stages for plugin overrides 17 | */ 18 | public static final int STAGE_RECOMPILE = 0; 19 | public static final int STAGE_REOBF = 1; 20 | public static final int STAGE_BUILD = 2; 21 | 22 | public TaskBuild(Side side, MCP instance) { 23 | super(side, instance); 24 | } 25 | 26 | @Override 27 | protected Stage[] setStages() { 28 | Path bin = MCPPaths.get(mcp, BIN, side); 29 | return new Stage[]{ 30 | stage(getLocalizedStage("recompile"), 31 | () -> new TaskRecompile(side, mcp, this).doTask()), 32 | stage(getLocalizedStage("reobf"), 50, 33 | () -> new TaskReobfuscate(side, mcp, this).doTask()), 34 | stage(getLocalizedStage("build"), 70, 35 | () -> { 36 | Side[] sides = side == Side.MERGED ? new Side[]{Side.CLIENT, Side.SERVER} : new Side[]{side}; 37 | for (Side localSide : sides) { 38 | Path originalJar = MCPPaths.get(mcp, JAR_ORIGINAL, localSide); 39 | Path reobfDir = MCPPaths.get(mcp, REOBF_SIDE, localSide); 40 | Path buildJar = MCPPaths.get(mcp, BUILD_JAR, localSide); 41 | Path buildZip = MCPPaths.get(mcp, BUILD_ZIP, localSide); 42 | FileUtil.createDirectories(MCPPaths.get(mcp, BUILD)); 43 | if (mcp.getOptions().getBooleanParameter(TaskParameter.FULL_BUILD)) { 44 | Files.deleteIfExists(buildJar); 45 | Files.copy(originalJar, buildJar); 46 | List reobfClasses = FileUtil.walkDirectory(reobfDir, path -> !Files.isDirectory(path)); 47 | FileUtil.packFilesToZip(buildJar, reobfClasses, reobfDir); 48 | List assets = FileUtil.walkDirectory(bin, path -> !Files.isDirectory(path) && !path.getFileName().toString().endsWith(".class")); 49 | FileUtil.packFilesToZip(buildJar, assets, bin); 50 | FileUtil.deleteFileInAZip(buildJar, "/META-INF/MOJANG_C.DSA"); 51 | FileUtil.deleteFileInAZip(buildJar, "/META-INF/MOJANG_C.SF"); 52 | FileUtil.deleteFileInAZip(buildJar, "/META-INF/CODESIGN.DSA"); 53 | FileUtil.deleteFileInAZip(buildJar, "/META-INF/CODESIGN.SF"); 54 | } else { 55 | Files.deleteIfExists(buildZip); 56 | FileUtil.compress(reobfDir, buildZip); 57 | List assets = FileUtil.walkDirectory(bin, path -> !Files.isDirectory(path) && !path.getFileName().toString().endsWith(".class")); 58 | FileUtil.packFilesToZip(buildZip, assets, bin); 59 | } 60 | } 61 | }) 62 | }; 63 | } 64 | 65 | @Override 66 | public void setProgress(int progress) { 67 | switch (step) { 68 | case STAGE_RECOMPILE: { 69 | int percent = (int) (progress * 0.49D); 70 | super.setProgress(1 + percent); 71 | break; 72 | } 73 | case STAGE_REOBF: { 74 | int percent = (int) (progress * 0.20D); 75 | super.setProgress(50 + percent); 76 | break; 77 | } 78 | default: 79 | super.setProgress(progress); 80 | break; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskCleanup.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.*; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.text.DecimalFormat; 9 | import java.text.DecimalFormatSymbols; 10 | import java.time.Duration; 11 | import java.time.Instant; 12 | import java.time.temporal.ChronoUnit; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Locale; 16 | 17 | import org.mcphackers.mcp.MCP; 18 | import org.mcphackers.mcp.MCPPaths; 19 | import org.mcphackers.mcp.tools.FileUtil; 20 | 21 | public class TaskCleanup extends TaskStaged { 22 | 23 | private static final DecimalFormat DECIMAL = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); 24 | 25 | public TaskCleanup(MCP instance) { 26 | super(Side.ANY, instance); 27 | } 28 | 29 | @Override 30 | protected Stage[] setStages() { 31 | return new Stage[] { 32 | stage(getLocalizedStage("cleaning"), () -> { 33 | Instant startTime = Instant.now(); 34 | 35 | boolean deleted = cleanup(); 36 | 37 | mcp.setCurrentVersion(null); 38 | 39 | if (deleted) { 40 | log("Cleanup finished in " + DECIMAL.format(Duration.between(startTime, Instant.now()).get(ChronoUnit.NANOS) / 1e+9F) + "s"); 41 | } else { 42 | log("Nothing to clear!"); 43 | } 44 | }) 45 | }; 46 | } 47 | 48 | public boolean cleanup() throws IOException { 49 | boolean deleted = false; 50 | List filesToDelete = new ArrayList<>(); 51 | for (Side side : Side.ALL) { 52 | filesToDelete.add(MCPPaths.get(mcp, JAR_ORIGINAL, side)); 53 | filesToDelete.add(MCPPaths.get(mcp, PROJECT, side)); 54 | filesToDelete.add(MCPPaths.get(mcp, PATCHES, side)); 55 | filesToDelete.add(MCPPaths.get(mcp, BUILD_ZIP, side)); 56 | filesToDelete.add(MCPPaths.get(mcp, BUILD_JAR, side)); 57 | } 58 | filesToDelete.add(MCPPaths.get(mcp, CONF)); 59 | filesToDelete.add(MCPPaths.get(mcp, NATIVES)); 60 | 61 | Path[] foldersToDelete = new Path[]{ 62 | MCPPaths.get(mcp, JARS), 63 | }; 64 | for (Path path : filesToDelete) { 65 | if (Files.exists(path)) { 66 | deleted = true; 67 | FileUtil.delete(path); 68 | } 69 | } 70 | for (Path path : foldersToDelete) { 71 | if (Files.exists(path) && path.toFile().list().length == 0) { 72 | deleted = true; 73 | Files.delete(path); 74 | } 75 | } 76 | return deleted; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskCreatePatch.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.*; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | import codechicken.diffpatch.DiffOperation; 10 | import org.mcphackers.mcp.MCP; 11 | import org.mcphackers.mcp.MCPPaths; 12 | 13 | public class TaskCreatePatch extends TaskStaged { 14 | public TaskCreatePatch(Side side, MCP instance) { 15 | super(side, instance); 16 | } 17 | 18 | @Override 19 | protected Stage[] setStages() { 20 | return new Stage[] { 21 | stage(getLocalizedStage("createpatch"), () -> { 22 | Path srcPathUnpatched = MCPPaths.get(mcp, SOURCE_UNPATCHED, side); 23 | Path srcPathPatched = MCPPaths.get(mcp, SOURCE, side); 24 | Path patchesOut = MCPPaths.get(mcp, PATCH, side); 25 | setProgress(getLocalizedStage("createpatch")); 26 | if (!Files.exists(srcPathPatched)) { 27 | throw new IOException("Patched " + side.name + " sources cannot be found!"); 28 | } 29 | if (!Files.exists(srcPathUnpatched)) { 30 | throw new IOException("Unpatched " + side.name + " sources cannot be found!"); 31 | } 32 | createDiffOperation(srcPathUnpatched, srcPathPatched, patchesOut); 33 | }) 34 | }; 35 | } 36 | 37 | public boolean createDiffOperation(Path aPath, Path bPath, Path outputPath) throws Exception { 38 | DiffOperation diffOperation = DiffOperation.builder() 39 | .aPath(aPath) 40 | .bPath(bPath) 41 | .aPrefix(null) 42 | .bPrefix(null) 43 | .singleDiff(true) 44 | .outputPath(outputPath) 45 | .filter(p -> p.endsWith(".java")) 46 | .build(); 47 | return diffOperation.doDiff(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskDownloadUpdate.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.URL; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | 10 | import org.json.JSONObject; 11 | import org.mcphackers.mcp.MCP; 12 | import org.mcphackers.mcp.MCPPaths; 13 | import org.mcphackers.mcp.tasks.mode.TaskMode; 14 | import org.mcphackers.mcp.tools.FileUtil; 15 | import org.mcphackers.mcp.tools.JSONUtil; 16 | import org.mcphackers.mcp.tools.Util; 17 | 18 | public class TaskDownloadUpdate extends TaskStaged { 19 | 20 | private static final String API = "https://api.github.com/repos/MCPHackers/RetroMCP-Java/releases/latest"; 21 | 22 | public TaskDownloadUpdate(MCP instance) { 23 | super(Side.ANY, instance); 24 | } 25 | 26 | @Override 27 | protected Stage[] setStages() { 28 | return new Stage[] { 29 | stage(getLocalizedStage("downloadupdate"), () -> { 30 | URL updateURL = new URL(API); 31 | InputStream in = updateURL.openStream(); 32 | JSONObject releaseJson = JSONUtil.parseJSON(in); 33 | String latestVersion = releaseJson.getString("tag_name"); 34 | String notes = releaseJson.getString("body"); 35 | if (!latestVersion.equals(MCP.VERSION)) { 36 | boolean confirmed = mcp.updateDialogue(notes, latestVersion); 37 | if (confirmed) { 38 | log("Downloading update..."); 39 | for (Object obj : releaseJson.getJSONArray("assets")) { 40 | if (obj instanceof JSONObject) { 41 | JSONObject assetObj = (JSONObject) obj; 42 | if (!assetObj.getString("name").endsWith(mcp.isGUI() ? "-GUI.jar" : "-CLI.jar")) { 43 | continue; 44 | } 45 | FileUtil.downloadFile(assetObj.getString("browser_download_url"), Paths.get(MCPPaths.UPDATE_JAR)); 46 | break; 47 | } 48 | } 49 | Path jarPath = Paths.get(MCP.class 50 | .getProtectionDomain() 51 | .getCodeSource() 52 | .getLocation() 53 | .toURI()); 54 | if (!Files.isDirectory(jarPath)) { 55 | String[] cmd = new String[]{ 56 | Util.getJava(), 57 | "-cp", 58 | MCPPaths.UPDATE_JAR, 59 | "org.mcphackers.mcp.Update", 60 | jarPath.toString() 61 | }; 62 | Util.runCommand(cmd); 63 | System.exit(0); 64 | } else { 65 | throw new IOException("Running from a folder! Aborting"); 66 | } 67 | } else { 68 | log("Cancelling update!"); 69 | } 70 | } else { 71 | mcp.showMessage(TaskMode.UPDATE_MCP.getFullName(), MCP.TRANSLATOR.translateKey("mcp.upToDate"), Task.INFO); 72 | } 73 | }) 74 | }; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskRecompile.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import org.mcphackers.mcp.MCP; 4 | import org.mcphackers.mcp.MCPPaths; 5 | import org.mcphackers.mcp.tasks.mode.TaskParameter; 6 | import org.mcphackers.mcp.tools.FileUtil; 7 | 8 | import javax.tools.*; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.nio.charset.StandardCharsets; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.nio.file.Paths; 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.stream.Collectors; 20 | import java.util.stream.Stream; 21 | 22 | import static org.mcphackers.mcp.MCPPaths.*; 23 | 24 | public class TaskRecompile extends TaskStaged { 25 | 26 | public TaskRecompile(Side side, MCP instance) { 27 | super(side, instance); 28 | } 29 | 30 | public TaskRecompile(Side side, MCP instance, ProgressListener listener) { 31 | super(side, instance, listener); 32 | } 33 | 34 | public static List collectClassPath(MCP mcp, Side side) { 35 | List classpath = new ArrayList<>(); 36 | classpath.add(MCPPaths.get(mcp, REMAPPED, side)); 37 | if (mcp.getCurrentVersion() != null) { 38 | classpath.addAll(mcp.getLibraries()); 39 | } 40 | return classpath; 41 | } 42 | 43 | @Override 44 | protected Stage[] setStages() { 45 | JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 46 | if (compiler == null) { 47 | throw new RuntimeException("Could not find compiling API. Please install or use a Java Development Kit to run this program."); 48 | } 49 | Path binPath = MCPPaths.get(mcp, BIN, side); 50 | Path srcPath = MCPPaths.get(mcp, SOURCE, side); 51 | return new Stage[]{ 52 | stage(getLocalizedStage("recompile"), 1, 53 | () -> { 54 | Files.createDirectories(binPath); 55 | FileUtil.cleanDirectory(binPath); 56 | setProgress(2); 57 | if (!Files.exists(srcPath)) { 58 | throw new IOException(side.getName() + " sources not found!"); 59 | } 60 | try (Stream paths = Files.list(srcPath)) { 61 | if (!paths.findAny().isPresent()) { 62 | return; 63 | } 64 | } 65 | 66 | final List src = collectSource(); 67 | final List classpath = collectClassPath(mcp, side); 68 | final List bootclasspath = collectBootClassPath(); 69 | 70 | List cp = new ArrayList<>(); 71 | classpath.forEach(p -> cp.add(p.toAbsolutePath().toString())); 72 | 73 | List options = new ArrayList<>(Arrays.asList("-d", binPath.toString())); 74 | 75 | int sourceVersion = mcp.getOptions().getIntParameter(TaskParameter.SOURCE_VERSION); 76 | if (sourceVersion >= 0) { 77 | options.addAll(Arrays.asList("-source", Integer.toString(sourceVersion))); 78 | } 79 | 80 | int targetVersion = mcp.getOptions().getIntParameter(TaskParameter.TARGET_VERSION); 81 | if (targetVersion >= 0) { 82 | options.addAll(Arrays.asList("-target", Integer.toString(targetVersion))); 83 | } 84 | 85 | List bootcp = new ArrayList<>(); 86 | bootclasspath.forEach(p -> bootcp.add(p.toAbsolutePath().toString())); 87 | if (!bootclasspath.isEmpty()) { 88 | options.addAll(Arrays.asList("-bootclasspath", String.join(System.getProperty("path.separator"), bootcp))); 89 | } 90 | 91 | options.addAll(Arrays.asList("-cp", String.join(System.getProperty("path.separator"), cp))); 92 | 93 | setProgress(3); 94 | 95 | DiagnosticCollector ds = new DiagnosticCollector<>(); 96 | recompile(compiler, ds, src, options); 97 | }), 98 | stage(getLocalizedStage("copyres"), 50, 99 | () -> { 100 | // Copy assets from source folder 101 | List assets = collectResources(); 102 | int i = 0; 103 | for (Path path : assets) { 104 | if (srcPath.relativize(path).getParent() != null) { 105 | Files.createDirectories(binPath.resolve(srcPath.relativize(path).getParent())); 106 | } 107 | Files.copy(path, binPath.resolve(srcPath.relativize(path))); 108 | i++; 109 | setProgress(50 + (int) ((double) i / assets.size() * 49)); 110 | } 111 | }) 112 | }; 113 | } 114 | 115 | public List collectResources() throws IOException { 116 | Path srcPath = MCPPaths.get(mcp, SOURCE, side); 117 | return FileUtil.walkDirectory(srcPath, path -> !Files.isDirectory(path) && !path.getFileName().toString().endsWith(".java") && !path.getFileName().toString().endsWith(".class")); 118 | } 119 | 120 | public List collectBootClassPath() throws IOException { 121 | List bootclasspath = new ArrayList<>(); 122 | String javaHome = mcp.getOptions().getStringParameter(TaskParameter.JAVA_HOME); 123 | if (!javaHome.isEmpty()) { 124 | Path libs = Paths.get(javaHome).resolve("lib"); 125 | Path libsJre = Paths.get(javaHome).resolve("jre/lib"); 126 | if (Files.exists(libs)) { 127 | FileUtil.collectJars(libs, bootclasspath); 128 | } 129 | if (Files.exists(libsJre)) { 130 | FileUtil.collectJars(libsJre, bootclasspath); 131 | } 132 | } 133 | return bootclasspath; 134 | } 135 | 136 | public List collectSource() throws IOException { 137 | Path srcPath = MCPPaths.get(mcp, SOURCE, side); 138 | List src; 139 | try (Stream pathStream = Files.walk(srcPath)) { 140 | src = pathStream.filter(path -> !Files.isDirectory(path) && path.getFileName().toString().endsWith(".java")).map(Path::toFile).collect(Collectors.toList()); 141 | } 142 | return src; 143 | } 144 | 145 | public void recompile(JavaCompiler compiler, DiagnosticCollector ds, Iterable src, Iterable recompileOptions) throws IOException, RuntimeException { 146 | StandardJavaFileManager mgr = compiler.getStandardFileManager(ds, null, StandardCharsets.UTF_8); 147 | Iterable sources = mgr.getJavaFileObjectsFromFiles(src); 148 | JavaCompiler.CompilationTask task = compiler.getTask(null, mgr, ds, recompileOptions, null, sources); 149 | mgr.close(); 150 | task.call(); 151 | for (Diagnostic diagnostic : ds.getDiagnostics()) 152 | if (diagnostic.getKind() == Diagnostic.Kind.ERROR || diagnostic.getKind() == Diagnostic.Kind.WARNING) { 153 | String[] kindString = {"Info", "Warning", "Error"}; 154 | byte kind = diagnostic.getKind() == Diagnostic.Kind.ERROR ? Task.ERROR : Task.WARNING; 155 | JavaFileObject source = diagnostic.getSource(); 156 | if (source == null) { 157 | addMessage(kindString[kind] + String.format("%n%s%n", 158 | diagnostic.getMessage(null)), 159 | kind); 160 | } else { 161 | addMessage(kindString[kind] + String.format(" on line %d in %s%n%s%n", 162 | diagnostic.getLineNumber(), 163 | source.getName(), 164 | diagnostic.getMessage(null)), 165 | kind); 166 | } 167 | } 168 | mgr.close(); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskRun.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.*; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.zip.ZipEntry; 14 | import java.util.zip.ZipInputStream; 15 | 16 | import org.mcphackers.mcp.MCP; 17 | import org.mcphackers.mcp.MCPPaths; 18 | import org.mcphackers.mcp.tasks.mode.TaskParameter; 19 | import org.mcphackers.mcp.tools.Util; 20 | import org.mcphackers.mcp.tools.versions.json.Version; 21 | import org.mcphackers.mcp.tools.versions.json.Version.Arguments; 22 | 23 | public class TaskRun extends TaskStaged { 24 | 25 | public static final List SERVER_MAIN = Arrays.asList("net.minecraft.server.Main", "net.minecraft.server.MinecraftServer", "com.mojang.minecraft.server.MinecraftServer"); 26 | 27 | public TaskRun(Side side, MCP instance) { 28 | super(side, instance); 29 | } 30 | 31 | @Override 32 | protected Stage[] setStages() { 33 | return new Stage[] { 34 | stage(getLocalizedStage("run"), () -> { 35 | Version currentVersion = mcp.getCurrentVersion(); 36 | Side mcpSide = mcp.getOptions().side; 37 | if (mcpSide == Side.ANY) { 38 | mcpSide = side; 39 | } 40 | 41 | String main = getMain(mcp, currentVersion, side); 42 | if (main == null) { 43 | mcp.log("Start class not found"); 44 | return; 45 | } 46 | 47 | boolean runBuild = mcp.getOptions().getBooleanParameter(TaskParameter.RUN_BUILD); 48 | boolean fullBuild = mcp.getOptions().getBooleanParameter(TaskParameter.FULL_BUILD); 49 | String[] runArgs = mcp.getOptions().getStringArrayParameter(TaskParameter.RUN_ARGS); 50 | List cpList = getClasspath(mcp, mcpSide, side, runBuild, fullBuild); 51 | 52 | List classPath = new ArrayList<>(); 53 | cpList.forEach(p -> classPath.add(p.toAbsolutePath().toString())); 54 | 55 | Path natives = MCPPaths.get(mcp, NATIVES).toAbsolutePath(); 56 | 57 | List args = new ArrayList<>(); 58 | args.add(Util.getJava()); 59 | Collections.addAll(args, runArgs); 60 | args.add("-Djava.library.path=" + natives); 61 | args.add("-cp"); 62 | args.add(String.join(File.pathSeparator, classPath)); 63 | args.add(main); 64 | if (side == Side.CLIENT) { 65 | args.addAll(getLaunchArgs(mcp, mcpSide)); 66 | Collections.addAll(args, mcp.getOptions().getStringParameter(TaskParameter.GAME_ARGS).split(" ")); 67 | } 68 | 69 | Util.runCommand(args.toArray(new String[0]), getMCDir(mcp, mcpSide), true); 70 | }) 71 | }; 72 | } 73 | 74 | public static String getMain(MCP mcp, Version version, Side side) throws IOException { 75 | if (side == Side.CLIENT) { 76 | return version.mainClass; 77 | } 78 | if (side == Side.SERVER) { 79 | Path jarPath = MCPPaths.get(mcp, JAR_ORIGINAL, Side.SERVER); 80 | try (ZipInputStream zipIn = new ZipInputStream(Files.newInputStream(jarPath))) { 81 | ZipEntry zipEntry; 82 | while ((zipEntry = zipIn.getNextEntry()) != null) { 83 | if (zipEntry.getName().endsWith(".class")) { 84 | String className = zipEntry.getName().substring(0, zipEntry.getName().length() - 6).replace('\\', '.').replace('/', '.'); 85 | if (SERVER_MAIN.contains(className)) { 86 | return className; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | return null; 93 | } 94 | 95 | /** 96 | * @param mcp 97 | * @return arguments for launching client 98 | */ 99 | public static List getLaunchArgs(MCP mcp, Side side) { 100 | Version ver = mcp.getCurrentVersion(); 101 | Arguments args = ver.arguments; 102 | String mcArgs = ver.minecraftArguments; 103 | List argsList = new ArrayList<>(); 104 | if (args != null) { 105 | for (Object o : args.game) { 106 | if (o instanceof String) { 107 | argsList.add((String) o); 108 | } 109 | } 110 | } else { 111 | argsList.addAll(Arrays.asList(mcArgs.split(" "))); 112 | } 113 | 114 | Path gameDir = getMCDir(mcp, side).toAbsolutePath(); 115 | Path assets = gameDir.resolve("assets"); 116 | 117 | for (int i = 0; i < argsList.size(); i++) { 118 | String arg = argsList.get(i); 119 | switch (arg) { 120 | case "${auth_player_name}": 121 | arg = "Player"; 122 | break; 123 | case "${auth_session}": 124 | case "${auth_uuid}": 125 | case "${auth_access_token}": 126 | arg = "-"; 127 | break; 128 | case "${user_properties}": 129 | arg = "{}"; 130 | break; 131 | case "${version_name}": 132 | arg = ver.id; 133 | break; 134 | case "${version_type}": 135 | arg = ver.type; 136 | break; 137 | case "${user_type}": 138 | arg = "legacy"; 139 | break; 140 | case "${assets_index_name}": 141 | arg = ver.assets; 142 | break; 143 | case "${assets_root}": 144 | case "${game_assets}": 145 | arg = assets.toString(); 146 | break; 147 | case "${game_directory}": 148 | arg = gameDir.toString(); 149 | break; 150 | } 151 | argsList.set(i, arg); 152 | } 153 | return argsList; 154 | } 155 | 156 | public static Path getMCDir(MCP mcp, Side side) { 157 | return MCPPaths.get(mcp, GAMEDIR, side); 158 | } 159 | 160 | private static List getClasspath(MCP mcp, Side side, Side runSide, boolean runBuild, boolean fullBuild) { 161 | List cpList = new ArrayList<>(mcp.getLibraries()); 162 | if (runBuild) { 163 | if (fullBuild) { 164 | cpList.add(MCPPaths.get(mcp, BUILD_JAR, runSide)); 165 | } else { 166 | cpList.add(MCPPaths.get(mcp, BUILD_ZIP, runSide)); 167 | cpList.add(MCPPaths.get(mcp, JAR_ORIGINAL, runSide)); 168 | } 169 | } else { 170 | cpList.add(MCPPaths.get(mcp, BIN, side)); 171 | if (Files.exists(MCPPaths.get(mcp, REMAPPED, side))) { 172 | cpList.add(MCPPaths.get(mcp, REMAPPED, side)); 173 | } 174 | } 175 | return cpList; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskRunnable.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | @FunctionalInterface 4 | public interface TaskRunnable { 5 | 6 | void doTask() throws Exception; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskSetup.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.CONF; 4 | import static org.mcphackers.mcp.MCPPaths.JARS; 5 | import static org.mcphackers.mcp.MCPPaths.LIB; 6 | import static org.mcphackers.mcp.MCPPaths.NATIVES; 7 | import static org.mcphackers.mcp.MCPPaths.VERSION; 8 | 9 | import java.io.BufferedWriter; 10 | import java.io.InputStream; 11 | import java.net.MalformedURLException; 12 | import java.net.URL; 13 | import java.nio.charset.StandardCharsets; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.nio.file.Paths; 17 | import java.util.List; 18 | 19 | import org.json.JSONObject; 20 | import org.mcphackers.mcp.MCP; 21 | import org.mcphackers.mcp.MCPPaths; 22 | import org.mcphackers.mcp.tasks.mode.TaskMode; 23 | import org.mcphackers.mcp.tasks.mode.TaskParameter; 24 | import org.mcphackers.mcp.tools.FileUtil; 25 | import org.mcphackers.mcp.tools.Util; 26 | import org.mcphackers.mcp.tools.versions.DownloadData; 27 | import org.mcphackers.mcp.tools.versions.VersionParser; 28 | import org.mcphackers.mcp.tools.versions.VersionParser.VersionData; 29 | import org.mcphackers.mcp.tools.versions.json.Version; 30 | 31 | public class TaskSetup extends TaskStaged { 32 | 33 | private long libsSize = 0; 34 | 35 | public TaskSetup(MCP instance) { 36 | super(Side.ANY, instance); 37 | } 38 | 39 | @Override 40 | protected Stage[] setStages() { 41 | return new Stage[] { 42 | stage(getLocalizedStage("setup"), 0, () -> { 43 | new TaskCleanup(mcp).cleanup(); 44 | FileUtil.createDirectories(MCPPaths.get(mcp, JARS)); 45 | FileUtil.createDirectories(MCPPaths.get(mcp, LIB)); 46 | FileUtil.createDirectories(MCPPaths.get(mcp, NATIVES)); 47 | 48 | setProgress(getLocalizedStage("setup"), 1); 49 | List versions = VersionParser.getInstance().getVersions(); 50 | String chosenVersion = mcp.getOptions().getStringParameter(TaskParameter.SETUP_VERSION); 51 | VersionData chosenVersionData; 52 | 53 | // Keep asking until chosenVersion equals one of the versionData 54 | input: 55 | while (true) { 56 | for (VersionData data : versions) { 57 | if (data.id.equals(chosenVersion)) { 58 | chosenVersionData = data; 59 | break input; 60 | } 61 | } 62 | chosenVersion = mcp.inputString(TaskMode.SETUP.getFullName(), MCP.TRANSLATOR.translateKey("task.setup.selectVersion")); 63 | } 64 | 65 | InputStream versionStream; 66 | try { 67 | versionStream = new URL(chosenVersionData.url).openStream(); 68 | } catch (MalformedURLException ex) { 69 | versionStream = Files.newInputStream(MCPPaths.get(mcp, chosenVersionData.url)); 70 | } 71 | JSONObject versionJsonObj = new JSONObject(new String(Util.readAllBytes(versionStream), StandardCharsets.UTF_8)); 72 | Version versionJson = Version.from(versionJsonObj); 73 | FileUtil.createDirectories(MCPPaths.get(mcp, CONF)); 74 | 75 | if (chosenVersionData.resources != null) { 76 | setProgress(getLocalizedStage("download", chosenVersionData.resources), 2); 77 | try { 78 | URL url = new URL(chosenVersionData.resources); 79 | FileUtil.extract(url.openStream(), MCPPaths.get(mcp, CONF)); 80 | } catch (MalformedURLException e) { 81 | Path p = Paths.get(chosenVersionData.resources); 82 | if (Files.exists(p)) { 83 | FileUtil.extract(p, MCPPaths.get(mcp, CONF)); 84 | } 85 | } 86 | } 87 | 88 | DownloadData dlData = new DownloadData(mcp, versionJson); 89 | dlData.performDownload((dl, totalSize) -> { 90 | libsSize += dl.downloadSize(); 91 | int percent = (int) ((double) libsSize / totalSize * 97D); 92 | setProgress(getLocalizedStage("download", dl.downloadURL()), 3 + percent); 93 | }); 94 | Path natives = MCPPaths.get(mcp, NATIVES); 95 | for (Path nativeArchive : DownloadData.getNatives(MCPPaths.get(mcp, LIB), versionJson)) { 96 | FileUtil.extract(nativeArchive, natives); 97 | } 98 | try (BufferedWriter writer = Files.newBufferedWriter(MCPPaths.get(mcp, VERSION))) { 99 | versionJsonObj.write(writer, 1, 0); 100 | } 101 | mcp.setCurrentVersion(versionJson); 102 | }) 103 | }; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskSourceBackup.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.SOURCE; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.time.LocalDate; 10 | import java.time.format.DateTimeFormatter; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.stream.Stream; 14 | import java.util.zip.ZipEntry; 15 | import java.util.zip.ZipOutputStream; 16 | 17 | import org.mcphackers.mcp.MCP; 18 | import org.mcphackers.mcp.MCPPaths; 19 | 20 | public class TaskSourceBackup extends TaskStaged { 21 | 22 | private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.BASIC_ISO_DATE; 23 | 24 | public TaskSourceBackup(Side side, MCP instance) { 25 | super(side, instance); 26 | } 27 | 28 | @Override 29 | protected Stage[] setStages() { 30 | return new Stage[] { 31 | stage(getLocalizedStage("backupsource"), this::packSourceIntoZip) 32 | }; 33 | } 34 | 35 | public TaskSourceBackup(Side side, MCP instance, ProgressListener listener) { 36 | super(side, instance, listener); 37 | } 38 | 39 | private void packSourceIntoZip() throws IOException { 40 | String filename = "src-backup-" + side.name + "-" + DATE_FORMATTER.format(LocalDate.now()); 41 | Path backupPath = MCPPaths.get(mcp, filename + ".zip"); 42 | for (int i = 1; Files.exists(backupPath); i++) { 43 | backupPath = MCPPaths.get(mcp, filename + "-" + i + ".zip"); 44 | } 45 | 46 | Path srcPath = MCPPaths.get(mcp, SOURCE, side); 47 | try (ZipOutputStream zip = new ZipOutputStream(Files.newOutputStream(backupPath))) { 48 | List srcFiles = new ArrayList<>(); 49 | try (Stream paths = Files.walk(srcPath)) { 50 | paths.forEach(file -> { 51 | if (file.getFileName().toString().endsWith(".java")) { 52 | srcFiles.add(file.toString()); 53 | } 54 | }); 55 | } 56 | long nFiles = srcFiles.size(); 57 | 58 | int i = 0; 59 | for (String path : srcFiles) { 60 | if (path.endsWith(".java")) { 61 | try { 62 | zip.putNextEntry(new ZipEntry(path)); 63 | zip.write(Files.readAllBytes(Paths.get(path))); 64 | zip.closeEntry(); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | setProgress(MCP.TRANSLATOR.translateKey("task.stage.backupsrc") + " " + path, (int) ((i / (float) nFiles) * 100)); 70 | i++; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskStaged.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import org.mcphackers.mcp.MCP; 4 | import org.mcphackers.mcp.plugin.MCPPlugin.TaskEvent; 5 | 6 | public abstract class TaskStaged extends Task { 7 | 8 | public int step; 9 | public Stage[] stages; 10 | 11 | public TaskStaged(Side side, MCP instance, ProgressListener listener) { 12 | super(side, instance, listener); 13 | } 14 | 15 | public TaskStaged(Side side, MCP instance) { 16 | super(side, instance); 17 | } 18 | 19 | public TaskStaged(MCP instance) { 20 | super(instance); 21 | } 22 | 23 | protected final void step() { 24 | step++; 25 | triggerEvent(TaskEvent.TASK_STEP); 26 | } 27 | 28 | protected abstract Stage[] setStages(); 29 | 30 | @Override 31 | public void doTask() throws Exception { 32 | stages = setStages(); 33 | mcp.setPluginOverrides(this); 34 | while (step < stages.length) { 35 | setProgress(stages[step].stageName, stages[step].completion); 36 | stages[step].doTask(); 37 | step(); 38 | } 39 | } 40 | 41 | /** 42 | * Replaces stage operation at stageIndex with task 43 | * 44 | * @param stageIndex 45 | * @param task 46 | */ 47 | public void overrideStage(int stageIndex, TaskRunnable task) { 48 | if (stageIndex < stages.length) { 49 | stages[stageIndex].setOperation(task); 50 | } 51 | } 52 | 53 | public Stage stage(String name, int percentage, TaskRunnable task) { 54 | return new Stage(name, percentage, task); 55 | } 56 | 57 | public Stage stage(String name, TaskRunnable task) { 58 | return new Stage(name, -1, task); 59 | } 60 | 61 | public static class Stage { 62 | private final String stageName; 63 | private final int completion; 64 | private TaskRunnable runnable; 65 | 66 | public Stage(String name, int i, TaskRunnable task) { 67 | setOperation(task); 68 | stageName = name; 69 | completion = i; 70 | } 71 | 72 | private void setOperation(TaskRunnable task) { 73 | runnable = task; 74 | } 75 | 76 | private void doTask() throws Exception { 77 | runnable.doTask(); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/TaskUpdateMD5.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.*; 4 | 5 | import java.io.BufferedWriter; 6 | import java.io.IOException; 7 | import java.nio.file.FileVisitResult; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.SimpleFileVisitor; 11 | import java.nio.file.attribute.BasicFileAttributes; 12 | import java.util.stream.Stream; 13 | 14 | import org.mcphackers.mcp.MCP; 15 | import org.mcphackers.mcp.MCPPaths; 16 | import org.mcphackers.mcp.tools.Util; 17 | 18 | public class TaskUpdateMD5 extends TaskStaged { 19 | /* 20 | * Indexes of stages for plugin overrides 21 | */ 22 | public static final int STAGE_RECOMPILE = 0; 23 | public static final int STAGE_MD5 = 1; 24 | private int progress = 0; 25 | 26 | public TaskUpdateMD5(Side side, MCP instance) { 27 | super(side, instance); 28 | } 29 | 30 | public TaskUpdateMD5(Side side, MCP instance, ProgressListener listener) { 31 | super(side, instance, listener); 32 | } 33 | 34 | @Override 35 | protected Stage[] setStages() { 36 | return new Stage[]{ 37 | stage(getLocalizedStage("recompile"), 38 | () -> new TaskRecompile(side, mcp, this).doTask()), 39 | stage(getLocalizedStage("updatemd5"), 50, 40 | () -> updateMD5(false)) 41 | }; 42 | } 43 | 44 | @Override 45 | public void setProgress(int progress) { 46 | if (step == 0) { 47 | int percent = (int) (progress * 0.50D); 48 | super.setProgress(percent); 49 | } else { 50 | super.setProgress(progress); 51 | } 52 | } 53 | 54 | public void updateMD5(boolean reobf) throws IOException { 55 | final Path binPath = MCPPaths.get(mcp, BIN, side); 56 | final Path md5 = MCPPaths.get(mcp, reobf ? MD5_RO : MD5, side); 57 | 58 | if (!Files.exists(binPath)) { 59 | throw new IOException(side.name + " classes not found!"); 60 | } 61 | try (BufferedWriter writer = Files.newBufferedWriter(md5)) { 62 | progress = 0; 63 | int total; 64 | try (Stream pathStream = Files.walk(binPath)) { 65 | total = (int) pathStream.parallel() 66 | .filter(p -> !p.toFile().isDirectory()) 67 | .count(); 68 | } 69 | Files.walkFileTree(binPath, new SimpleFileVisitor() { 70 | @Override 71 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 72 | String md5_hash = Util.getMD5(file); 73 | String fileName = binPath.relativize(file).toString().replace("\\", "/").replace(".class", ""); 74 | writer.append(fileName).append(" ").append(md5_hash).append("\n"); 75 | progress++; 76 | setProgress(50 + (int) ((double) progress / (double) total * 50)); 77 | return FileVisitResult.CONTINUE; 78 | } 79 | }); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/mode/TaskModeBuilder.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks.mode; 2 | 3 | import org.mcphackers.mcp.tasks.Task; 4 | import org.mcphackers.mcp.tasks.mode.TaskMode.Requirement; 5 | 6 | public class TaskModeBuilder { 7 | 8 | private String name; 9 | private boolean usesProgressBars = true; 10 | private Class taskClass; 11 | private TaskParameter[] params = new TaskParameter[]{}; 12 | private Requirement requirement; 13 | 14 | public TaskModeBuilder setName(String name) { 15 | this.name = name; 16 | return this; 17 | } 18 | 19 | public TaskModeBuilder setProgressBars(boolean enabled) { 20 | this.usesProgressBars = enabled; 21 | return this; 22 | } 23 | 24 | public TaskModeBuilder setTaskClass(Class taskClass) { 25 | this.taskClass = taskClass; 26 | return this; 27 | } 28 | 29 | public TaskModeBuilder setParameters(TaskParameter[] params) { 30 | this.params = params; 31 | return this; 32 | } 33 | 34 | public TaskModeBuilder addRequirement(Requirement condition) { 35 | this.requirement = condition; 36 | return this; 37 | } 38 | 39 | public TaskMode build() { 40 | return new TaskMode(this.name, this.taskClass, this.params, this.usesProgressBars, this.requirement); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/mode/TaskParameter.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks.mode; 2 | 3 | import de.fernflower.main.extern.IFernflowerPreferences; 4 | import org.mcphackers.mcp.MCP; 5 | import org.mcphackers.mcp.Options; 6 | import org.mcphackers.mcp.tasks.Task.Side; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | 12 | /** 13 | * Any optional parameters which can be accessed from tasks 14 | * 15 | * @see Options#getParameter(TaskParameter) 16 | */ 17 | public enum TaskParameter { 18 | 19 | SIDE("side", Integer.class, Side.ANY.index), 20 | PATCHES("patch", Boolean.class, true), 21 | IGNORED_PACKAGES("ignore", String[].class, new String[]{"paulscode", "com/jcraft", "de/jarnbjo", "isom"}), 22 | FERNFLOWER_OPTIONS("ff_options", String.class, getDefaultFFOptions()), 23 | OBFUSCATION("obf", Boolean.class, false), 24 | SRG_OBFUSCATION("srgobf", Boolean.class, false), 25 | FULL_BUILD("fullbuild", Boolean.class, false), 26 | RUN_BUILD("runbuild", Boolean.class, false), 27 | RUN_ARGS("runargs", String[].class, new String[]{"-Xms1024M", "-Xmx1024M"}), 28 | GAME_ARGS("gameargs", String.class, ""), 29 | SETUP_VERSION("setup", String.class, null), 30 | SOURCE_VERSION("source", Integer.class, -1), 31 | TARGET_VERSION("target", Integer.class, -1), 32 | JAVA_HOME("javahome", String.class, ""), 33 | EXCLUDED_CLASSES("excludedclasses", String.class, ""), 34 | DECOMPILE_RESOURCES("resources", Boolean.class, false), 35 | GUESS_GENERICS("generics", Boolean.class, false), 36 | STRIP_GENERICS("stripgenerics", Boolean.class, false), 37 | OUTPUT_SRC("outputsrc", Boolean.class, true); 38 | 39 | public static final TaskParameter[] VALUES = TaskParameter.values(); 40 | 41 | public final String name; 42 | public final Class type; 43 | public final Object defaultValue; 44 | 45 | TaskParameter(String name, Class c, T value) { 46 | TaskParameterMap.nameToParamMap.put(name, this); 47 | this.name = name; 48 | this.type = c; 49 | this.defaultValue = value; 50 | } 51 | 52 | public String getDesc() { 53 | String s = "task.param." + name; 54 | if (MCP.TRANSLATOR.hasKey(s)) { 55 | return MCP.TRANSLATOR.translateKey(s); 56 | } 57 | return MCP.TRANSLATOR.translateKey("task.noDesc"); 58 | } 59 | 60 | private static String getDefaultFFOptions() { 61 | Map ffOptions = new HashMap<>(IFernflowerPreferences.DEFAULTS); 62 | ffOptions.put(IFernflowerPreferences.NO_COMMENT_OUTPUT, "1"); 63 | ffOptions.put(IFernflowerPreferences.REMOVE_BRIDGE, "0"); 64 | ffOptions.put(IFernflowerPreferences.ASCII_STRING_CHARACTERS, "1"); 65 | ffOptions.put(IFernflowerPreferences.OVERRIDE_ANNOTATION, "0"); 66 | ffOptions.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1"); 67 | ffOptions.put(IFernflowerPreferences.INDENT_STRING, "\t"); 68 | ffOptions.remove(IFernflowerPreferences.BANNER); 69 | return ffOptions.toString(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tasks/mode/TaskParameterMap.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks.mode; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class TaskParameterMap { 7 | /** 8 | * Cached task parameter types 9 | */ 10 | static final Map nameToParamMap = new HashMap<>(); 11 | 12 | public static TaskParameter get(String param) { 13 | return nameToParamMap.get(param); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/ClassUtils.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools; 2 | 3 | import java.lang.reflect.Method; 4 | import java.lang.reflect.Modifier; 5 | import java.net.URL; 6 | import java.net.URLClassLoader; 7 | import java.nio.file.Path; 8 | import java.util.ArrayList; 9 | import java.util.Enumeration; 10 | import java.util.List; 11 | import java.util.jar.JarEntry; 12 | import java.util.jar.JarFile; 13 | 14 | public abstract class ClassUtils { 15 | @SuppressWarnings("unchecked") 16 | public static List> getClasses(Path p, Class type) throws Exception { 17 | String pathToJar = p.toAbsolutePath().toString(); 18 | JarFile jarFile = new JarFile(pathToJar); 19 | Enumeration e = jarFile.entries(); 20 | 21 | List> classes = new ArrayList<>(); 22 | URL[] urls = {new URL("jar:file:" + pathToJar + "!/")}; 23 | URLClassLoader cl = URLClassLoader.newInstance(urls); 24 | 25 | while (e.hasMoreElements()) { 26 | JarEntry je = e.nextElement(); 27 | if (je.isDirectory() || !je.getName().endsWith(".class")) { 28 | continue; 29 | } 30 | // -6 because of .class 31 | String className = je.getName().substring(0, je.getName().length() - 6); 32 | className = className.replace('/', '.'); 33 | Class cls = cl.loadClass(className); 34 | if (type.isAssignableFrom(cls)) { 35 | classes.add((Class) cls); 36 | } 37 | } 38 | jarFile.close(); 39 | return classes; 40 | } 41 | 42 | /** 43 | * {@link Modifier#isAbstract(int)} does no guarantee that all methods were implemented in the compiled class 44 | * And there is a chance it was compiled from a different source where one of the methods didn't exist 45 | */ 46 | public static boolean isClassAbstract(Class type) { 47 | for (Method meth : type.getMethods()) { 48 | if (Modifier.isAbstract(meth.getModifiers())) { 49 | return true; 50 | } 51 | } 52 | return false; 53 | } 54 | 55 | public static int getSourceFromClassVersion(int classVersion) { 56 | if (classVersion >= 45) { 57 | return classVersion - 44; 58 | } 59 | return -1; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/JSONUtil.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import org.json.JSONArray; 14 | import org.json.JSONException; 15 | import org.json.JSONObject; 16 | 17 | public final class JSONUtil { 18 | 19 | public static Object[] getArray(JSONArray array) { 20 | Object[] objs = new Object[array.length()]; 21 | for (int i = 0; i < array.length(); i++) { 22 | objs[i] = array.get(i); 23 | } 24 | return objs; 25 | } 26 | 27 | public static JSONObject getJSON(InputStream stream) { 28 | try { 29 | return parseJSON(stream); 30 | } catch (JSONException | IOException ex) { 31 | return null; 32 | } 33 | } 34 | 35 | public static List toList(JSONArray array) { 36 | if (array == null) { 37 | return Collections.emptyList(); 38 | } 39 | List list = new ArrayList<>(); 40 | for (int i = 0; i < array.length(); i++) { 41 | String s = array.optString(i, null); 42 | if (s == null) continue; 43 | list.add(s); 44 | } 45 | return list; 46 | } 47 | 48 | public static Map toMap(JSONObject object) { 49 | if (object == null) { 50 | return Collections.emptyMap(); 51 | } 52 | Map map = new HashMap<>(); 53 | for (String key : object.keySet()) { 54 | String value = object.optString(key, null); 55 | if (value == null) continue; 56 | try { 57 | int i = Integer.parseInt(key); 58 | map.put(i, value); 59 | } catch (NumberFormatException ignored) { 60 | } 61 | } 62 | return map; 63 | } 64 | 65 | public static JSONObject parseJSONFile(Path path) throws JSONException, IOException { 66 | String content = new String(Files.readAllBytes(path)); 67 | return new JSONObject(content); 68 | } 69 | 70 | public static JSONObject parseJSON(InputStream stream) throws JSONException, IOException { 71 | byte[] bytes = Util.readAllBytes(stream); 72 | String content = new String(bytes); 73 | return new JSONObject(content); 74 | } 75 | 76 | public static JSONArray parseJSONArray(InputStream stream) throws JSONException, IOException { 77 | byte[] bytes = Util.readAllBytes(stream); 78 | String content = new String(bytes); 79 | return new JSONArray(content); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/OS.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools; 2 | 3 | import java.util.Locale; 4 | 5 | public enum OS { 6 | 7 | linux, 8 | windows, 9 | osx, 10 | unknown; 11 | 12 | public static OS os; 13 | 14 | 15 | public static OS getOs() { 16 | if (os == null) { 17 | String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); 18 | if (osName.contains("mac") || osName.contains("darwin")) { 19 | os = osx; 20 | } else if (osName.contains("win")) { 21 | os = windows; 22 | } else if (osName.contains("nix") || osName.contains("nux") 23 | || osName.contains("aix") || osName.contains("sunos")) { 24 | os = linux; 25 | } else { 26 | os = unknown; 27 | } 28 | } 29 | return os; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/Util.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools; 2 | 3 | import java.awt.*; 4 | import java.awt.datatransfer.StringSelection; 5 | import java.io.BufferedInputStream; 6 | import java.io.BufferedReader; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.net.URI; 13 | import java.net.URISyntaxException; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.security.MessageDigest; 17 | import java.security.NoSuchAlgorithmException; 18 | import java.util.Objects; 19 | import java.util.concurrent.ExecutorService; 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.Future; 22 | 23 | public abstract class Util { 24 | public static final ExecutorService SINGLE_THREAD_EXECUTOR = Executors.newSingleThreadExecutor(); 25 | 26 | public static int runCommand(String[] cmd, Path dir, boolean killOnShutdown) throws IOException { 27 | ProcessBuilder procBuilder = new ProcessBuilder(cmd); 28 | if (dir != null) { 29 | procBuilder.directory(dir.toAbsolutePath().toFile()); 30 | } 31 | Process proc = procBuilder.start(); 32 | Thread hook = new Thread(proc::destroy); 33 | if (killOnShutdown) { 34 | Runtime.getRuntime().addShutdownHook(hook); 35 | } 36 | BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream())); 37 | BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); 38 | Thread stderr = new Thread(()-> { 39 | String line; 40 | while (true) { 41 | try { 42 | line = err.readLine(); 43 | if(line != null) { 44 | System.out.println("Minecraft STDERR: " + line); 45 | } 46 | } catch (IOException ignored) { 47 | // we don't really care what happens here 48 | } 49 | } 50 | 51 | }); 52 | Thread stdout = new Thread(()-> { 53 | String line; 54 | while (true) { 55 | try { 56 | line = in.readLine(); 57 | if(line != null) { 58 | System.out.println( line); 59 | } 60 | } catch (IOException ignored) { 61 | // we don't really care what happens here 62 | } 63 | } 64 | 65 | }); 66 | try { 67 | proc.waitFor(); 68 | } catch (InterruptedException e) { 69 | throw new RuntimeException("thread interrupted while runCommand was waiting for a process to finish", e ); 70 | } 71 | in.close(); 72 | err.close(); 73 | stderr.interrupt(); 74 | stdout.interrupt(); 75 | 76 | if (killOnShutdown) { 77 | Runtime.getRuntime().removeShutdownHook(hook); 78 | } 79 | return proc.exitValue(); 80 | } 81 | 82 | public static void runCommand(String[] cmd) throws IOException { 83 | ProcessBuilder procBuilder = new ProcessBuilder(cmd); 84 | procBuilder.start(); 85 | } 86 | 87 | public static void copyToClipboard(String text) { 88 | Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null); 89 | } 90 | 91 | public static Thread operateOnThread(Runnable function) { 92 | Thread thread = new Thread(function); 93 | thread.start(); 94 | return thread; 95 | } 96 | 97 | public static Future enqueueRunnable(Runnable function) { 98 | return SINGLE_THREAD_EXECUTOR.submit(function); 99 | } 100 | 101 | public static void openUrl(String url) { 102 | try { 103 | if (Objects.requireNonNull(OS.getOs()) == OS.linux) { 104 | new ProcessBuilder("/usr/bin/env", "xdg-open", url).start(); 105 | } else { 106 | if (Desktop.isDesktopSupported()) { 107 | Desktop desktop = Desktop.getDesktop(); 108 | desktop.browse(new URI(url)); 109 | } 110 | } 111 | } catch (IOException ex) { 112 | throw new RuntimeException(ex); 113 | } catch (URISyntaxException ex) { 114 | throw new IllegalArgumentException(ex); 115 | } 116 | } 117 | 118 | public static byte[] readAllBytes(InputStream inputStream) throws IOException { 119 | final int bufLen = 4 * 0x400; // 4KB 120 | byte[] buf = new byte[bufLen]; 121 | int readLen; 122 | IOException exception = null; 123 | 124 | try { 125 | try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { 126 | while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) 127 | outputStream.write(buf, 0, readLen); 128 | 129 | return outputStream.toByteArray(); 130 | } 131 | } catch (IOException e) { 132 | exception = e; 133 | throw e; 134 | } finally { 135 | if (exception == null) inputStream.close(); 136 | else try { 137 | inputStream.close(); 138 | } catch (IOException e) { 139 | exception.addSuppressed(e); 140 | } 141 | } 142 | } 143 | 144 | public static String getMD5(Path file) throws IOException { 145 | try { 146 | MessageDigest md = MessageDigest.getInstance("MD5"); 147 | byte[] digest = getDigest(md, file); 148 | 149 | StringBuilder sb = new StringBuilder(); 150 | for (byte bite : digest) { 151 | sb.append(String.format("%02x", bite & 0xff)); 152 | } 153 | return sb.toString(); 154 | } catch (NoSuchAlgorithmException e) { 155 | throw new IOException(e); 156 | } 157 | } 158 | 159 | public static String getSHA1(Path file) throws IOException { 160 | try { 161 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 162 | byte[] digest = getDigest(md, file); 163 | 164 | StringBuilder sb = new StringBuilder(); 165 | for (byte bite : digest) { 166 | sb.append(Integer.toString((bite & 255) + 256, 16).substring(1).toLowerCase()); 167 | } 168 | return sb.toString(); 169 | } catch (NoSuchAlgorithmException e) { 170 | throw new IOException(e); 171 | } 172 | } 173 | 174 | public static byte[] getDigest(MessageDigest md, Path file) { 175 | try (InputStream fs = Files.newInputStream(file)) { 176 | BufferedInputStream bs = new BufferedInputStream(fs); 177 | byte[] buffer = new byte[1024]; 178 | int bytesRead; 179 | 180 | while ((bytesRead = bs.read(buffer, 0, buffer.length)) != -1) { 181 | md.update(buffer, 0, bytesRead); 182 | } 183 | return md.digest(); 184 | } catch (IOException ex) { 185 | ex.printStackTrace(); 186 | } 187 | return new byte[] {}; 188 | } 189 | 190 | public static String firstUpperCase(String s) { 191 | if (s == null) { 192 | return null; 193 | } 194 | if (s.length() <= 1) { 195 | return s.toUpperCase(); 196 | } else { 197 | return Character.toUpperCase(s.charAt(0)) + s.substring(1); 198 | } 199 | } 200 | 201 | public static String convertFromEscapedString(String s) { 202 | return s.replace("\\n", "\n").replace("\\t", "\t"); 203 | } 204 | 205 | public static String convertToEscapedString(String s) { 206 | return s.replace("\n", "\\n").replace("\t", "\\t"); 207 | } 208 | 209 | public static String getJava() { 210 | return System.getProperties().getProperty("java.home") + File.separator + "bin" + File.separator + "java"; 211 | } 212 | 213 | public static int getJavaVersion() { 214 | String javaVersion = System.getProperty("java.version"); 215 | String[] versionParts = javaVersion.split("\\."); 216 | int versionNumber = Integer.parseInt(versionParts[0]); 217 | 218 | if (versionNumber < 9) { 219 | versionNumber = Integer.parseInt(versionParts[1]); 220 | } else { 221 | versionNumber = Integer.parseInt(versionParts[0]); 222 | } 223 | return versionNumber; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/fernflower/DecompileLogger.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.fernflower; 2 | 3 | import de.fernflower.main.extern.IFernflowerLogger; 4 | import org.mcphackers.mcp.MCP; 5 | import org.mcphackers.mcp.tasks.ProgressListener; 6 | 7 | public class DecompileLogger extends IFernflowerLogger { 8 | 9 | private final ProgressListener listener; 10 | private int total; 11 | 12 | public DecompileLogger(ProgressListener listener) { 13 | this.listener = listener; 14 | } 15 | 16 | @Override 17 | public void writeMessage(String message, Severity severity) { 18 | if(severity.ordinal() >= Severity.WARN.ordinal()) { 19 | // System.out.println(message); 20 | } 21 | } 22 | 23 | @Override 24 | public void writeMessage(String message, Throwable t) { 25 | } 26 | 27 | @Override 28 | public void startReadingClass(String className) { 29 | listener.setProgress(MCP.TRANSLATOR.translateKey("task.stage.decompile") + " " + className); 30 | } 31 | 32 | @Override 33 | public void startSave(int total) { 34 | this.total = total; 35 | } 36 | 37 | @Override 38 | public void updateSave(int current) { 39 | listener.setProgress((int) ((double) current / (double) total * 100)); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/fernflower/Decompiler.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.fernflower; 2 | 3 | import de.fernflower.main.decompiler.BaseDecompiler; 4 | import de.fernflower.main.decompiler.DirectoryResultSaver; 5 | import de.fernflower.main.extern.IBytecodeProvider; 6 | import de.fernflower.main.extern.IFernflowerPreferences; 7 | import de.fernflower.util.InterpreterUtil; 8 | import org.mcphackers.mcp.MCP; 9 | import org.mcphackers.mcp.tasks.ProgressListener; 10 | import org.mcphackers.mcp.tasks.mode.TaskParameter; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.zip.ZipEntry; 19 | import java.util.zip.ZipFile; 20 | 21 | public class Decompiler implements IBytecodeProvider { 22 | public final DecompileLogger log; 23 | private final Path source; 24 | private final List libraries; 25 | private final Path destination; 26 | private final Map mapOptions; 27 | private final ZipFileCache openZips = new ZipFileCache(); 28 | 29 | public Decompiler(ProgressListener listener, Path source, Path out, List libs, MCP mcp) { 30 | this.source = source; 31 | this.libraries = libs; 32 | this.destination = out; 33 | this.log = new DecompileLogger(listener); 34 | this.mapOptions = mcp.getOptions().getFernflowerOptions(); 35 | this.mapOptions.put(IFernflowerPreferences.REMOVE_BRIDGE, mcp.getOptions().getBooleanParameter(TaskParameter.GUESS_GENERICS) ? "1" : "0"); 36 | } 37 | 38 | public void decompile() throws IOException { 39 | BaseDecompiler decompiler = new BaseDecompiler(this, new DirectoryResultSaver(destination.toFile()), mapOptions, log/*, javadocs.exists() ? new TinyJavadocProvider(javadocs) : null*/); 40 | for (Path lib : libraries) { 41 | if (Files.exists(lib)) 42 | decompiler.addSpace(lib.toAbsolutePath().toFile(), false); 43 | } 44 | decompiler.addSpace(source.toAbsolutePath().toFile(), true); 45 | decompiler.decompileContext(); 46 | this.openZips.close(); 47 | } 48 | 49 | @Override 50 | public byte[] getBytecode(String externalPath, String internalPath) throws IOException { 51 | if (internalPath == null) { 52 | File file = new File(externalPath); 53 | return InterpreterUtil.getBytes(file); 54 | } else { 55 | final ZipFile archive = this.openZips.get(externalPath); 56 | final ZipEntry entry = archive.getEntry(internalPath); 57 | if (entry == null) { 58 | throw new IOException("Entry not found: " + internalPath); 59 | } 60 | return InterpreterUtil.getBytes(archive, entry); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/fernflower/SimpleJavadocProvider.java: -------------------------------------------------------------------------------- 1 | //package org.mcphackers.mcp.tools.fernflower; 2 | // 3 | //import de.fernflower.main.providers.IJavadocProvider; 4 | //import de.fernflower.struct.StructClass; 5 | //import de.fernflower.struct.StructField; 6 | //import de.fernflower.struct.StructMethod; 7 | // 8 | //import java.io.BufferedReader; 9 | //import java.io.File; 10 | //import java.io.FileReader; 11 | //import java.io.IOException; 12 | //import java.util.HashMap; 13 | //import java.util.Map; 14 | // 15 | //public class SimpleJavadocProvider implements IJavadocProvider { 16 | // 17 | // private static final Map classes = new HashMap<>(); 18 | // private static final Map methods = new HashMap<>(); 19 | // private static final Map fields = new HashMap<>(); 20 | // 21 | // public SimpleJavadocProvider(File javadocFile) throws IOException { 22 | // readMappings(javadocFile); 23 | // } 24 | // 25 | // @Override 26 | // public String getClassDoc(StructClass structClass) { 27 | // return classes.get(structClass.qualifiedName.replace(".", "/")); 28 | // } 29 | // 30 | // @Override 31 | // public String getMethodDoc(StructClass structClass, StructMethod structMethod) { 32 | // return methods.get(structClass.qualifiedName.replace(".", "/") + "/" + structMethod.getName() + structMethod.getDescriptor()); 33 | // } 34 | // 35 | // 36 | // @Override 37 | // public String getFieldDoc(StructClass structClass, StructField structField) { 38 | // return fields.get(structClass.qualifiedName.replace(".", "/") + "/" + structField.getName() + "(" + structField.getDescriptor() + ")"); 39 | // } 40 | // 41 | // private static void readMappings(File inputFile) throws IOException { 42 | // try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) { 43 | // for (String line = reader.readLine(); line != null; line = reader.readLine()) { 44 | // String[] splitLine = line.split("="); 45 | // 46 | // if (splitLine[0].startsWith("c ")) { 47 | // // Class Javadoc: c net/minecraft/client/Minecraft="Hello\nWorld" 48 | // classes.put(splitLine[0].replaceFirst("c ", ""), splitLine[1].replaceAll("\"", "")); 49 | // } else if (splitLine[0].startsWith("m ")) { 50 | // // Method Javadoc: m net/minecraft/client/Minecraft/runTick()V="Hello\nWorld" 51 | // methods.put(splitLine[0].replace("m ", ""), splitLine[1].replaceAll("\"", "")); 52 | // } else if (splitLine[0].startsWith("f ")) { 53 | // // Field Javadoc: f net/minecraft/client/Minecraft/fullscreen(Z)="Hello\nWorld" 54 | // fields.put(splitLine[0].replace("f ", ""), splitLine[1].replaceAll("\"", "")); 55 | // } else { 56 | // System.err.println("Invalid keyword with: " + line); 57 | // } 58 | // } 59 | // } 60 | // } 61 | //} 62 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/fernflower/TinyJavadocProvider.java: -------------------------------------------------------------------------------- 1 | //package org.mcphackers.mcp.tools.fernflower; 2 | // 3 | //import de.fernflower.main.providers.IJavadocProvider; 4 | //import de.fernflower.struct.StructClass; 5 | //import de.fernflower.struct.StructField; 6 | //import de.fernflower.struct.StructMethod; 7 | //import net.fabricmc.mappingio.MappingReader; 8 | //import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; 9 | //import net.fabricmc.mappingio.tree.MappingTree; 10 | //import net.fabricmc.mappingio.tree.MemoryMappingTree; 11 | //import org.objectweb.asm.Opcodes; 12 | // 13 | //import java.io.BufferedReader; 14 | //import java.io.File; 15 | //import java.io.IOException; 16 | //import java.nio.file.Files; 17 | //import java.util.ArrayList; 18 | //import java.util.List; 19 | // 20 | //public class TinyJavadocProvider implements IJavadocProvider { 21 | // public final MappingTree mappingTree; 22 | // 23 | // public TinyJavadocProvider(File tinyFile) { 24 | // mappingTree = readMappings(tinyFile); 25 | // } 26 | // 27 | // @Override 28 | // public String getClassDoc(StructClass structClass) { 29 | // MappingTree.ClassMapping classMapping = mappingTree.getClass(structClass.qualifiedName); 30 | // 31 | // if (classMapping == null) { 32 | // return null; 33 | // } 34 | // 35 | // if (!isRecord(structClass)) { 36 | // return classMapping.getComment(); 37 | // } else { 38 | // // A RECORD??? IN MY JAVA 8??? 39 | // return null; 40 | // } 41 | // } 42 | // 43 | // @Override 44 | // public String getFieldDoc(StructClass structClass, StructField structField) { 45 | // // None static fields in records are handled in the class javadoc. 46 | // if (isRecord(structClass) && !isStatic(structField)) { 47 | // return null; 48 | // } 49 | // 50 | // MappingTree.ClassMapping classMapping = mappingTree.getClass(structClass.qualifiedName); 51 | // 52 | // if (classMapping == null) { 53 | // return null; 54 | // } 55 | // 56 | // MappingTree.FieldMapping fieldMapping = classMapping.getField(structField.getName(), structField.getDescriptor()); 57 | // 58 | // return fieldMapping != null ? fieldMapping.getComment() : null; 59 | // } 60 | // 61 | // @Override 62 | // public String getMethodDoc(StructClass structClass, StructMethod structMethod) { 63 | // MappingTree.ClassMapping classMapping = mappingTree.getClass(structClass.qualifiedName); 64 | // 65 | // if (classMapping == null) { 66 | // return null; 67 | // } 68 | // 69 | // MappingTree.MethodMapping methodMapping = classMapping.getMethod(structMethod.getName(), structMethod.getDescriptor()); 70 | // 71 | // if (methodMapping != null) { 72 | // List parts = new ArrayList<>(); 73 | // 74 | // if (methodMapping.getComment() != null) { 75 | // parts.add(methodMapping.getComment()); 76 | // } 77 | // 78 | // boolean addedParam = false; 79 | // 80 | // for (MappingTree.MethodArgMapping argMapping : methodMapping.getArgs()) { 81 | // String comment = argMapping.getComment(); 82 | // 83 | // if (comment != null) { 84 | // if (!addedParam && methodMapping.getComment() != null) { 85 | // //Add a blank line before params when the method has a comment 86 | // parts.add(""); 87 | // addedParam = true; 88 | // } 89 | // 90 | // parts.add(String.format("@param %s %s", argMapping.getName("named"), comment)); 91 | // } 92 | // } 93 | // 94 | // if (parts.isEmpty()) { 95 | // return null; 96 | // } 97 | // 98 | // return String.join("\n", parts); 99 | // } 100 | // 101 | // return null; 102 | // } 103 | // 104 | // private static MappingTree readMappings(File input) throws RuntimeException { 105 | // try (BufferedReader reader = Files.newBufferedReader(input.toPath())) { 106 | // MemoryMappingTree mappingTree = new MemoryMappingTree(); 107 | // MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(mappingTree, "named"); 108 | // MappingReader.read(reader, nsSwitch); 109 | // 110 | // return mappingTree; 111 | // } catch (IOException e) { 112 | // throw new RuntimeException("Failed to read mappings", e); 113 | // } 114 | // } 115 | // 116 | // public static boolean isRecord(StructClass structClass) { 117 | // return (structClass.getAccessFlags() & Opcodes.ACC_RECORD) != 0; 118 | // } 119 | // 120 | // public static boolean isStatic(StructField structField) { 121 | // return (structField.getAccessFlags() & Opcodes.ACC_STATIC) != 0; 122 | // } 123 | //} 124 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/fernflower/ZipFileCache.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.fernflower; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.UncheckedIOException; 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.zip.ZipFile; 9 | 10 | // Taken from VineFlower graciously 11 | public final class ZipFileCache implements AutoCloseable { 12 | private final Map files = new ConcurrentHashMap<>(); 13 | 14 | public ZipFile get(final String path) throws IOException { 15 | try { 16 | return this.files.computeIfAbsent(path, pth -> { 17 | try { 18 | return new ZipFile(new File(pth)); 19 | } catch (IOException ex) { 20 | throw new UncheckedIOException(ex); 21 | } 22 | }); 23 | } catch (UncheckedIOException ex) { 24 | throw ex.getCause(); 25 | } 26 | } 27 | 28 | @Override 29 | public void close() { 30 | IOException failure = null; 31 | 32 | for (Map.Entry entry : this.files.entrySet()) { 33 | try { 34 | entry.getValue().close(); 35 | } catch (IOException ex) { 36 | if (failure == null) { 37 | failure = ex; 38 | } else { 39 | failure.addSuppressed(ex); 40 | } 41 | } 42 | } 43 | 44 | this.files.clear(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/mappings/MappingUtil.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.mappings; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import net.fabricmc.mappingio.MappingReader; 15 | import net.fabricmc.mappingio.MappingWriter; 16 | import net.fabricmc.mappingio.adapter.MappingNsRenamer; 17 | import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; 18 | import net.fabricmc.mappingio.format.MappingFormat; 19 | import net.fabricmc.mappingio.tree.MemoryMappingTree; 20 | 21 | public final class MappingUtil { 22 | 23 | /** 24 | * @param number 25 | * @return Obfuscated string based on number index 26 | */ 27 | public static String getObfuscatedName(int number) { 28 | // Default obfuscation scheme 29 | return getObfuscatedName('a', 'z', number); 30 | } 31 | 32 | /** 33 | * @param from 34 | * @param to 35 | * @param number 36 | * @return Obfuscated string based on number index and character range 37 | */ 38 | public static String getObfuscatedName(char from, char to, int number) { 39 | if (number == 0) { 40 | return String.valueOf(from); 41 | } 42 | int num = number; 43 | int allChars = to - from + 1; 44 | StringBuilder retName = new StringBuilder(); 45 | while (num >= 0) { 46 | char c = (char) (from + (num % allChars)); 47 | retName.insert(0, c); 48 | num = num / allChars - 1; 49 | } 50 | return retName.toString(); 51 | } 52 | 53 | /** 54 | * @param chars 55 | * @param number 56 | * @return Obfuscated string based on number index and character array 57 | */ 58 | public static String getObfuscatedName(char[] chars, int number) { 59 | if (number == 0) { 60 | return String.valueOf(chars[0]); 61 | } 62 | int num = number; 63 | int allChars = chars.length; 64 | StringBuilder retName = new StringBuilder(); 65 | while (num >= 0) { 66 | char c = chars[num % allChars]; 67 | retName.insert(0, c); 68 | num = num / allChars - 1; 69 | } 70 | return retName.toString(); 71 | } 72 | 73 | public static List readNamespaces(Path mappings) throws IOException { 74 | List namespaces = new ArrayList<>(); 75 | boolean invalid = false; 76 | try (BufferedReader reader = Files.newBufferedReader(mappings)) { 77 | String header = reader.readLine(); 78 | if (header != null) { 79 | if (header.startsWith("tiny\t2\t0\t")) { 80 | namespaces.addAll(Arrays.asList(header.substring(9).trim().split("\t"))); 81 | } else if (header.startsWith("v1\t")) { 82 | namespaces.addAll(Arrays.asList(header.substring(3).trim().split("\t"))); 83 | } else { 84 | invalid = true; 85 | } 86 | } else { 87 | invalid = true; 88 | } 89 | } 90 | if (invalid) { 91 | throw new IllegalStateException("No valid tiny header in " + mappings); 92 | } 93 | return namespaces; 94 | } 95 | 96 | //official named -> named client server 97 | public static void mergeMappings(Path client, Path server, Path out) throws IOException { 98 | MemoryMappingTree mergedMappingTree = new MemoryMappingTree(); 99 | 100 | // Create visitors to flip the mapping tree 101 | Map clientNsRenames = Collections.singletonMap("official", "client"); 102 | Map serverNsRenames = Collections.singletonMap("official", "server"); 103 | 104 | MappingNsRenamer clientNsRenamer = new MappingNsRenamer(mergedMappingTree, clientNsRenames); 105 | MappingSourceNsSwitch clientNsSwitch = new MappingSourceNsSwitch(clientNsRenamer, "named"); 106 | MappingNsRenamer serverNsRenamer = new MappingNsRenamer(mergedMappingTree, serverNsRenames); 107 | MappingSourceNsSwitch serverNsSwitch = new MappingSourceNsSwitch(serverNsRenamer, "named"); 108 | 109 | // Read client/server mappings with the above visitors 110 | MappingReader.read(client, MappingFormat.TINY_2_FILE, clientNsSwitch); 111 | MappingReader.read(server, MappingFormat.TINY_2_FILE, serverNsSwitch); 112 | 113 | // Export the merged mapping trees 114 | mergedMappingTree.accept(MappingWriter.create(out, MappingFormat.TINY_2_FILE)); 115 | } 116 | 117 | //official named -> named client 118 | public static void mergeMappings(Path client, Path out) throws IOException { 119 | String currentSrc = "official"; 120 | String currentDst = "named"; 121 | String newDst = "client"; 122 | Map nsRenames = new HashMap<>(); 123 | nsRenames.put(currentSrc, newDst); 124 | 125 | // Switch the namespaces from official -> named to named -> official 126 | // Rename the namespaces from named -> official to named -> client 127 | MappingNsRenamer nsRenamer = new MappingNsRenamer(MappingWriter.create(out, MappingFormat.TINY_2_FILE), nsRenames); 128 | MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsRenamer, currentDst); 129 | MappingReader.read(client, MappingFormat.TINY_2_FILE, nsSwitch); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/EclipseProjectWriter.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.PROJECT; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | import org.mcphackers.mcp.MCP; 10 | import org.mcphackers.mcp.MCPPaths; 11 | import org.mcphackers.mcp.tasks.Task; 12 | import org.mcphackers.mcp.tasks.Task.Side; 13 | import org.mcphackers.mcp.tools.Util; 14 | import org.mcphackers.mcp.tools.project.eclipse.EclipseClasspath; 15 | import org.mcphackers.mcp.tools.project.eclipse.EclipsePreferences; 16 | import org.mcphackers.mcp.tools.project.eclipse.EclipseProject; 17 | import org.mcphackers.mcp.tools.project.eclipse.EclipseRunConfig; 18 | import org.mcphackers.mcp.tools.versions.json.DependDownload; 19 | import org.mcphackers.mcp.tools.versions.json.Version; 20 | 21 | public class EclipseProjectWriter implements ProjectWriter { 22 | 23 | @Override 24 | public void createProject(MCP mcp, Side side, int sourceVersion) throws IOException { 25 | Path proj = MCPPaths.get(mcp, PROJECT, side); 26 | Version version = mcp.getCurrentVersion(); 27 | String clientArgs = ProjectWriter.getLaunchArgs(mcp, side); 28 | Task.Side[] launchSides = side == Task.Side.MERGED ? new Task.Side[]{Task.Side.CLIENT, Task.Side.SERVER} : new Task.Side[]{side}; 29 | 30 | String projectName = "Minecraft " + (side == Task.Side.CLIENT ? "Client" : side == Task.Side.SERVER ? "Server" : side == Task.Side.MERGED ? "Merged" : "Project"); 31 | 32 | try (XMLWriter writer = new XMLWriter(Files.newBufferedWriter(proj.resolve(".classpath")))) { 33 | EclipseClasspath classpath = new EclipseClasspath(mcp, projectName, sourceVersion); 34 | for (DependDownload dependency : version.libraries) { 35 | classpath.addDependency(dependency); 36 | } 37 | classpath.toXML(writer, side); 38 | } 39 | 40 | try (XMLWriter writer = new XMLWriter(Files.newBufferedWriter(proj.resolve(".project")))) { 41 | EclipseProject project = new EclipseProject(projectName); 42 | project.toXML(writer); 43 | } 44 | 45 | for (Task.Side launchSide : launchSides) { 46 | try (XMLWriter writer = new XMLWriter(Files.newBufferedWriter(proj.resolve(Util.firstUpperCase(launchSide.name) + ".launch")))) { 47 | EclipseRunConfig runConfig = new EclipseRunConfig(mcp); 48 | runConfig.setProjectName(projectName); 49 | runConfig.setLaunchSide(launchSide); 50 | runConfig.setClientArgs(clientArgs); 51 | runConfig.toXML(writer); 52 | } 53 | } 54 | 55 | Path settings = proj.resolve(".settings"); 56 | Files.createDirectories(settings); 57 | 58 | String sourceVer = sourceVersion >= 9 ? String.valueOf(sourceVersion) : "1." + sourceVersion; 59 | 60 | try (PairWriter writer = new PairWriter(Files.newBufferedWriter(settings.resolve("org.eclipse.jdt.core.prefs")))) { 61 | EclipsePreferences preferences = new EclipsePreferences(); 62 | preferences.setSourceVersion(sourceVer); 63 | preferences.toString(writer); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/PairWriter.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.IOException; 5 | import java.io.Writer; 6 | 7 | public class PairWriter extends BufferedWriter { 8 | public PairWriter(Writer out) { 9 | super(out); 10 | } 11 | 12 | public void writePair(String key, String value) throws IOException { 13 | this.write(key + "=" + value + System.lineSeparator()); 14 | } 15 | 16 | public void writePair(String key, int value) throws IOException { 17 | this.write(key + "=" + value + System.lineSeparator()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/ProjectWriter.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | import org.mcphackers.mcp.MCP; 7 | import org.mcphackers.mcp.tasks.Task.Side; 8 | import org.mcphackers.mcp.tasks.TaskRun; 9 | 10 | public interface ProjectWriter { 11 | static String getLaunchArgs(MCP mcp, Side side) { 12 | List args = TaskRun.getLaunchArgs(mcp, side); 13 | for (int i = 0; i < args.size(); i++) { 14 | String arg = args.get(i); 15 | if (arg.contains(" ")) { 16 | arg = "\"" + arg + "\""; 17 | } 18 | args.set(i, arg); 19 | } 20 | return String.join(" ", args).replace("\"", """); 21 | } 22 | 23 | void createProject(MCP mcp, Side side, int sourceVersion) throws IOException; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/VSCProjectWriter.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project; 2 | 3 | import static org.mcphackers.mcp.MCPPaths.*; 4 | 5 | import java.io.BufferedWriter; 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.List; 10 | 11 | import org.json.JSONArray; 12 | import org.json.JSONObject; 13 | import org.mcphackers.mcp.MCP; 14 | import org.mcphackers.mcp.MCPPaths; 15 | import org.mcphackers.mcp.tasks.Task; 16 | import org.mcphackers.mcp.tasks.Task.Side; 17 | import org.mcphackers.mcp.tasks.TaskRun; 18 | 19 | public class VSCProjectWriter implements ProjectWriter { 20 | 21 | @Override 22 | public void createProject(MCP mcp, Side side, int sourceVersion) throws IOException { 23 | Path proj = MCPPaths.get(mcp, PROJECT, side); 24 | Task.Side[] launchSides = side == Task.Side.MERGED ? new Task.Side[]{Task.Side.CLIENT, Task.Side.SERVER} : new Task.Side[]{side}; 25 | 26 | String projectName = "Minecraft " + (side == Task.Side.CLIENT ? "Client" : side == Task.Side.SERVER ? "Server" : side == Task.Side.MERGED ? "Merged" : "Project"); 27 | 28 | Files.createDirectories(proj.resolve(".vscode")); 29 | try (BufferedWriter writer = Files.newBufferedWriter(proj.resolve(".vscode/settings.json"))) { 30 | JSONObject settingsJson = new JSONObject(); 31 | JSONObject searchExclude = new JSONObject(); 32 | searchExclude.put("src_original/**", true); 33 | searchExclude.put("bin/**", true); 34 | searchExclude.put("output/**", true); 35 | settingsJson.put("search.exclude", searchExclude); 36 | settingsJson.write(writer); 37 | } 38 | try (BufferedWriter writer = Files.newBufferedWriter(proj.resolve(".vscode/launch.json"))) { 39 | JSONObject launchJson = new JSONObject(); 40 | launchJson.put("version", "0.2.0"); 41 | JSONArray configurations = new JSONArray(); 42 | for (Task.Side launchSide : launchSides) { 43 | JSONObject config = new JSONObject(); 44 | config.put("type", "java"); 45 | config.put("name", projectName); 46 | config.put("request", "launch"); 47 | config.put("mainClass", TaskRun.getMain(mcp, mcp.getCurrentVersion(), launchSide)); 48 | config.put("vmArgs", "-Djava.library.path=${workspaceFolder}/../libraries/natives"); 49 | config.put("projectName", projectName); 50 | List args = TaskRun.getLaunchArgs(mcp, launchSide); 51 | JSONArray arguments = new JSONArray(); 52 | for(String arg : args) { 53 | arguments.put(arg); 54 | } 55 | config.put("args", arguments); 56 | configurations.put(config); 57 | } 58 | launchJson.put("configurations", configurations); 59 | launchJson.write(writer); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/XMLWriter.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.IOException; 5 | 6 | public class XMLWriter extends BufferedWriter { 7 | 8 | private int indent; 9 | 10 | public XMLWriter(BufferedWriter writer) { 11 | super(writer); 12 | } 13 | 14 | private void appendInd(StringBuilder s) { 15 | for (int i = 0; i < indent; i++) { 16 | s.append("\t"); 17 | } 18 | } 19 | 20 | public void writeln(String s) throws IOException { 21 | StringBuilder b = new StringBuilder(); 22 | appendInd(b); 23 | b.append(s); 24 | super.write(b.toString()); 25 | newLine(); 26 | } 27 | 28 | public void writeAttribute(String attribute) throws IOException { 29 | writeln("<" + attribute + "/>"); 30 | } 31 | 32 | public void startAttribute(String attribute) throws IOException { 33 | writeln("<" + attribute + ">"); 34 | indent++; 35 | } 36 | 37 | public void writeSelfEndingAttribute(String attribute) throws IOException { 38 | writeln("<" + attribute + " />"); 39 | } 40 | 41 | public void closeAttribute(String attribute) throws IOException { 42 | indent--; 43 | writeln(""); 44 | } 45 | 46 | public void stringAttribute(String attribute, String value) throws IOException { 47 | writeln("<" + attribute + ">" + value + ""); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/eclipse/EclipseClasspath.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project.eclipse; 2 | 3 | import org.mcphackers.mcp.MCP; 4 | import org.mcphackers.mcp.MCPPaths; 5 | import org.mcphackers.mcp.tasks.Task; 6 | import org.mcphackers.mcp.tasks.Task.Side; 7 | import org.mcphackers.mcp.tools.project.XMLWriter; 8 | import org.mcphackers.mcp.tools.versions.json.DependDownload; 9 | import org.mcphackers.mcp.tools.versions.json.Rule; 10 | 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class EclipseClasspath { 17 | private final MCP mcp; 18 | private final String projectName; 19 | private final int javaVersion; 20 | private final List dependencies = new ArrayList<>(); 21 | 22 | private final static String[] VM_TYPES = { 23 | "J2SE-1.5", 24 | "JavaSE-1.6", 25 | "JavaSE-1.7", 26 | "JavaSE-1.8" 27 | }; 28 | 29 | private String getStandardVMType() { 30 | if(javaVersion >= 5 && javaVersion <= 8) { 31 | return VM_TYPES[javaVersion - 5]; 32 | } 33 | return "JavaSE-" + javaVersion; 34 | } 35 | 36 | public EclipseClasspath(MCP mcp, String projectName, int javaVersion) { 37 | this.mcp = mcp; 38 | this.projectName = projectName; 39 | this.javaVersion = javaVersion; 40 | } 41 | 42 | public void addDependency(DependDownload dependency) { 43 | this.dependencies.add(dependency); 44 | } 45 | 46 | public void toXML(XMLWriter writer, Task.Side side) throws IOException { 47 | writer.writeln(""); 48 | writer.startAttribute("classpath"); 49 | writer.startAttribute("classpathentry kind=\"src\" path=\"src\""); 50 | writer.startAttribute("attributes"); 51 | writer.writeAttribute("attribute name=\"org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY\" value=\"" + projectName + "/libraries/natives\""); 52 | writer.closeAttribute("attributes"); 53 | writer.closeAttribute("classpathentry"); 54 | // TODO: Implement proper client/server side libraries 55 | if (!side.equals(Side.SERVER)) { 56 | for (DependDownload dependencyDownload : this.dependencies) { 57 | if (Rule.apply(dependencyDownload.rules)) { 58 | String lib = dependencyDownload.getArtifactPath(null); 59 | if(lib == null) { 60 | continue; 61 | } 62 | String src = dependencyDownload.getArtifactPath("sources"); 63 | if (Files.exists(MCPPaths.get(mcp, "libraries/" + lib))) { 64 | if (src != null) { 65 | writer.writeAttribute("classpathentry kind=\"lib\" path=\"libraries/" + lib + "\" sourcepath=\"libraries/" + src + "\""); 66 | } else { 67 | writer.writeAttribute("classpathentry kind=\"lib\" path=\"libraries/" + lib + "\""); 68 | } 69 | } 70 | } 71 | } 72 | } 73 | writer.writeAttribute("classpathentry kind=\"lib\" path=\"jars/deobfuscated.jar\" sourcepath=\"jars/deobfuscated-source.jar\""); 74 | writer.writeAttribute("classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/" + getStandardVMType() + "\""); 75 | writer.writeAttribute("classpathentry kind=\"output\" path=\"output\""); 76 | writer.closeAttribute("classpath"); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/eclipse/EclipsePreferences.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project.eclipse; 2 | 3 | import org.mcphackers.mcp.tools.project.PairWriter; 4 | 5 | import java.io.IOException; 6 | 7 | public class EclipsePreferences { 8 | private String sourceVer; 9 | 10 | public void setSourceVersion(String sourceVer) { 11 | this.sourceVer = sourceVer; 12 | } 13 | 14 | public String getSourceVer() { 15 | return this.sourceVer; 16 | } 17 | 18 | public void toString(PairWriter writer) throws IOException { 19 | writer.writePair("eclipse.preferences.version", 1); 20 | writer.writePair("org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode", "enabled"); 21 | writer.writePair("org.eclipse.jdt.core.compiler.codegen.methodParameters", "do not generate"); 22 | writer.writePair("org.eclipse.jdt.core.compiler.codegen.targetPlatform", this.getSourceVer()); 23 | writer.writePair("org.eclipse.jdt.core.compiler.codegen.unusedLocal", "preserve"); 24 | writer.writePair("org.eclipse.jdt.core.compiler.compliance", this.getSourceVer()); 25 | writer.writePair("org.eclipse.jdt.core.compiler.debug.lineNumber", "generate"); 26 | writer.writePair("org.eclipse.jdt.core.compiler.debug.localVariable", "generate"); 27 | writer.writePair("org.eclipse.jdt.core.compiler.debug.sourceFile", "generate"); 28 | writer.writePair("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "error"); 29 | writer.writePair("org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures", "disabled"); 30 | writer.writePair("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "error"); 31 | writer.writePair("org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures", "warning"); 32 | writer.writePair("org.eclipse.jdt.core.compiler.release", "disabled"); 33 | writer.writePair("org.eclipse.jdt.core.compiler.source", this.getSourceVer()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/eclipse/EclipseProject.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project.eclipse; 2 | 3 | import org.mcphackers.mcp.tools.project.XMLWriter; 4 | 5 | import java.io.IOException; 6 | import java.util.Random; 7 | 8 | public class EclipseProject { 9 | private final String projectName; 10 | 11 | public EclipseProject(String projectName) { 12 | this.projectName = projectName; 13 | } 14 | 15 | public void toXML(XMLWriter writer) throws IOException { 16 | writer.writeln(""); 17 | writer.startAttribute("projectDescription"); 18 | writer.stringAttribute("name", this.projectName); 19 | writer.stringAttribute("comment", ""); 20 | writer.startAttribute("projects"); 21 | writer.closeAttribute("projects"); 22 | writer.startAttribute("buildSpec"); 23 | writer.startAttribute("buildCommand"); 24 | writer.stringAttribute("name", "org.eclipse.jdt.core.javabuilder"); 25 | writer.startAttribute("arguments"); 26 | writer.closeAttribute("arguments"); 27 | writer.closeAttribute("buildCommand"); 28 | writer.closeAttribute("buildSpec"); 29 | writer.startAttribute("natures"); 30 | writer.stringAttribute("nature", "org.eclipse.jdt.core.javanature"); 31 | writer.closeAttribute("natures"); 32 | writer.startAttribute("linkedResources"); 33 | writer.startAttribute("link"); 34 | writer.stringAttribute("name", "libraries"); 35 | writer.stringAttribute("type", "2"); 36 | writer.stringAttribute("locationURI", "$%7BPARENT-1-PROJECT_LOC%7D/libraries"); 37 | writer.closeAttribute("link"); 38 | writer.closeAttribute("linkedResources"); 39 | // Filter out src and jars 40 | long id = new Random().nextLong(); 41 | writer.startAttribute("filteredResources"); 42 | // Broken for VSCode even though it's supposedly eclipse-project compatible 43 | // String[] matches = {"src", "jars", "source"}; 44 | // for (String match : matches) { 45 | // writer.startAttribute("filter"); 46 | // writer.stringAttribute("id", Long.toString(id++)); 47 | // writer.stringAttribute("name", ""); 48 | // writer.stringAttribute("type", "9"); 49 | // writer.startAttribute("matcher"); 50 | // writer.stringAttribute("id", "org.eclipse.ui.ide.multiFilter"); 51 | // writer.stringAttribute("arguments", "1.0-name-matches-false-false-" + match); 52 | // writer.closeAttribute("matcher"); 53 | // writer.closeAttribute("filter"); 54 | // } 55 | writer.closeAttribute("filteredResources"); 56 | writer.closeAttribute("projectDescription"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/project/eclipse/EclipseRunConfig.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.project.eclipse; 2 | 3 | import org.mcphackers.mcp.MCP; 4 | import org.mcphackers.mcp.tasks.Task; 5 | import org.mcphackers.mcp.tasks.TaskRun; 6 | import org.mcphackers.mcp.tools.project.XMLWriter; 7 | 8 | import java.io.IOException; 9 | 10 | public class EclipseRunConfig { 11 | private final MCP mcp; 12 | private String projectName; 13 | private Task.Side launchSide; 14 | private String clientArgs; 15 | 16 | public EclipseRunConfig(MCP mcp) { 17 | this.mcp = mcp; 18 | } 19 | 20 | public void setProjectName(String projectName) { 21 | this.projectName = projectName; 22 | } 23 | 24 | public String getProjectName() { 25 | return this.projectName; 26 | } 27 | 28 | public void setLaunchSide(Task.Side launchSide) { 29 | this.launchSide = launchSide; 30 | } 31 | 32 | public Task.Side getLaunchSide() { 33 | return this.launchSide; 34 | } 35 | 36 | public void setClientArgs(String clientArgs) { 37 | this.clientArgs = clientArgs; 38 | } 39 | 40 | public String getClientArgs() { 41 | return this.clientArgs; 42 | } 43 | 44 | public void toXML(XMLWriter writer) throws IOException { 45 | writer.writeln(""); 46 | writer.startAttribute("launchConfiguration type=\"org.eclipse.jdt.launching.localJavaApplication\""); 47 | writer.startAttribute("listAttribute key=\"org.eclipse.debug.core.MAPPED_RESOURCE_PATHS\""); 48 | writer.writeAttribute("listEntry value=\"/" + this.getProjectName() + "\""); 49 | writer.closeAttribute("listAttribute"); 50 | writer.startAttribute("listAttribute key=\"org.eclipse.debug.core.MAPPED_RESOURCE_TYPES\""); 51 | writer.writeAttribute("listEntry value=\"4\""); 52 | writer.closeAttribute("listAttribute"); 53 | writer.startAttribute("listAttribute key=\"org.eclipse.debug.ui.favoriteGroups\""); 54 | writer.writeAttribute("listEntry value=\"org.eclipse.debug.ui.launchGroup.run\""); 55 | writer.writeAttribute("listEntry value=\"org.eclipse.debug.ui.launchGroup.debug\""); 56 | writer.closeAttribute("listAttribute"); 57 | writer.writeAttribute("booleanAttribute key=\"org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE\" value=\"false\""); 58 | writer.writeAttribute("booleanAttribute key=\"org.eclipse.jdt.launching.ATTR_SHOW_CODEDETAILS_IN_EXCEPTION_MESSAGES\" value=\"true\""); 59 | writer.writeAttribute("booleanAttribute key=\"org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD\" value=\"true\""); 60 | writer.writeAttribute("stringAttribute key=\"org.eclipse.jdt.launching.MAIN_TYPE\" value=\"" + TaskRun.getMain(mcp, mcp.getCurrentVersion(), this.getLaunchSide()) + "\""); 61 | writer.writeAttribute("stringAttribute key=\"org.eclipse.jdt.launching.MODULE_NAME\" value=\"" + this.getProjectName() + "\""); 62 | if (launchSide == Task.Side.CLIENT) { 63 | writer.writeAttribute("stringAttribute key=\"org.eclipse.jdt.launching.PROGRAM_ARGUMENTS\" value=\"" + this.getClientArgs() + "\""); 64 | } 65 | writer.writeAttribute("stringAttribute key=\"org.eclipse.jdt.launching.PROJECT_ATTR\" value=\"" + this.getProjectName() + "\""); 66 | writer.closeAttribute("launchConfiguration"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/source/Source.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.source; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.StandardCharsets; 5 | import java.nio.file.FileVisitResult; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.SimpleFileVisitor; 9 | import java.nio.file.attribute.BasicFileAttributes; 10 | import java.util.ArrayList; 11 | import java.util.Comparator; 12 | import java.util.List; 13 | import java.util.Set; 14 | import java.util.Stack; 15 | import java.util.function.Function; 16 | import java.util.regex.MatchResult; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | public abstract class Source { 21 | 22 | public static final Pattern PACKAGE = Pattern.compile("package ([.*\\w]+);(\\r|)\\n"); 23 | public static final Pattern IMPORT = Pattern.compile("import ([.*\\w]+);((\\r|)\\n)+"); 24 | 25 | public static void modify(Path src, List modify) throws IOException { 26 | Files.walkFileTree(src, new SimpleFileVisitor() { 27 | @Override 28 | public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException { 29 | if (!file.toString().endsWith(".java")) { 30 | return FileVisitResult.CONTINUE; 31 | } 32 | String className = file.toString().substring(0, file.toString().length() - 5); 33 | StringBuilder source = new StringBuilder(new String(Files.readAllBytes(file), StandardCharsets.UTF_8)); 34 | for (Source srcModify : modify) { 35 | srcModify.apply(className, source); 36 | } 37 | Files.write(file, source.toString().getBytes()); 38 | return FileVisitResult.CONTINUE; 39 | } 40 | }); 41 | } 42 | 43 | private static boolean addMatch(StringBuilder source, Pattern pattern, String stringToAdd, boolean after, boolean onlyFirst) { 44 | Stack results = new Stack<>(); 45 | String sourceString = source.toString(); 46 | Matcher matcher = pattern.matcher(sourceString); 47 | 48 | while (matcher.find()) { 49 | MatchResult match = matcher.toMatchResult(); 50 | results.push(match); 51 | if (onlyFirst) { 52 | break; 53 | } 54 | } 55 | boolean result = !results.isEmpty(); 56 | while (!results.isEmpty()) { 57 | MatchResult match = results.pop(); 58 | if (after && match.end() >= 0) { 59 | source.insert(match.end(), stringToAdd); 60 | } else if (!after && match.start() >= 0) { 61 | source.insert(match.start(), stringToAdd); 62 | } 63 | } 64 | return result; 65 | } 66 | 67 | public abstract void apply(String className, StringBuilder source); 68 | 69 | protected void updateImports(StringBuilder source, Set imports) { 70 | replaceTextOfMatchGroup(source, IMPORT, match -> { 71 | // Add import to the set 72 | imports.add(match.group(1)); 73 | // Remove import from source 74 | return ""; 75 | }); 76 | List importsList = new ArrayList<>(imports); 77 | importsList.sort(Comparator.naturalOrder()); 78 | StringBuilder sb = new StringBuilder(); 79 | String n = System.lineSeparator(); 80 | String lastPkg = ""; 81 | for (String imp : importsList) { 82 | int dot = imp.indexOf('.'); 83 | String pkg = dot == -1 ? imp : imp.substring(0, dot); 84 | if (!pkg.equals(lastPkg)) { 85 | sb.append(n); 86 | } 87 | lastPkg = pkg; 88 | sb.append("import ").append(imp).append(";").append(n); 89 | } 90 | // Re-add all imports 91 | String importsString = sb.toString(); 92 | if (!addAfterFirstMatch(source, PACKAGE, importsString)) { 93 | source.insert(0, importsString); 94 | } 95 | } 96 | 97 | protected String replaceTextOfMatchGroup(String source, Pattern pattern, Function replaceStrategy) { 98 | StringBuilder sb = new StringBuilder(source); 99 | replaceTextOfMatchGroup(sb, pattern, replaceStrategy); 100 | return sb.toString(); 101 | } 102 | 103 | protected void replaceTextOfMatchGroup(StringBuilder source, Pattern pattern, Function replaceStrategy) { 104 | Stack results = new Stack<>(); 105 | String sourceString = source.toString(); 106 | Matcher matcher = pattern.matcher(sourceString); 107 | 108 | while (matcher.find()) { 109 | results.push(matcher.toMatchResult()); 110 | } 111 | while (!results.isEmpty()) { 112 | MatchResult match = results.pop(); 113 | if (match.start() >= 0 && match.end() >= 0) { 114 | source.replace(match.start(), match.end(), replaceStrategy.apply(match)); 115 | } 116 | } 117 | } 118 | 119 | protected boolean addAfterFirstMatch(StringBuilder source, Pattern pattern, String stringToAdd) { 120 | return addMatch(source, pattern, stringToAdd, true, true); 121 | } 122 | 123 | protected boolean addBeforeFirstMatch(StringBuilder source, Pattern pattern, String stringToAdd) { 124 | return addMatch(source, pattern, stringToAdd, false, true); 125 | } 126 | 127 | protected boolean addAfterMatch(StringBuilder source, Pattern pattern, String stringToAdd) { 128 | return addMatch(source, pattern, stringToAdd, true, false); 129 | } 130 | 131 | protected boolean addBeforeMatch(StringBuilder source, Pattern pattern, String stringToAdd) { 132 | return addMatch(source, pattern, stringToAdd, false, false); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/DownloadData.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions; 2 | 3 | import java.io.IOException; 4 | import java.net.URL; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Map.Entry; 10 | 11 | import org.json.JSONObject; 12 | import org.mcphackers.mcp.DownloadListener; 13 | import org.mcphackers.mcp.MCP; 14 | import org.mcphackers.mcp.MCPPaths; 15 | import org.mcphackers.mcp.tasks.Task.Side; 16 | import org.mcphackers.mcp.tools.FileUtil; 17 | import org.mcphackers.mcp.tools.Util; 18 | import org.mcphackers.mcp.tools.versions.json.AssetIndex; 19 | import org.mcphackers.mcp.tools.versions.json.AssetIndex.Asset; 20 | import org.mcphackers.mcp.tools.versions.json.DependDownload; 21 | import org.mcphackers.mcp.tools.versions.json.Rule; 22 | import org.mcphackers.mcp.tools.versions.json.Version; 23 | 24 | public class DownloadData { 25 | 26 | private final Path gameDir; 27 | public int totalSize; 28 | protected List downloadQueue = new ArrayList<>(); 29 | protected AssetIndex assets; 30 | 31 | public DownloadData(MCP mcp, Version version) { 32 | this(MCPPaths.get(mcp, MCPPaths.LIB), MCPPaths.get(mcp, MCPPaths.JARS), MCPPaths.get(mcp, MCPPaths.JAR_ORIGINAL, Side.CLIENT), MCPPaths.get(mcp, MCPPaths.JAR_ORIGINAL, Side.SERVER), version); 33 | } 34 | 35 | public DownloadData(Path libraries, Path gameDir, Path client, Path server, Version version) { 36 | this.gameDir = gameDir; 37 | queueDownload(version.downloads.artifacts.get("client"), client); 38 | queueDownload(version.downloads.artifacts.get("server"), server); 39 | for (DependDownload dependencyDownload : version.libraries) { 40 | if (Rule.apply(dependencyDownload.rules)) { 41 | queueDownload(dependencyDownload.getDownload(null), libraries); 42 | queueDownload(dependencyDownload.getDownload(dependencyDownload.getNatives()), libraries); 43 | queueDownload(dependencyDownload.getDownload("sources"), libraries); 44 | } 45 | } 46 | try { 47 | Path assetIndex = gameDir.resolve("assets/indexes/" + version.assets + ".json"); 48 | String assetIndexString; 49 | if (!Files.exists(assetIndex) || !version.assetIndex.sha1.equals(Util.getSHA1(assetIndex))) { 50 | assetIndexString = new String(Util.readAllBytes(FileUtil.openURLStream(new URL(version.assetIndex.url)))); 51 | Files.write(assetIndex, assetIndexString.getBytes()); 52 | } else { 53 | assetIndexString = new String(Files.readAllBytes(assetIndex)); 54 | } 55 | setAssets(AssetIndex.from(new JSONObject(assetIndexString))); 56 | } catch (IOException ignored) { 57 | } 58 | } 59 | 60 | public static List getLibraries(Version version) { 61 | List retList = new ArrayList<>(); 62 | for (DependDownload dependencyDownload : version.libraries) { 63 | if (Rule.apply(dependencyDownload.rules)) { 64 | String lib = dependencyDownload.getArtifactPath(null); 65 | retList.add(lib); 66 | } 67 | } 68 | return retList; 69 | } 70 | 71 | public static List getLibraries(Path libDir, Version version) { 72 | List retList = new ArrayList<>(); 73 | for (DependDownload dependencyDownload : version.libraries) { 74 | if (Rule.apply(dependencyDownload.rules)) { 75 | String lib = dependencyDownload.getArtifactPath(null); 76 | if(lib == null) { 77 | continue; 78 | } 79 | retList.add((libDir.resolve(lib))); 80 | } 81 | } 82 | return retList; 83 | } 84 | 85 | public static List getNatives(Path libDir, Version version) { 86 | List retList = new ArrayList<>(); 87 | for (DependDownload dependencyDownload : version.libraries) { 88 | if (Rule.apply(dependencyDownload.rules)) { 89 | String natives = dependencyDownload.getNatives(); 90 | if (natives != null) { 91 | String lib = dependencyDownload.getArtifactPath(natives); 92 | if(lib == null) { 93 | continue; 94 | } 95 | retList.add(libDir.resolve(lib)); 96 | } 97 | } 98 | } 99 | return retList; 100 | } 101 | 102 | public void setAssets(AssetIndex assets) { 103 | if (this.assets != null) { 104 | return; 105 | } 106 | this.assets = assets; 107 | Path dir = gameDir.resolve(assets.map_to_resources ? "resources/" : "assets/objects/"); 108 | for (Entry entry : assets.objects.entrySet()) { 109 | queueDownload(entry.getValue(), dir); 110 | } 111 | } 112 | 113 | public void queueDownload(IDownload dl, Path baseDir) { 114 | if (dl == null) { 115 | return; 116 | } 117 | totalSize += dl.downloadSize(); 118 | downloadQueue.add(new Download(dl, baseDir)); 119 | } 120 | 121 | public void performDownload(DownloadListener listener) throws IOException { 122 | for (Download dl : downloadQueue) { 123 | IDownload download = dl.download; 124 | Path baseDir = dl.dir; 125 | String path = download.downloadPath(); 126 | // if downloadPath is null then baseDir is the location of downloaded file. 127 | Path file = path == null ? baseDir : baseDir.resolve(path); 128 | listener.notify(dl.download, totalSize); 129 | if (!Files.exists(file) || (download.verify() && !download.downloadHash().equals(Util.getSHA1(file)))) { 130 | Path parent = file.getParent(); 131 | if (parent != null) Files.createDirectories(parent); 132 | FileUtil.downloadFile(download.downloadURL(), file); 133 | } 134 | } 135 | } 136 | 137 | private static class Download { 138 | IDownload download; 139 | Path dir; 140 | 141 | public Download(IDownload dl, Path path) { 142 | download = dl; 143 | dir = path; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/IDownload.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions; 2 | 3 | public interface IDownload { 4 | 5 | String downloadPath(); 6 | 7 | String downloadURL(); 8 | 9 | long downloadSize(); 10 | 11 | String downloadHash(); 12 | 13 | boolean verify(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/VersionParser.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions; 2 | 3 | import java.io.InputStream; 4 | import java.net.URL; 5 | import java.net.URLConnection; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.time.Instant; 10 | import java.time.format.DateTimeFormatter; 11 | import java.util.ArrayList; 12 | import java.util.Comparator; 13 | import java.util.List; 14 | 15 | import org.json.JSONArray; 16 | import org.json.JSONObject; 17 | import org.mcphackers.mcp.tools.JSONUtil; 18 | import org.mcphackers.mcp.tools.versions.json.VersionMetadata; 19 | 20 | public class VersionParser { 21 | 22 | public static String mappingsJson = "https://mcphackers.github.io/versionsV2/versions.json"; 23 | private static VersionParser INSTANCE; 24 | 25 | private final List versions = new ArrayList<>(); 26 | public Exception failureCause; 27 | 28 | protected VersionParser() { 29 | JSONArray json; 30 | try { 31 | json = getJson(); 32 | } catch (Exception e) { 33 | failureCause = e; 34 | return; // Couldn't init json 35 | } 36 | for (Object j : json) { 37 | if (!(j instanceof JSONObject)) { 38 | continue; 39 | } 40 | try { 41 | VersionData data = VersionData.from((JSONObject) j); 42 | versions.add(data); 43 | } catch (Exception e) { 44 | // Catching exception will skip to the next version 45 | e.printStackTrace(); 46 | } 47 | } 48 | versions.sort(new VersionSorter()); 49 | INSTANCE = this; 50 | } 51 | 52 | public static VersionParser getInstance() { 53 | if (INSTANCE == null) { 54 | return new VersionParser(); 55 | } 56 | return INSTANCE; 57 | } 58 | 59 | private static JSONArray getJson() throws Exception { 60 | InputStream in; 61 | Path versions = Paths.get("versions.json"); 62 | if (Files.exists(versions)) { 63 | in = Files.newInputStream(versions); 64 | } else { 65 | URLConnection connect = new URL(mappingsJson).openConnection(); 66 | connect.setConnectTimeout(30000); 67 | in = connect.getInputStream(); 68 | } 69 | return JSONUtil.parseJSONArray(in); 70 | } 71 | 72 | /** 73 | * Returns version data from version id/name 74 | * 75 | * @param id 76 | * @return VersionData 77 | */ 78 | public VersionData getVersion(String id) { 79 | for (VersionData data : versions) { 80 | if (data.id.equals(id)) { 81 | return data; 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | /** 88 | * @return All cached VersionData 89 | */ 90 | public List getVersions() { 91 | return versions; 92 | } 93 | 94 | public static class VersionData extends VersionMetadata { 95 | public String resources; 96 | 97 | public static VersionData from(JSONObject obj) { 98 | if (obj == null) { 99 | return null; 100 | } 101 | return new VersionData() { 102 | { 103 | id = obj.getString("id"); 104 | time = obj.getString("time"); 105 | releaseTime = obj.getString("releaseTime"); 106 | type = obj.getString("type"); 107 | url = obj.getString("url"); 108 | resources = obj.optString("resources", null); 109 | } 110 | }; 111 | } 112 | 113 | @Override 114 | public String toString() { 115 | String typ; 116 | String ver; 117 | if (id.startsWith("rd") && "old_alpha".equals(type)) { 118 | typ = "Pre-Classic"; 119 | ver = id; 120 | } else if (id.startsWith("c") && "old_alpha".equals(type)) { 121 | typ = "Classic"; 122 | ver = id.substring(1); 123 | } else if (id.startsWith("inf-")) { 124 | typ = "Infdev"; 125 | ver = id.substring(4); 126 | } else if (id.startsWith("in-")) { 127 | typ = "Indev"; 128 | ver = id.substring(3); 129 | } else if (id.startsWith("a") && "old_alpha".equals(type)) { 130 | typ = "Alpha"; 131 | ver = id.substring(1); 132 | } else if (id.startsWith("b")) { 133 | typ = "Beta"; 134 | ver = id.substring(1); 135 | } else { 136 | typ = type.substring(0, 1).toUpperCase() + type.substring(1); 137 | ver = id; 138 | } 139 | return typ + " " + ver; 140 | } 141 | } 142 | 143 | /** 144 | * Sorts versions by date 145 | */ 146 | public static class VersionSorter implements Comparator { 147 | 148 | @Override 149 | public int compare(VersionData t1, VersionData t2) { 150 | try { 151 | Instant i1 = Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse(t1.releaseTime)); 152 | Instant i2 = Instant.from(DateTimeFormatter.ISO_DATE_TIME.parse(t2.releaseTime)); 153 | return i2.compareTo(i1); 154 | } catch (Exception e) { 155 | return -1; 156 | } 157 | } 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/Artifact.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import org.json.JSONObject; 4 | import org.mcphackers.mcp.tools.versions.IDownload; 5 | 6 | public class Artifact implements IDownload { 7 | public String path; 8 | public String name; 9 | public String sha1; 10 | public String url; 11 | public long size; 12 | 13 | public static Artifact from(JSONObject obj, String nameStr) { 14 | if (obj == null) { 15 | return null; 16 | } 17 | return new Artifact() { 18 | { 19 | name = nameStr; 20 | path = obj.optString("path", null); 21 | sha1 = obj.optString("sha1", null); 22 | url = obj.optString("url", null); 23 | size = obj.optLong("size"); 24 | } 25 | }; 26 | } 27 | 28 | @Override 29 | public String downloadPath() { 30 | return path; 31 | } 32 | 33 | @Override 34 | public String downloadURL() { 35 | return url; 36 | } 37 | 38 | @Override 39 | public long downloadSize() { 40 | return size; 41 | } 42 | 43 | @Override 44 | public String downloadHash() { 45 | return sha1; 46 | } 47 | 48 | @Override 49 | public boolean verify() { 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/AssetIndex.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | import org.json.JSONObject; 7 | import org.mcphackers.mcp.tools.versions.IDownload; 8 | 9 | public class AssetIndex { 10 | public Map objects = new LinkedHashMap<>(); 11 | public boolean virtual; 12 | public boolean map_to_resources; 13 | 14 | public static AssetIndex from(JSONObject obj) { 15 | if (obj == null) { 16 | return null; 17 | } 18 | return new AssetIndex() { 19 | { 20 | virtual = obj.optBoolean("virtual"); 21 | map_to_resources = obj.optBoolean("map_to_resources"); 22 | JSONObject obj2 = obj.optJSONObject("objects"); 23 | if (obj2 != null) { 24 | for (String s : obj2.keySet()) { 25 | objects.put(s, assetFrom(obj2.getJSONObject(s))); 26 | } 27 | } 28 | } 29 | }; 30 | } 31 | 32 | public Asset assetFrom(JSONObject obj) { 33 | if (obj == null) { 34 | return null; 35 | } 36 | return new Asset() { 37 | { 38 | hash = obj.getString("hash"); 39 | size = obj.getLong("size"); 40 | url = obj.optString("url", null); 41 | // reconstruct = obj.optBoolean("reconstruct"); 42 | // compressedHash = obj.optString("compressedHash", null); 43 | // compressedSize = obj.optLong("compressedSize"); 44 | } 45 | }; 46 | } 47 | 48 | public static class Asset implements IDownload { 49 | public String hash; 50 | public String url; 51 | public long size; 52 | // public boolean reconstruct; 53 | // public String compressedHash; 54 | // public long compressedSize; 55 | 56 | @Override 57 | public String downloadURL() { 58 | return url != null ? url : "https://resources.download.minecraft.net/" + downloadPath(); 59 | } 60 | 61 | @Override 62 | public long downloadSize() { 63 | return size; 64 | } 65 | 66 | @Override 67 | public String downloadPath() { 68 | return hash.substring(0, 2) + "/" + hash; 69 | } 70 | 71 | @Override 72 | public String downloadHash() { 73 | return hash; 74 | } 75 | 76 | @Override 77 | public boolean verify() { 78 | return true; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/AssetIndexMeta.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import org.json.JSONObject; 4 | 5 | public class AssetIndexMeta { 6 | public String id; 7 | public String sha1; 8 | public long size; 9 | public long totalSize; 10 | public String url; 11 | 12 | public static AssetIndexMeta from(JSONObject obj) { 13 | if (obj == null) { 14 | return null; 15 | } 16 | return new AssetIndexMeta() { 17 | { 18 | id = obj.getString("id"); 19 | sha1 = obj.getString("sha1"); 20 | size = obj.getLong("size"); 21 | totalSize = obj.getLong("totalSize"); 22 | url = obj.getString("url"); 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/Classifiers.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.json.JSONObject; 7 | 8 | public class Classifiers { 9 | public Map artifacts = new HashMap<>(); 10 | 11 | public static Classifiers from(JSONObject obj) { 12 | if (obj == null) { 13 | return null; 14 | } 15 | return new Classifiers() { 16 | { 17 | for(String key : obj.keySet()) { 18 | artifacts.put(key, getArtifact(obj, key)); 19 | } 20 | } 21 | }; 22 | } 23 | 24 | private static Artifact getArtifact(JSONObject root, String name) { 25 | return Artifact.from(root.optJSONObject(name), name); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/DependDownload.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.json.JSONArray; 7 | import org.json.JSONObject; 8 | import org.mcphackers.mcp.tools.OS; 9 | import org.mcphackers.mcp.tools.versions.IDownload; 10 | 11 | public class DependDownload { 12 | public Downloads downloads; 13 | public String name; 14 | public String url; 15 | public List rules = new ArrayList<>(); 16 | 17 | public static DependDownload from(JSONObject obj) { 18 | if (obj == null) { 19 | return null; 20 | } 21 | return new DependDownload() { 22 | { 23 | name = obj.getString("name"); 24 | url = obj.optString("url", null); 25 | downloads = Downloads.from(obj.optJSONObject("downloads")); 26 | JSONArray a = obj.optJSONArray("rules"); 27 | if (a != null) { 28 | for (Object o : a) { 29 | rules.add(Rule.from((JSONObject) o)); 30 | } 31 | } 32 | } 33 | }; 34 | } 35 | 36 | private static String getLibPath(String name, String classifierSuffix) { 37 | String[] artifact = name.split(":"); 38 | return artifact[0].replace('.', '/') + "/" + artifact[1] + "/" + artifact[2] + "/" + artifact[1] + "-" + artifact[2] + (classifierSuffix == null ? "" : ("-" + classifierSuffix)) + ".jar"; 39 | } 40 | 41 | public String getArtifactURL(String artifactName) { 42 | Artifact artifact = getArtifact(artifactName); 43 | if(artifact == null) { 44 | return null; 45 | } 46 | if(artifact.url != null) { 47 | return artifact.url; 48 | } 49 | String urlBase = "https://libraries.minecraft.net/"; 50 | if(url != null) { 51 | urlBase = url.codePointAt(url.length() - 1) == '/' ? url : url + "/"; 52 | } 53 | return urlBase + getLibPath(name, artifact.name); 54 | } 55 | 56 | public IDownload getDownload(String artifactName) { 57 | final Artifact artifact = getArtifact(artifactName); 58 | if(artifact == null) { 59 | return null; 60 | } 61 | final String url = getArtifactURL(artifactName); 62 | final String path = getArtifactPath(artifactName); 63 | return new IDownload() { 64 | 65 | @Override 66 | public String downloadPath() { 67 | return path; 68 | } 69 | 70 | @Override 71 | public String downloadURL() { 72 | return url; 73 | } 74 | 75 | @Override 76 | public long downloadSize() { 77 | return artifact.size; 78 | } 79 | 80 | @Override 81 | public String downloadHash() { 82 | return artifact.sha1; 83 | } 84 | 85 | @Override 86 | public boolean verify() { 87 | return true; 88 | } 89 | 90 | }; 91 | } 92 | 93 | public String getArtifactPath(String artifactName) { 94 | Artifact artifact = getArtifact(artifactName); 95 | if(artifact == null) { 96 | return null; 97 | } 98 | if(artifact.path != null) { 99 | return artifact.path; 100 | } 101 | return getLibPath(name, artifact.name); 102 | } 103 | 104 | private Artifact getArtifact(String name) { 105 | if(name == null) { 106 | return downloads == null ? null : downloads.artifact; 107 | } 108 | return (downloads == null || downloads.classifiers == null) ? null : downloads.classifiers.artifacts.get(name); 109 | } 110 | 111 | public String getNatives() { 112 | switch (OS.getOs()) { 113 | case windows: 114 | if (getArtifact("natives-windows") != null) { 115 | return "natives-windows"; 116 | } 117 | break; 118 | case linux: 119 | if (getArtifact("natives-linux") != null) { 120 | return "natives-linux"; 121 | } 122 | break; 123 | case osx: 124 | if (getArtifact("natives-osx") != null) { 125 | return "natives-osx"; 126 | } 127 | if (getArtifact("natives-macos") != null) { 128 | return "natives-macos"; 129 | } 130 | break; 131 | default: 132 | break; 133 | } 134 | return null; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/Downloads.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import org.json.JSONObject; 4 | 5 | public class Downloads { 6 | public Artifact artifact; 7 | public Classifiers classifiers; 8 | 9 | public static Downloads from(JSONObject obj) { 10 | if (obj == null) { 11 | return null; 12 | } 13 | return new Downloads() { 14 | { 15 | artifact = Artifact.from(obj.optJSONObject("artifact"), null); 16 | classifiers = Classifiers.from(obj.optJSONObject("classifiers")); 17 | } 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/Manifest.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | public class Manifest { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/Rule.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Map.Entry; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | import org.json.JSONObject; 12 | import org.mcphackers.mcp.tools.OS; 13 | 14 | public class Rule { 15 | public Action action; 16 | public OSInfo os; 17 | public Map features = new HashMap<>(); 18 | 19 | public static Rule from(JSONObject obj) { 20 | if (obj == null) { 21 | return null; 22 | } 23 | return new Rule() { 24 | { 25 | action = Action.valueOf(obj.getString("action")); 26 | os = OSInfo.from(obj.optJSONObject("os")); 27 | JSONObject obj2 = obj.optJSONObject("features"); 28 | if (obj2 != null) { 29 | for (String s : obj2.keySet()) { 30 | features.put(s, obj2.getBoolean(s)); 31 | } 32 | } 33 | } 34 | }; 35 | } 36 | 37 | public static boolean apply(List rules) { 38 | return apply(rules, Collections.emptyList()); 39 | } 40 | 41 | public static boolean apply(List rules, List features) { 42 | if (rules != null && !rules.isEmpty()) { 43 | Rule.Action lastAction = Rule.Action.disallow; 44 | 45 | for (Rule compatibilityRule : rules) { 46 | Rule.Action action = compatibilityRule.getAppliedAction(features); 47 | if (action != null) { 48 | lastAction = action; 49 | } 50 | } 51 | return lastAction == Rule.Action.allow; 52 | } else { 53 | return true; 54 | } 55 | } 56 | 57 | public Rule.Action getAppliedAction(List featuresList) { 58 | boolean featuresMatch; 59 | for (Entry entry : features.entrySet()) { 60 | featuresMatch = featuresList.contains(entry.getKey()) == entry.getValue(); 61 | if (!featuresMatch) return Rule.Action.disallow; 62 | } 63 | return this.os != null && !this.os.equalsOS(OS.getOs()) ? null : this.action; 64 | } 65 | 66 | public enum Action { 67 | allow, 68 | disallow 69 | } 70 | 71 | public static class OSInfo { 72 | public OS name; 73 | public String version; 74 | 75 | public static OSInfo from(JSONObject obj) { 76 | if (obj == null) { 77 | return null; 78 | } 79 | return new OSInfo() { 80 | { 81 | name = OS.valueOf(obj.getString("name")); 82 | version = obj.optString("version"); 83 | } 84 | }; 85 | } 86 | 87 | public boolean equalsOS(OS os) { 88 | if (this.name != null && this.name != os) { 89 | return false; 90 | } else { 91 | if (this.version != null) { 92 | try { 93 | Pattern pattern = Pattern.compile(this.version); 94 | Matcher matcher = pattern.matcher(System.getProperty("os.version")); 95 | if (!matcher.matches()) { 96 | return false; 97 | } 98 | } catch (Throwable ignored) { 99 | } 100 | } 101 | 102 | return true; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/Version.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.json.JSONObject; 7 | 8 | /** 9 | * Deserialized version JSON 10 | */ 11 | public class Version { 12 | 13 | public AssetIndexMeta assetIndex; 14 | public String assets; 15 | public Classifiers downloads; 16 | public String id; 17 | public String time; 18 | public String releaseTime; 19 | public String type; 20 | public List libraries; 21 | //public Logging logging; 22 | public String mainClass; 23 | public String minecraftArguments; 24 | public Arguments arguments; 25 | 26 | public static Version from(JSONObject obj) { 27 | if (obj == null) { 28 | return null; 29 | } 30 | return new Version() { 31 | { 32 | assetIndex = AssetIndexMeta.from(obj.getJSONObject("assetIndex")); 33 | assets = obj.getString("assets"); 34 | downloads = Classifiers.from(obj.getJSONObject("downloads")); 35 | id = obj.getString("id"); 36 | time = obj.getString("time"); 37 | releaseTime = obj.getString("releaseTime"); 38 | type = obj.getString("type"); 39 | libraries = new ArrayList<>(); 40 | for (Object o : obj.getJSONArray("libraries")) { 41 | if (o instanceof JSONObject) { 42 | libraries.add(DependDownload.from((JSONObject) o)); 43 | } 44 | } 45 | mainClass = obj.getString("mainClass"); 46 | minecraftArguments = obj.optString("minecraftArguments", null); 47 | } 48 | }; 49 | } 50 | 51 | public static class Arguments { 52 | public List game; 53 | public List jvm; 54 | 55 | public static Arguments from(JSONObject obj) { 56 | if (obj == null) { 57 | return null; 58 | } 59 | return new Arguments() { 60 | { 61 | for (Object o : obj.getJSONArray("game")) { 62 | if (o instanceof JSONObject) { 63 | game.add(Argument.from((JSONObject) o)); 64 | } 65 | game.add(o); 66 | } 67 | for (Object o : obj.getJSONArray("jvm")) { 68 | if (o instanceof JSONObject) { 69 | jvm.add(Argument.from((JSONObject) o)); 70 | } 71 | jvm.add(o); 72 | } 73 | } 74 | }; 75 | } 76 | } 77 | 78 | public static class Argument { 79 | public List rules; 80 | public Object value; 81 | 82 | public static Argument from(JSONObject obj) { 83 | if (obj == null) { 84 | return null; 85 | } 86 | return new Argument() { 87 | { 88 | for (Object o : obj.getJSONArray("rules")) { 89 | rules.add(Rule.from((JSONObject) o)); 90 | } 91 | value = obj.get("value"); 92 | } 93 | }; 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/mcphackers/mcp/tools/versions/json/VersionMetadata.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tools.versions.json; 2 | 3 | import org.json.JSONObject; 4 | 5 | public class VersionMetadata { 6 | public String id; 7 | public String type; 8 | public String time; 9 | public String releaseTime; 10 | public String url; 11 | 12 | 13 | public static VersionMetadata from(JSONObject obj) { 14 | if (obj == null) { 15 | return null; 16 | } 17 | return new VersionMetadata() { 18 | { 19 | id = obj.getString("id"); 20 | time = obj.getString("time"); 21 | releaseTime = obj.getString("releaseTime"); 22 | type = obj.getString("type"); 23 | url = obj.getString("url"); 24 | } 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/icon/rmcp.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCPHackers/RetroMCP-Java/fd1c4e0cb98176631ce17141fe91610dfc82240e/src/main/resources/icon/rmcp.ico -------------------------------------------------------------------------------- /src/main/resources/icon/rmcp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCPHackers/RetroMCP-Java/fd1c4e0cb98176631ce17141fe91610dfc82240e/src/main/resources/icon/rmcp.png -------------------------------------------------------------------------------- /src/main/resources/lang/cs_CZ.lang: -------------------------------------------------------------------------------- 1 | language = Čeština 2 | mcp.needJDK = K rekompilaci je potřeba Java Development Kit! 3 | mcp.error = Chyba 4 | mcp.newVersion = Nová verze nalezena: 5 | mcp.confirmExit = Jste si jistí že chcete aplikaci ukončit ? Některé úlohy stále běží! 6 | mcp.confirmUpdate = Jste si jistí že chcete provést aktualizaci? 7 | mcp.confirmUpdateMD5 = Jste si jistí že chcete regenerovat originální hashe? 8 | mcp.confirmAction = Potvrdit Akci 9 | mcp.confirmDecompile = Jste si jistí že chcete odstranit zdrojový kód a znovu provést dekompilaci? 10 | mcp.confirmSetup = Jste si jistí že chcete provést nastavení pro zvolenou verzi? 11 | mcp.confirmCleanup = Jste si jistí že chcete odstranit zdrojový kód a aktuální verzi? 12 | mcp.askSourceBackup = Provést zálohu zdrojového kódu? 13 | mcp.incompatiblePlugin = Nalezen nekompatibilní plugin: 14 | mcp.versionList.currentVersion = Aktuální verze: 15 | mcp.versionList.failure = Nepodařilo se získat seznam verzí! 16 | mcp.versionList.loading = Načítání... 17 | mcp.checkUpdate = Zkontrolovat aktualizace 18 | mcp.upToDate = Aktuální! 19 | mcp.viewDir = Zobrazit pracovní složku 20 | mcp.changeDir = Změnit pracovní složku 21 | mcp.enterDir = Zadejte cestu ke složce 22 | mcp.selectDir = Zvolit složku 23 | mcp.moreTasks = Další úlohy... 24 | mcp.github = GitHub Stránka 25 | mcp.wiki = Wiki 26 | mcp.side = Strana 27 | mcp.startClient = Spustit klienta 28 | mcp.startServer = Spustit server 29 | mcp.console = Výstup konzole 30 | 31 | side.client = Klient 32 | side.server = Server 33 | side.merged = Spojené 34 | side.any = Jakákoliv 35 | 36 | options = Možnosti 37 | options.language = Jazyk 38 | options.theme = Téma 39 | options.enterValue = Vložte hodnotu 40 | options.enterValues = Vložte hodnoty 41 | options.enterValues.info = (Oddělte hodnoty čárkami (,)) 42 | options.running = Běh 43 | options.resetDefaults = Obnovit výchozí nastavení 44 | options.invalidValue = Neplatná hodnota! 45 | 46 | options.theme.swing = Swing 47 | options.theme.flatlightlaf = FlatLightLaf 48 | options.theme.flatdarklaf = FlatDarkLaf 49 | options.theme.flatdarculalaf = FlatDarculaLaf 50 | 51 | task.help = Pomoc 52 | task.help.desc = Zobrazí použití příkazů 53 | task.setup = Nastavení 54 | task.setup.desc = Nastavit výchozí verzi pro pracovní prostor 55 | task.setup.selectVersion = Vyber verzi: 56 | task.decompile = Dekompilace 57 | task.decompile.desc = Začne dekompilaci Minecraftu 58 | task.recompile = Rekompilace 59 | task.recompile.desc = Rekompiluje Minecraft 60 | task.reobfuscate = Reobfuskace 61 | task.reobfuscate.desc = Reobfuskuje třídy Minecraftu 62 | task.build = Sestavení 63 | task.build.desc = Sestavit jar nebo zip soubor se změněnými třídami 64 | task.start = Spuštění 65 | task.start.desc = Spustí klienta nebo server se změněnými třídami 66 | task.cleanup = Vyčistit 67 | task.cleanup.desc = Smaže všechen zdrojový kód a třídy 68 | task.exit = Ukončit 69 | task.exit.desc = Ukončí program 70 | task.updatemd5 = Aktualizovat MD5 71 | task.updatemd5.desc = Aktualizuje MD5 hashe používané pro reobfuskaci 72 | task.createpatch = Vytvořit Patch 73 | task.createpatch.desc = Vytvoří patch podle změn ve zdrojovém kódu 74 | task.backupsrc = Zálohovat zdrojový kód 75 | task.backupsrc.desc = Zabalí zdrojový kód do souboru zip 76 | task.applypatch = Aplikovat patch 77 | task.applypatch.desc = Aplikuje patche ze složky patches 78 | task.noDesc = Žádný popis 79 | 80 | task.stage.idle = Nečinný 81 | task.stage.finished = Dokončeno! 82 | task.stage.recompile = Rekompilování 83 | task.stage.decompile = Dekompilování 84 | task.stage.backupsrc = Zálohování 85 | task.stage.reobf = Reobfuskování 86 | task.stage.build = Sestavení 87 | task.stage.setup = Nastavení 88 | task.stage.createpatch = Vytvaření patche 89 | task.stage.prepare = Přípravování 90 | task.stage.patch = Aplikovaní patchů 91 | task.stage.copysrc = Kopírování zdrojového kódu 92 | task.stage.remap = Přemapování JAR 93 | task.stage.rdi = Aplikování RDInjector 94 | task.stage.constants = Záměňování konstant 95 | task.stage.copyres = Kopírování zdrojového kódu 96 | task.stage.gathermd5 = Sbírání MD5 Hashů 97 | task.stage.updatemd5 = Aktualizování MD5 hashů 98 | task.stage.download = Stahování: %s 99 | task.stage.workspace = Nastavování pracovního prostoru 100 | 101 | task.param.debug = Zobrazit další údaje 102 | task.param.side = Nastavit stranu 103 | task.param.src = Vyčistit zdroje 104 | task.param.patch = Aplikovat patche 105 | task.param.ignore = Nastavit ignorované balíčky 106 | task.param.ind = Nastavit odsazovací string 107 | task.param.obf = Obfuskovat třídy modu 108 | task.param.fullbuild = Plné sestavení 109 | task.param.runbuild = Spustit sestavení 110 | task.param.runargs = Argumenty JVM 111 | task.param.gameargs = Herní argumenty 112 | task.param.setup = Nastavit verzi 113 | task.param.source = Nastavit specifickou verzi zdroje 114 | task.param.target = Nastavit specifickou verzi cíle 115 | task.param.javahome = Nastavit JAVA_HOME který je použit při dekompilaci 116 | task.param.override = Přidat @Override 117 | task.param.resources = Nechat si zdroje (resources) 118 | task.param.generics = Odhadnout generika 119 | task.param.stripgenerics = Vyhodit generika 120 | task.param.outputsrc = Výstup zdrojového kódu 121 | 122 | tasks.success = Úspěšně dokončeno! 123 | tasks.warning = Dokončeno s varováními! 124 | tasks.error = Dokončeno s chybami! -------------------------------------------------------------------------------- /src/main/resources/lang/de_DE.lang: -------------------------------------------------------------------------------- 1 | language = Deutsch 2 | mcp.needJDK = Java Development Kit ist für die Neukompilierung erforderlich! 3 | mcp.error = Fehler 4 | mcp.newVersion = Neue Version gefunden: 5 | mcp.confirmExit = Sind Sie sicher, dass Sie beenden wollen? Einige Aufgaben sind noch nicht fertig! 6 | mcp.confirmUpdate = Sind Sie sicher, dass Sie aktualisieren wollen? 7 | mcp.confirmUpdateMD5 = Sind Sie sicher, dass Sie die ursprünglichen Hashes neu generieren wollen? 8 | mcp.confirmAction = Aktion bestätigen 9 | mcp.confirmDecompile = Sind Sie sicher, dass Sie die Quellen löschen und erneut dekompilieren wollen? 10 | mcp.confirmSetup = Sind Sie sicher, dass Sie das Setup für die ausgewählte Version ausführen wollen? 11 | mcp.confirmCleanup = Sind Sie sicher, dass Sie alle Quelldateien löschen und die derzeit aktive Version leeren wollen? 12 | mcp.askSourceBackup = Ein Backup der Quelldateien machen? 13 | mcp.incompatiblePlugin = Inkompatibles Plugin gefunden: 14 | mcp.versionList.currentVersion = Aktuelle Version: 15 | mcp.versionList.failure = Versionsliste kann nicht abgerufen werden! 16 | mcp.versionList.loading = Wird geladen... 17 | mcp.checkUpdate = Auf Aktualisierungen prüfen 18 | mcp.upToDate = Auf dem neuesten Stand! 19 | mcp.viewDir = Arbeitsverzeichnis anzeigen 20 | mcp.changeDir = Arbeitsverzeichnis wechseln 21 | mcp.enterDir = Einen Pfad zu einem Verzeichnis eingeben 22 | mcp.selectDir = Ordner wählen 23 | mcp.moreTasks = Mehr Aufgaben... 24 | mcp.github = GitHub-Seite 25 | mcp.wiki = Wiki 26 | mcp.side = Seite 27 | mcp.startClient = Client starten 28 | mcp.startServer = Server starten 29 | mcp.console = Konsolenausgabe 30 | mcp.clearConsole = Konsolenausgabe leeren 31 | 32 | side.client = Client 33 | side.server = Server 34 | side.merged = Vereint 35 | side.any = Beliebig 36 | 37 | options = Optionen 38 | options.language = Sprache 39 | options.theme = Thema 40 | options.enterValue = Einen Wert eingeben 41 | options.enterValues = Eine Reihe von Werten eingeben 42 | options.enterValues.info = (Werte mit Komma trennen) 43 | options.running = Laufend 44 | options.resetDefaults = Auf Standardwerte zurücksetzen 45 | options.invalidValue = Ungültiger Wert! 46 | 47 | options.theme.swing = Swing 48 | options.theme.flatlightlaf = FlatLightLaf 49 | options.theme.flatdarklaf = FlatDarkLaf 50 | options.theme.flatdarculalaf = FlatDarculaLaf 51 | 52 | task.help = Hilfe 53 | task.help.desc = Zeigt die Verwendung des Befehls an 54 | task.setup = Einrichtung 55 | task.setup.desc = Anfangsarbeitsbereich für eine Version festlegen 56 | task.setup.selectVersion = Version auswählen: 57 | task.decompile = Dekompilieren 58 | task.decompile.desc = Mit der Dekompilierung von Minecraft beginnen 59 | task.recompile = Neu kompilieren 60 | task.recompile.desc = Minecraft Quellen neu kompilieren 61 | task.reobfuscate = Reobfuskieren 62 | task.reobfuscate.desc = Minecraft-Klassen reobfuskieren 63 | task.build = Bauen 64 | task.build.desc = Erzeugt das endgültige jar oder zip 65 | task.start = Starten 66 | task.start.desc = Startet den Client oder Server aus den kompilierten Klassen 67 | task.cleanup = Aufräumen 68 | task.cleanup.desc = Alle Quell- und Klassenordner löschen 69 | task.exit = Beenden 70 | task.exit.desc = Beenden des Programms 71 | task.updatemd5 = MD5 aktualisieren 72 | task.updatemd5.desc = MD5-Hashtabellen aktualisieren, die für die Reobfuskation verwendet werden 73 | task.createpatch = Patch erstellen 74 | task.createpatch.desc = Erzeugt einen Patch auf der Grundlage Ihrer Änderungen am Quelltext 75 | task.backupsrc = Quelldateien sichern 76 | task.backupsrc.desc = Packe Quelldateien in eine zip 77 | task.applypatch = Patch anwenden 78 | task.applypatch.desc = Patch aus dem Patchordner anwenden 79 | task.updatemcp = RetroMCP aktualisieren 80 | task.updatemcp.desc = Nach Aktualisierungen suchen 81 | task.noDesc = Keine Beschreibung angegeben 82 | 83 | task.stage.idle = Leerlauf 84 | task.stage.finished = Beendet! 85 | task.stage.recompile = Neu kompilieren 86 | task.stage.decompile = Dekompilieren 87 | task.stage.backupsrc = Sichern 88 | task.stage.reobf = Reobfuskieren 89 | task.stage.build = Bauen 90 | task.stage.setup = Einrichten 91 | task.stage.createpatch = Patch erstellen 92 | task.stage.prepare = Vorbereiten 93 | task.stage.patch = Patches anwenden 94 | task.stage.copysrc = Kopieren von Quellen 95 | task.stage.remap = JAR neu zuordnen 96 | task.stage.rdi = RDInjector anwenden 97 | task.stage.constants = Ersetzen von Konstanten 98 | task.stage.copyres = Kopieren von Ressourcen 99 | task.stage.gathermd5 = Sammeln von MD5-Hashes 100 | task.stage.updatemd5 = MD5 aktualisieren 101 | task.stage.download = Herunterladen: %s 102 | task.stage.workspace = Arbeitsbereich einrichten 103 | 104 | task.param.debug = Zusätzliche Informationen anzeigen 105 | task.param.side = Seite einstellen 106 | task.param.src = Quelle bereinigen 107 | task.param.patch = Patches anwenden 108 | task.param.ignore = Ignorierte Pakete einstellen 109 | task.param.ff_options = Fernflower-Einstellungen setzen 110 | task.param.obf = Modklassen obfuskieren 111 | task.param.srgobf = Mit Searge-Namen obfuskieren 112 | task.param.excludedclasses = Ausgeschlossene Klassen 113 | task.param.fullbuild = Vollständig bauen 114 | task.param.runbuild = Build ausführen 115 | task.param.runargs = JVM-Argumente 116 | task.param.gameargs = Spiel-Parameter 117 | task.param.setup = Version einrichten 118 | task.param.source = Eine bestimmte Quellversion festlegen 119 | task.param.target = Eine bestimmte Zielversion festlegen 120 | task.param.javahome = JAVA_HOME für die Kompilierung festlegen 121 | task.param.resources = Ressourcen behalten 122 | task.param.generics = Generics erraten 123 | task.param.stripgenerics = Generics entfernen 124 | task.param.outputsrc = Quelldateien ausgeben 125 | 126 | tasks.success = Erfolgreich beendet! 127 | tasks.warning = Mit Warnungen beendet! 128 | tasks.error = Mit Fehlern beendet! 129 | -------------------------------------------------------------------------------- /src/main/resources/lang/en_US.lang: -------------------------------------------------------------------------------- 1 | language = English 2 | mcp.needJDK = Java Development Kit is required to recompile! 3 | mcp.error = Error 4 | mcp.newVersion = New version found: 5 | mcp.confirmExit = Are you sure you want to exit? Some tasks have not finished execution yet! 6 | mcp.confirmUpdate = Are you sure you want to update? 7 | mcp.confirmUpdateMD5 = Are you sure you want to regenerate original hashes? 8 | mcp.confirmAction = Confirm Action 9 | mcp.confirmDecompile = Are you sure you want to delete sources and decompile again? 10 | mcp.confirmSetup = Are you sure you want to run setup for selected version? 11 | mcp.confirmCleanup = Are you sure you want to delete all sources and clear currently active version? 12 | mcp.askSourceBackup = Make a backup of the source files? 13 | mcp.incompatiblePlugin = Incompatible plugin found: 14 | mcp.versionList.currentVersion = Current version: 15 | mcp.versionList.failure = Unable to get version list! 16 | mcp.versionList.loading = Loading... 17 | mcp.checkUpdate = Check for updates 18 | mcp.upToDate = Up to date! 19 | mcp.viewDir = View working directory 20 | mcp.changeDir = Change working directory 21 | mcp.enterDir = Enter a path to a directory 22 | mcp.selectDir = Select folder 23 | mcp.moreTasks = More tasks... 24 | mcp.github = GitHub Page 25 | mcp.wiki = Wiki 26 | mcp.side = Side 27 | mcp.startClient = Launch client 28 | mcp.startServer = Launch server 29 | mcp.console = Console output 30 | mcp.clearConsole = Clear Console 31 | 32 | side.client = Client 33 | side.server = Server 34 | side.merged = Merged 35 | side.any = Any 36 | 37 | options = Options 38 | options.language = Language 39 | options.theme = Theme 40 | options.enterValue = Enter a value 41 | options.enterValues = Enter a set of values 42 | options.enterValues.info = (Separate values with comma) 43 | options.running = Running 44 | options.resetDefaults = Reset to defaults 45 | options.invalidValue = Invalid value! 46 | 47 | options.theme.swing = Swing 48 | options.theme.flatlightlaf = FlatLightLaf 49 | options.theme.flatdarklaf = FlatDarkLaf 50 | options.theme.flatdarculalaf = FlatDarculaLaf 51 | 52 | task.help = Help 53 | task.help.desc = Displays command usage 54 | task.setup = Setup 55 | task.setup.desc = Set initial workspace for a version 56 | task.setup.selectVersion = Select version: 57 | task.decompile = Decompile 58 | task.decompile.desc = Start decompiling Minecraft 59 | task.recompile = Recompile 60 | task.recompile.desc = Recompile Minecraft sources 61 | task.reobfuscate = Reobfuscate 62 | task.reobfuscate.desc = Reobfuscate Minecraft classes 63 | task.build = Build 64 | task.build.desc = Build the jar or a zip with modified classes 65 | task.start = Start 66 | task.start.desc = Run client or server from compiled classes 67 | task.cleanup = Cleanup 68 | task.cleanup.desc = Delete all source and class folders 69 | task.exit = Exit 70 | task.exit.desc = Exit the program 71 | task.updatemd5 = Update MD5 72 | task.updatemd5.desc = Update MD5 hash tables used for reobfuscation 73 | task.createpatch = Create Patch 74 | task.createpatch.desc = Create a patch based off your changes to source 75 | task.backupsrc = Backup source 76 | task.backupsrc.desc = Pack source files into a zip 77 | task.applypatch = Apply patch 78 | task.applypatch.desc = Apply patch from patches directory 79 | task.updatemcp = Update RetroMCP 80 | task.updatemcp.desc = Check for updates 81 | task.noDesc = No description provided 82 | 83 | task.stage.idle = Idle 84 | task.stage.finished = Finished! 85 | task.stage.recompile = Recompiling 86 | task.stage.decompile = Decompiling 87 | task.stage.backupsrc = Backing up 88 | task.stage.reobf = Reobfuscating 89 | task.stage.build = Building 90 | task.stage.setup = Setting up 91 | task.stage.createpatch = Creating patch 92 | task.stage.prepare = Preparing 93 | task.stage.patch = Applying patches 94 | task.stage.copysrc = Copying sources 95 | task.stage.remap = Remapping JAR 96 | task.stage.rdi = Applying RDInjector 97 | task.stage.constants = Replacing constants 98 | task.stage.copyres = Copying resources 99 | task.stage.gathermd5 = Gathering MD5 hashes 100 | task.stage.updatemd5 = Updating MD5 101 | task.stage.download = Downloading: %s 102 | task.stage.workspace = Setting up workspace 103 | 104 | task.param.debug = Display additional info 105 | task.param.side = Set side 106 | task.param.src = Source cleanup 107 | task.param.patch = Apply patches 108 | task.param.ignore = Set ignored packages 109 | task.param.ff_options = Set Fernflower options 110 | task.param.obf = Obfuscate mod classes 111 | task.param.srgobf = Obfuscate using Searge names 112 | task.param.excludedclasses = Excluded classes 113 | task.param.fullbuild = Full build 114 | task.param.runbuild = Run build 115 | task.param.runargs = JVM arguments 116 | task.param.gameargs = Game arguments 117 | task.param.setup = Setup version 118 | task.param.source = Set a specific source version 119 | task.param.target = Set a specific target version 120 | task.param.javahome = Set JAVA_HOME used for compiling 121 | task.param.resources = Keep resources 122 | task.param.generics = Guess generics 123 | task.param.stripgenerics = Strip generics 124 | task.param.outputsrc = Output source 125 | 126 | tasks.success = Finished successfully! 127 | tasks.warning = Finished with warnings! 128 | tasks.error = Finished with errors! 129 | -------------------------------------------------------------------------------- /src/main/resources/lang/es_ES.lang: -------------------------------------------------------------------------------- 1 | language = Español 2 | mcp.needJDK = ¡Necesitas Java Development Kit para compilar! 3 | mcp.error = Error 4 | mcp.newVersion = Nueva versión encontrada: 5 | mcp.confirmExit = ¿Estás seguro? ¡Algunas tareas no han terminado de ejecutarse! 6 | mcp.confirmUpdate = ¿Estás seguro de que quieres actualizar? 7 | mcp.confirmUpdateMD5 = ¿Quieres regenerar los hashes originales? 8 | mcp.confirmAction = Confirmar Acción 9 | mcp.confirmDecompile = ¿Quieres eliminar todo el código y descompilar de nuevo? 10 | mcp.confirmSetup = ¿Quieres preparar un espacio de trabajo para la versión seleccionada? 11 | mcp.confirmCleanup = ¿Quieres eliminar todo el código y limpiar la versión seleccionada? 12 | mcp.askSourceBackup = ¿Hacer una copia de seguridad del código? 13 | mcp.incompatiblePlugin = Plugin incompatible encontrado: 14 | mcp.versionList.currentVersion = Versión seleccionada: 15 | mcp.versionList.failure = Error al obtener la lista de versiones ¡Intentelo más tarde! 16 | mcp.versionList.loading = Cargando... 17 | mcp.checkUpdate = Comprobar nuevas actualizaciones 18 | mcp.upToDate = Usted está ejecutando la última versión de R-MCP. 19 | mcp.viewDir = Ver el directorio de trabajo 20 | mcp.changeDir = Cambiar el directorio de trabajo 21 | mcp.enterDir = Ingrese una ubicación hacia el directorio 22 | mcp.selectDir = Seleccionar carpeta 23 | mcp.moreTasks = Más tareas... 24 | mcp.github = Página de GitHub 25 | mcp.wiki = Wiki 26 | mcp.side = Cliente/Servidor 27 | mcp.startClient = Lanzar cliente 28 | mcp.startServer = Lanzar servidor 29 | mcp.console = Salida de Consola 30 | mcp.clearConsole = Limpiar Consola 31 | 32 | side.client = Cliente 33 | side.server = Servidor 34 | side.merged = Ambos 35 | side.any = Cualquiera 36 | 37 | options = Opciones 38 | options.language = Lenguaje 39 | options.theme = Tema 40 | options.enterValue = Ingrese un valor 41 | options.enterValues = Ingrese varios valores 42 | options.enterValues.info = (Valores separados por coma) 43 | options.running = Ejecutar 44 | options.resetDefaults = Configuración predeterminada 45 | options.invalidValue = ¡Valor inválido! 46 | 47 | options.theme.swing = Swing 48 | options.theme.flatlightlaf = FlatLightLaf 49 | options.theme.flatdarklaf = FlatDarkLaf 50 | options.theme.flatdarculalaf = FlatDarculaLaf 51 | 52 | task.help = Ayuda 53 | task.help.desc = Muestra el uso de los comandos 54 | task.setup = Montar un espacio de trabajo 55 | task.setup.desc = Cambia el espacio de trabajo inicial para una version 56 | task.setup.selectVersion = Selecciona una versión: 57 | task.decompile = Descompilar 58 | task.decompile.desc = Comenzar a descompilar Minecraft 59 | task.recompile = Recompilar 60 | task.recompile.desc = Recompilar código de Minecraft 61 | task.reobfuscate = Reobfuscar 62 | task.reobfuscate.desc = Reobfuscar clases de Minecraft 63 | task.build = Compilar 64 | task.build.desc = Compila el jar o un zip con clases modificadas 65 | task.start = Lanzar 66 | task.start.desc = Ejecuta el cliente o servidor 67 | task.cleanup = Limpiar 68 | task.cleanup.desc = Eliminar todo el código 69 | task.exit = Salir 70 | task.exit.desc = Salir del program 71 | task.updatemd5 = Actualizar MD5 72 | task.updatemd5.desc = Actualizar las tablas de hashes MD5 usadas para reobfuscar 73 | task.createpatch = Crear Parche 74 | task.createpatch.desc = Crea un parche basado en tus cambios al código 75 | task.backupsrc = Copia de seguridad del código 76 | task.backupsrc.desc = Empacar el código en un archivo zip 77 | task.applypatch = Aplicar parche 78 | task.applypatch.desc = Aplicar parch desde el directorio de parches 79 | task.updatemcp = Actualizar RetroMCP 80 | task.updatemcp.desc = Comprobar nuevas actualizaciones 81 | task.noDesc = No hay descripción disponible 82 | 83 | task.stage.idle = Ausente 84 | task.stage.finished = ¡Completada! 85 | task.stage.recompile = Recompilando 86 | task.stage.decompile = Descompilando 87 | task.stage.backupsrc = Haciendo una copia de seguridad 88 | task.stage.reobf = Reobfuscando 89 | task.stage.build = Compilando 90 | task.stage.setup = Preparando 91 | task.stage.createpatch = Creando parche 92 | task.stage.prepare = Preparando 93 | task.stage.patch = Aplicando parches 94 | task.stage.copysrc = Copiando código 95 | task.stage.remap = Remapeando el JAR 96 | task.stage.rdi = Aplicando RDInjector 97 | task.stage.constants = Remplazando constantes 98 | task.stage.copyres = Copiando recursos 99 | task.stage.gathermd5 = Reuniendo hashes MD5 100 | task.stage.updatemd5 = Actualizando MD5 101 | task.stage.download = Descargando: %s 102 | task.stage.workspace = Creando un espacio de trabajo 103 | 104 | task.param.debug = Mostrar información adicional 105 | task.param.side = Cambiar lado 106 | task.param.src = Limpiar código 107 | task.param.patch = Aplicar parches 108 | task.param.ignore = Establecer paquetes ignorados 109 | task.param.ff_options = Establecer opciones de FernFlower 110 | task.param.obf = Obfuscar clases de mod 111 | task.param.excludedclasses = Clases excluídas 112 | task.param.fullbuild = build completa 113 | task.param.runbuild = Ejecutar build 114 | task.param.runargs = Argumentos de la JVM 115 | task.param.gameargs = Argumentos del juego 116 | task.param.setup = Preparar versión 117 | task.param.source = Establecer una versión del código específica 118 | task.param.target = Establecer una versión target del código específica 119 | task.param.javahome = Establecer JAVA_HOME usada para compilar 120 | task.param.resources = Mantener recursos 121 | task.param.generics = Adivinar genericos 122 | task.param.stripgenerics = Estripear genericos 123 | task.param.outputsrc = Salida de recursos 124 | 125 | tasks.success = ¡Finalizado correctamente! 126 | tasks.warning = ¡Finalizado con advertencias! 127 | tasks.error = ¡Finalizado con errores! 128 | -------------------------------------------------------------------------------- /src/main/resources/lang/fr_FR.lang: -------------------------------------------------------------------------------- 1 | language = Français 2 | mcp.needJDK = Le Java Development Kit est nécéssaire pour recompiler ! 3 | mcp.error = Erreur 4 | mcp.newVersion = Nouvelle version trouvée : 5 | mcp.confirmExit = Êtes-vous sûr de vouloir quitter ? Certaines tâches n'ont pas terminé leur éxécution ! 6 | mcp.confirmUpdate = Êtes-vous sûr de vouloir mettre à jour ? 7 | mcp.confirmUpdateMD5 = Êtes-vous sûr de vouloir regénérer les hachages d'origine ? 8 | mcp.confirmAction = Confirmer l'action 9 | mcp.confirmDecompile = Êtes-vous sûr de vouloir effacer les sources et décompiler à nouveau ? 10 | mcp.confirmSetup = Êtes-vous sûr de vouloir démarrer l'installation pour la version sélectionnée ? 11 | mcp.confirmCleanup = Êtes-vous sûr de vouloir effacer toutes les sources et la version actuelle ? 12 | mcp.askSourceBackup = Faire une sauvegarde des fichers de source ? 13 | mcp.incompatiblePlugin = Plugin incompatible trouvé : 14 | mcp.versionList.currentVersion = Version actuelle : 15 | mcp.versionList.failure = Impossible d'obtenir la liste des versions ! 16 | mcp.versionList.loading = Chargement... 17 | mcp.checkUpdate = Rechercher des mises à jour 18 | mcp.upToDate = À jour ! 19 | mcp.viewDir = Afficher le répertoire de travail 20 | mcp.changeDir = Modifier le répertoire de travail 21 | mcp.enterDir = Entrer un chemin vers un répertoire 22 | mcp.selectDir = Sélectionner un dossier 23 | mcp.moreTasks = Plus de tâches... 24 | mcp.github = Page GitHub 25 | mcp.wiki = Wiki 26 | mcp.side = Côté 27 | mcp.startClient = Démarrer le client 28 | mcp.startServer = Démarrer le serveur 29 | mcp.console = Sortie de la console 30 | 31 | side.client = Client 32 | side.server = Serveur 33 | side.merged = Fusionné 34 | side.any = Tout 35 | 36 | options = Options 37 | options.language = Langue 38 | options.theme = Thème 39 | options.enterValue = Entrer une valeur 40 | options.enterValues = Entrer un ensemble de valeurs 41 | options.enterValues.info = (Séparer les valeurs par une virgule) 42 | options.running = En cours d'éxécution 43 | options.resetDefaults = Réinitialiser aux valeurs par défaut 44 | options.invalidValue = Valeur invalide ! 45 | 46 | options.theme.swing = Swing 47 | options.theme.flatlightlaf = FlatLightLaf 48 | options.theme.flatdarklaf = FlatDarkLaf 49 | options.theme.flatdarculalaf = FlatDarculaLaf 50 | 51 | task.help = Aide 52 | task.help.desc = Affiche l'utilisation de la commande 53 | task.setup = Configuration 54 | task.setup.desc = Définit l'espace de travail initial pour une version 55 | task.setup.selectVersion = Sélectionner une version : 56 | task.decompile = Décompiler 57 | task.decompile.desc = Démarre la décompilation de Minecraft 58 | task.recompile = Recompiler 59 | task.recompile.desc = Recompile les sources de Minecraft 60 | task.reobfuscate = Réobfusquer 61 | task.reobfuscate.desc = Réobfusque les classes de Minecraft 62 | task.build = Build 63 | task.build.desc = Construit le fichier jar ou zip avec les classes modifiées 64 | task.start = Démarrer 65 | task.start.desc = Démarre le client ou le serveur à partir des classes compilées 66 | task.cleanup = Nettoyer 67 | task.cleanup.desc = Efface tous les dossiers de sources et de classes 68 | task.exit = Quitter 69 | task.exit.desc = Quitte le programme 70 | task.updatemd5 = Mettre à jour les MD5 71 | task.updatemd5.desc = Met à jour les tables de hachage MD5 utilisées pour la réobfuscation 72 | task.createpatch = Créer un correctif 73 | task.createpatch.desc = Crée un correctif basé sur vos modifications apportées à la source 74 | task.backupsrc = Sauvegarder la source 75 | task.backupsrc.desc = Compresse les fichiers de source dans un zip 76 | task.applypatch = Appliquer un correctif 77 | task.applypatch.desc = Applique un correctif à partir du répertoire des correctifs 78 | task.noDesc = Aucune description fournie 79 | 80 | task.stage.idle = Inactif 81 | task.stage.finished = Terminé ! 82 | task.stage.recompile = Recompilation 83 | task.stage.decompile = Décompilation 84 | task.stage.backupsrc = Sauvegarde 85 | task.stage.reobf = Réobfuscation 86 | task.stage.build = Build 87 | task.stage.setup = Configuration 88 | task.stage.createpatch = Création du correctif 89 | task.stage.prepare = Préparation 90 | task.stage.patch = Application des correctifs 91 | task.stage.copysrc = Copie des sources 92 | task.stage.remap = Remappage du JAR 93 | task.stage.rdi = Application de RDInjector 94 | task.stage.constants = Remplacement des constantes 95 | task.stage.copyres = Copie des ressources 96 | task.stage.gathermd5 = Collecte des hachages MD5 97 | task.stage.updatemd5 = Mise à jour des MD5 98 | task.stage.download = Téléchargement : %s 99 | task.stage.workspace = Configuration de l'espace de travail 100 | 101 | task.param.debug = Afficher des informations supplémentaires 102 | task.param.side = Définir le côté 103 | task.param.src = Nettoyer les sources 104 | task.param.patch = Appliquer les correctifs 105 | task.param.ignore = Définir les packages ignorés 106 | task.param.ind = Définir le string d'indentation 107 | task.param.obf = Obfusquer les classes des mods 108 | task.param.fullbuild = Build complet 109 | task.param.runbuild = Démarrer le build 110 | task.param.runargs = Arguments JVM 111 | task.param.gameargs = Arguments de jeu 112 | task.param.setup = Version d'installation 113 | task.param.source = Définir une version de source spécifique 114 | task.param.target = Définir une version cible spécifique 115 | task.param.javahome = Définir JAVA_HOME utilisé pour la compilation 116 | task.param.override = Décompiler @Override 117 | task.param.resources = Conserver les ressources 118 | task.param.generics = Deviner les génériques 119 | task.param.stripgenerics = Retirer les génériques 120 | task.param.outputsrc = Sortir la source 121 | 122 | tasks.success = Terminé avec succès ! 123 | tasks.warning = Terminé avec des avertissements ! 124 | tasks.error = Terminé avec des erreurs ! 125 | -------------------------------------------------------------------------------- /src/main/resources/lang/nb_NO.lang: -------------------------------------------------------------------------------- 1 | language = Norsk Bokmål 2 | mcp.needJDK = Java Development Kit kreves for å omkompilere! 3 | mcp.error = Feil 4 | mcp.newVersion = Ny versjon funnet: 5 | mcp.confirmExit = Er du sikker på at du vil avslutte? Noen oppgaver har ikke blitt ferdig utført enda! 6 | mcp.confirmUpdate = Er du sikker på du vil oppdatere? 7 | mcp.confirmUpdateMD5 = Er du sikker på at du vil regenerere de originale hashene? 8 | mcp.confirmAction = Bekreft handling 9 | mcp.confirmDecompile = Er du sikker på at du vil fjerne kildekoden og omkompilere igjen? 10 | mcp.confirmSetup = Er du sikker på at du vil kjøre oppsettet for den valgte versjonen? 11 | mcp.confirmCleanup = Er du sikker på at du vil fjerne kildekoden og slette den nåværende aktive versjonen? 12 | mcp.askSourceBackup = Vil du lage en sikkerhetskopi av kildekoden? 13 | mcp.incompatiblePlugin = Ukompatibel utvidelse funnet: 14 | mcp.versionList.currentVersion = Nåværende versjon: 15 | mcp.versionList.failure = Kan ikke hente versjonslista! 16 | mcp.versionList.loading = Laster... 17 | mcp.checkUpdate = Sjekk for oppdateringer 18 | mcp.upToDate = Oppdatert! 19 | mcp.viewDir = Se arbeidsområde 20 | mcp.changeDir = Endre arbeidsområde 21 | mcp.enterDir = Skriv inn banen til et arbeidsområde 22 | mcp.selectDir = Velg mappe 23 | mcp.moreTasks = Fler oppgaver... 24 | mcp.github = GitHub-side 25 | mcp.wiki = Wiki 26 | mcp.side = Side 27 | mcp.startClient = Start klient 28 | mcp.startServer = Start server 29 | mcp.console = Konsollutdata 30 | 31 | side.client = Klient 32 | side.server = Server 33 | side.merged = Sammenslått 34 | side.any = Hvilken som helst 35 | 36 | options = Alternativer 37 | options.language = Språk 38 | options.theme = Tema 39 | options.enterValue = Skriv inn en verdi 40 | options.enterValues = Skriv inn et sett verdier 41 | options.enterValues.info = (Skill verdiene med komma) 42 | options.running = Kjører 43 | options.resetDefaults = Nullstill til standardverdier 44 | options.invalidValue = Ugyldig verdi! 45 | 46 | options.theme.swing = Swing 47 | options.theme.flatlightlaf = FlatLightLaf 48 | options.theme.flatdarklaf = FlatDarkLaf 49 | options.theme.flatdarculalaf = FlatDraculaLaf 50 | 51 | task.help = Hjelp 52 | task.help.desc = Viser informasjon om kommandobruk 53 | task.setup = Oppsett 54 | task.setup.desc = Angi første arbeidsområde for en versjon 55 | task.setup.selectVersion = Velg versjon: 56 | task.decompile = Dekompiler 57 | task.decompile.desc = Start dekompileringen av Minecraft 58 | task.recompile = Rekompiler 59 | task.recompile.desc = Rekompiler Minecraft-kildekode 60 | task.reobfuscate = Reobfusker 61 | task.reobfuscate.desc = Reobfusker Minecraft-klasser 62 | task.build = Bygg 63 | task.build.desc = Bygg JAR-fila eller en ZIP-fil med modifiserte klasser 64 | task.start = Start 65 | task.start.desc = Kjør klienten eller serveren fra kompilerte klasser 66 | task.cleanup = Rens 67 | task.cleanup.desc = Fjern all kildekode og klassemapper 68 | task.exit = Avslutt 69 | task.exit.desc = Avslutt programmet 70 | task.updatemd5 = Oppdater MD5 71 | task.updatemd5.desc = Oppdater MD5 hashtabeller brukt for reobfuskering 72 | task.createpatch = Lag Patch 73 | task.createpatch.desc = Lag en patch basert på endringene i kildekoden 74 | task.backupsrc = Sikkerhetskopier kildekoden 75 | task.backupsrc.desc = Pakk ned kildekoden i en ZIP-fil 76 | task.applypatch = Legg til patch 77 | task.applypatch.desc = Legg til patch fra patchområdet 78 | task.updatemcp = Oppdater RetroMCP 79 | task.updatemcp.desc = Sjekk for oppdateringer 80 | task.noDesc = Ingen beskrivelse angitt 81 | 82 | task.stage.idle = Inaktiv 83 | task.stage.finished = Ferdig! 84 | task.stage.recompile = Rekompiler 85 | task.stage.decompile = Dekompiler 86 | task.stage.backupsrc = Sikkerhetskopierer 87 | task.stage.reobf = Reobfuskerer 88 | task.stage.build = Bygger 89 | task.stage.setup = Setter opp 90 | task.stage.createpatch = Lager patch 91 | task.stage.prepare = Forbereder 92 | task.stage.patch = Legger til patcher 93 | task.stage.copysrc = Kopierer kildekode 94 | task.stage.remap = Omkartlegger JAR 95 | task.stage.rdi = Legger til RDInjector 96 | task.stage.constants = Erstatter konstanter 97 | task.stage.copyres = Kopierer ressurser 98 | task.stage.gathermd5 = Samler MD5-hasher 99 | task.stage.updatemd5 = Oppdaterer MD5 100 | task.stage.download = Laster ned: %s 101 | task.stage.workspace = Setter opp arbeidsområde 102 | 103 | task.param.debug = Vis tilleggsinformasjon 104 | task.param.side = Velg side 105 | task.param.src = Kilderens 106 | task.param.patch = Legg til patcher 107 | task.param.ignore = Angi ignorerte pakker 108 | task.param.ind = Angi innrykksstreng 109 | task.param.obf = Obfusker modifikasjonsklasser 110 | task.param.excludedclasses = Ekskluderte klasser 111 | task.param.fullbuild = Bygg alt 112 | task.param.runbuild = Kjør bygg 113 | task.param.runargs = JVM-argumenter 114 | task.param.gameargs = Spillargumenter 115 | task.param.setup = Sett opp versjon 116 | task.param.source = Angi en spesifikk kildeversjon 117 | task.param.target = Angi en spesifikk målversjon 118 | task.param.javahome = Angi JAVA_HOME brukt for kompilering 119 | task.param.override = Legg til @Override 120 | task.param.resources = Behold ressurser 121 | task.param.generics = Gjett genetikk 122 | task.param.stripgenerics = Fjern genetikk 123 | task.param.outputsrc = Utdatakilde 124 | 125 | tasks.success = Fullført! 126 | tasks.warning = Fullført med advarsler! 127 | tasks.error = Fullført med feil! -------------------------------------------------------------------------------- /src/main/resources/lang/ru_RU.lang: -------------------------------------------------------------------------------- 1 | language = Русский 2 | mcp.needJDK = Необходим Java Development Kit для рекомпиляции! 3 | mcp.error = Ошибка 4 | mcp.newVersion = Найдена новая версия: 5 | mcp.confirmExit = Вы уверены что хотите выйти? Некоторые задачи ещё не закончили выполнение! 6 | mcp.confirmUpdate = Вы уверены, что хотите обновить программу? 7 | mcp.confirmUpdateMD5 = Вы уверены, что хотите пересоздать базу хешей? 8 | mcp.confirmAction = Подтвердите действие 9 | mcp.confirmDecompile = Вы уверены, что хотите удалить код и снова декомпилировать игру? 10 | mcp.confirmSetup = Вы уверены, что хотите запустить установку для данной версии? 11 | mcp.askSourceBackup = Сделать бэкап? 12 | mcp.incompatiblePlugin = Обнаружен несовместимый плагин: 13 | mcp.versionList.currentVersion = Текущая версия: 14 | mcp.versionList.failure = Не удается получить список версий! 15 | mcp.versionList.loading = Загрузка... 16 | mcp.checkUpdate = Проверить наличие обновлений 17 | mcp.upToDate = Установлена последняя версия! 18 | mcp.viewDir = Открыть папку рабочей среды 19 | mcp.changeDir = Сменить папку рабочую среду 20 | mcp.enterDir = Введите путь к директории 21 | mcp.selectDir = Выберите папку 22 | mcp.moreTasks = Больше задач... 23 | mcp.github = Страница GitHub 24 | mcp.wiki = Вики 25 | mcp.side = Сторона 26 | mcp.startClient = Запустить клиент 27 | mcp.startServer = Запустить сервер 28 | mcp.console = Вывод консоли 29 | 30 | side.client = Клиент 31 | side.server = Сервер 32 | side.merged = Объединенный 33 | side.any = Любой 34 | 35 | options = Настройки 36 | options.language = Язык 37 | options.theme = Тема 38 | options.enterValue = Введите значение 39 | options.enterValues = Введите группу значении 40 | options.enterValues.info = (Разделяйте значения запятой) 41 | options.running = Запуск 42 | options.resetDefaults = Сбросить по умолчанию 43 | options.invalidValue = Некорректное значение! 44 | 45 | task.help = Помощь 46 | task.help.desc = Отображает использование команды 47 | task.setup = Настройка 48 | task.setup.desc = Настройка рабочей среды для определенной версии 49 | task.setup.selectVersion = Выберите версию: 50 | task.decompile = Декомпиляция 51 | task.decompile.desc = Начать декомпилирование игры 52 | task.recompile = Рекомпиляция 53 | task.recompile.desc = Рекомпилировать код игры 54 | task.reobfuscate = Реобфусикация 55 | task.reobfuscate.desc = Реобфусикацировать классы игры 56 | task.build = Сборка 57 | task.build.desc = Собрать готовый .jar или .zip архив 58 | task.start = Старт 59 | task.start.desc = Запустить клиент или сервер с компилированных классов 60 | task.cleanup = Очистка 61 | task.cleanup.desc = Удалить все папки с кодом и ресурсами 62 | task.exit = Выход 63 | task.exit.desc = Выйти из программы 64 | task.updatemd5 = Обновить MD5 65 | task.updatemd5.desc = Обновить MD5 хеши, используемые для реобфусикации 66 | task.createpatch = Создать патч 67 | task.createpatch.desc = Создать патч на основе измененного кода 68 | task.backupsrc = Бэкап 69 | task.backupsrc.desc = Упаковать исходный код в архив 70 | task.applypatch = Наложить патч 71 | task.applypatch.desc = Применяет патч из директории "patches" 72 | task.noDesc = Нет описания 73 | 74 | task.stage.idle = Простой 75 | task.stage.finished = Завершен! 76 | task.stage.recompile = Рекомпиляция 77 | task.stage.decompile = Декомпиляция 78 | task.stage.backupsrc = Создание бэкапа 79 | task.stage.reobf = Реобфусикация 80 | task.stage.build = Сборка 81 | task.stage.setup = Настройка 82 | task.stage.createpatch = Создание патча 83 | task.stage.prepare = Подготовка 84 | task.stage.patch = Применение патчей 85 | task.stage.copysrc = Копирования кода 86 | task.stage.remap = Ремаппинг JAR 87 | task.stage.rdi = Применение RDInjector 88 | task.stage.constants = Замена констант 89 | task.stage.copyres = Копирование ресурсов 90 | task.stage.gathermd5 = Генерация MD5 хешей 91 | task.stage.updatemd5 = Обновление MD5 92 | task.stage.download = Загрузка %s 93 | task.stage.workspace = Настройка рабочей среды 94 | 95 | task.param.debug = Показать дополнительную информацию 96 | task.param.side = Задать сторону 97 | task.param.src = Очистка кода 98 | task.param.patch = Применить патчи 99 | task.param.ignore = Задать игнорируемые пакеты 100 | task.param.ind = Задать строку отступа 101 | task.param.obf = Обфусицировать мод 102 | task.param.fullbuild = Полная сборка 103 | task.param.runbuild = Запускать сборку 104 | task.param.runargs = JVM аргументы 105 | task.param.setup = Настройка версии 106 | task.param.source = Задать версию для аргумента компилятора "source" 107 | task.param.target = Задать версию для аргумента компилятора "target" 108 | task.param.javahome = Задать JAVA_HOME для компилирования 109 | task.param.override = Декомпиляция с @Override 110 | task.param.resources = Оставлять ресурсы 111 | task.param.generics = Отгадывать дженерики 112 | task.param.stripgenerics = Удалить дженерики 113 | task.param.outputsrc = Выводить исходный код 114 | 115 | tasks.success = Завершено успешно! 116 | tasks.warning = Завершено с предупреждениями! 117 | tasks.error = Завершено с ошибками! -------------------------------------------------------------------------------- /src/main/resources/lang/zh_CN.lang: -------------------------------------------------------------------------------- 1 | language = 中文 2 | mcp.needJDK = 你需要 Java Development Kit才能重编译! 3 | mcp.error = 错误 4 | mcp.newVersion = 发现更新: 5 | mcp.confirmExit = 你确定要退出吗?有些任务尚未完成。 6 | mcp.confirmUpdate = 你确定要更新吗? 7 | mcp.confirmUpdateMD5 = 你确定要重新生成原始哈希值吗? 8 | mcp.confirmAction = 确认执行 9 | mcp.confirmDecompile = 你确定要删除源代码并重新反编译吗? 10 | mcp.confirmSetup = 你确定要执行选定版本的初始化吗? 11 | mcp.confirmCleanup = 你确定要删除源码并且清除当前版本吗? 12 | mcp.askSourceBackup = 需要备份源码吗? 13 | mcp.incompatiblePlugin = 发现不兼容的插件: 14 | mcp.versionList.currentVersion = 目前版本: 15 | mcp.versionList.failure = 无法获取版本列表! 16 | mcp.versionList.loading = 加载中...... 17 | mcp.checkUpdate = 检查更新 18 | mcp.upToDate = 已经是最新版本! 19 | mcp.viewDir = 浏览工作空间目录 20 | mcp.changeDir = 更换工作空间目录 21 | mcp.enterDir = 键入目录路径 22 | mcp.selectDir = 选择文件夹 23 | mcp.moreTasks = 更多任务...... 24 | mcp.github = GitHub 页面 25 | mcp.wiki = Wiki 26 | mcp.side = 端 27 | mcp.startClient = 启动客户端 28 | mcp.startServer = 启动服务端 29 | mcp.console = 控制台输出 30 | mcp.clearConsole = 清除控制台 31 | 32 | side.client = 客户端 33 | side.server = 服务端 34 | side.merged = 双端合并 35 | side.any = 任意一端 36 | 37 | options = 选项 38 | options.language = 语言 39 | options.theme = 主题 40 | options.enterValue = 键入一个值 41 | options.enterValues = 键入多个值 42 | options.enterValues.info = (以逗号区分每一个值) 43 | options.running = 运行时设置 44 | options.resetDefaults = 重置为默认值 45 | options.invalidValue = 无效值! 46 | 47 | options.theme.swing = Swing 48 | options.theme.flatlightlaf = FlatLightLaf 49 | options.theme.flatdarklaf = FlatDarkLaf 50 | options.theme.flatdarculalaf = FlatDarculaLaf 51 | 52 | task.help = 帮助 53 | task.help.desc = 显示命令用法 54 | task.setup = 初始化 55 | task.setup.desc = 设立针对某个版本的初始工作空间 56 | task.setup.selectVersion = 选择版本: 57 | task.decompile = 反编译 58 | task.decompile.desc = 开始反编译 Minecraft 59 | task.recompile = 重编译 60 | task.recompile.desc = 重新编译 Minecraft 源代码 61 | task.reobfuscate = 重混淆 62 | task.reobfuscate.desc = 重新混淆 Minecraft 类 63 | task.build = 构建 64 | task.build.desc = 构建最终的 jar 或 zip 文件 65 | task.start = 开始 66 | task.start.desc = 从由编译的类文件启动 Minecraft 客户端或服务端 67 | task.cleanup = 清理 68 | task.cleanup.desc = 删除所有源代码及类文件夹 69 | task.exit = 退出 70 | task.exit.desc = 退出程序 71 | task.updatemd5 = 更新 MD5 72 | task.updatemd5.desc = 更新用于重混淆的 MD5 哈希表 73 | task.createpatch = 创建补丁 74 | task.createpatch.desc = 根据源代码的变更生成补丁 75 | task.backupsrc = 备份源码 76 | task.backupsrc.desc = 将源码打包成zip文档 77 | task.applypatch = 应用补丁 78 | task.applypatch.desc = 从patchs文件夹里应用补丁 79 | task.updatemcp = 更新RetroMCP 80 | task.updatemcp.desc = 检查更新 81 | task.noDesc = 无描述; 82 | 83 | task.stage.idle = 闲置中 84 | task.stage.finished = 已完成! 85 | task.stage.recompile = 重编译中 86 | task.stage.decompile = 反编译中 87 | task.stage.reobf = 重混淆中 88 | task.stage.build = 构建中 89 | task.stage.setup = 初始化中 90 | task.stage.createpatch = 创建补丁中 91 | task.stage.prepare = 准备中 92 | task.stage.patch = 应用补丁中 93 | task.stage.copysrc = 复制源代码中 94 | task.stage.remap = 映射 JAR 中 95 | task.stage.rdi = 应用 RDInjector 中 96 | task.stage.constants = 替换常量中 97 | task.stage.extractsrc = 提取源代码中 98 | task.stage.copyres = 复制资源中 99 | task.stage.gathermd5 = 获取 MD5 哈希表中 100 | task.stage.updatemd5 = 更新 MD5 中 101 | task.stage.download = 下载 %s 中 102 | task.stage.workspace = 设立工作空间中 103 | 104 | task.param.debug = 显示额外信息 105 | task.param.side = 设置端 106 | task.param.src = 清理源代码 107 | task.param.patch = 应用补丁 108 | task.param.ignore = 设置忽略包 109 | task.param.ff_options = 设置Fernflower选项 110 | task.param.obf = 重新混淆模组 111 | task.param.excludedclasses = 排除类 112 | task.param.fullbuild = 完全构建 113 | task.param.runbuild = 运行构建 114 | task.param.runargs = JVM参数 115 | task.param.gameargs = 游戏参数 116 | task.param.setup = 初始化版本 117 | task.param.source = 设置具体的源版本 118 | task.param.target = 设置具体的目标版本 119 | task.param.javahome = 设置编译时用到的 JAVA_HOME 环境变量 120 | task.param.resources = 保持资源 121 | task.param.generics = 猜测泛型 122 | task.param.stripgenerics = 删除泛型 123 | task.param.outputsrc = 输出源码 124 | 125 | tasks.success = 成功完成! 126 | tasks.warning = 完成,但有警告! 127 | tasks.error = 完成,但有错误! -------------------------------------------------------------------------------- /src/test/java/org/mcphackers/mcp/TestPlugin.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import org.mcphackers.mcp.plugin.MCPPlugin; 6 | import org.mcphackers.mcp.tasks.Task; 7 | import org.mcphackers.mcp.tasks.TaskMergeMappings; 8 | import org.mcphackers.mcp.tasks.TaskStaged; 9 | import org.mcphackers.mcp.tasks.mode.TaskMode; 10 | import org.mcphackers.mcp.tasks.mode.TaskModeBuilder; 11 | 12 | public class TestPlugin implements MCPPlugin { 13 | private final Logger logger = Logger.getLogger("testPlugin"); 14 | 15 | @Override 16 | public String pluginId() { 17 | return "test"; 18 | } 19 | 20 | @Override 21 | public void init(MCP mcp) { 22 | logger.info("Test plugin has initialized!"); 23 | TaskMode mergeMappingsTask = new TaskModeBuilder() 24 | .setName("mergemappings") 25 | .setTaskClass(TaskMergeMappings.class) 26 | .setProgressBars(false) 27 | .build(); 28 | } 29 | 30 | @Override 31 | public void onTaskEvent(TaskEvent event, Task task) { 32 | 33 | } 34 | 35 | @Override 36 | public void onMCPEvent(MCPEvent event, MCP mcp) { 37 | 38 | } 39 | 40 | @Override 41 | public void setTaskOverrides(TaskStaged task) { 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/mcphackers/mcp/tasks/TaskMergeMappings.java: -------------------------------------------------------------------------------- 1 | package org.mcphackers.mcp.tasks; 2 | 3 | import org.mcphackers.mcp.MCP; 4 | import org.mcphackers.mcp.tools.mappings.MappingUtil; 5 | 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | 10 | public class TaskMergeMappings extends TaskStaged { 11 | public TaskMergeMappings(MCP instance) { 12 | super(instance); 13 | } 14 | 15 | @Override 16 | protected Stage[] setStages() { 17 | return new Stage[] { 18 | stage(getLocalizedStage("mergemappings"), () -> { 19 | Path clientMappings = Paths.get("client.tiny"); 20 | Path serverMappings = Paths.get("server.tiny"); 21 | Path mergedMappings = Paths.get("merged.tiny"); 22 | if (Files.exists(clientMappings)) { 23 | if (Files.exists(serverMappings)) { 24 | // Merge client & server mappings 25 | MappingUtil.mergeMappings(clientMappings, serverMappings, mergedMappings); 26 | } else { 27 | // Only client mappings exist 28 | MappingUtil.mergeMappings(clientMappings, mergedMappings); 29 | } 30 | } 31 | }) 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/org.mcphackers.mcp.plugin.MCPPlugin: -------------------------------------------------------------------------------- 1 | org.mcphackers.mcp.TestPlugin 2 | -------------------------------------------------------------------------------- /src/test/resources/lang/en_US.lang: -------------------------------------------------------------------------------- 1 | task.mergemappings = Merge mappings 2 | task.mergemappings.desc = Merges mappings into V2 format 3 | --------------------------------------------------------------------------------