├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── maven.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ └── maven-wrapper.properties ├── CODE_OF_CONDUCT.md ├── Jenkinsfile ├── LICENSE ├── README.md ├── _config.yml ├── modpackdownloader-cli ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── nincraft │ │ └── modpackdownloader │ │ └── cli │ │ └── ModpackDownloaderCLI.java │ └── resources │ └── log4j2.xml ├── modpackdownloader-core ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── nincraft │ │ │ └── modpackdownloader │ │ │ ├── ModpackDownloaderManager.java │ │ │ ├── container │ │ │ ├── CurseFile.java │ │ │ ├── CurseModpackFile.java │ │ │ ├── DownloadableFile.java │ │ │ ├── Manifest.java │ │ │ ├── Minecraft.java │ │ │ ├── Mod.java │ │ │ ├── ModLoader.java │ │ │ └── ThirdParty.java │ │ │ ├── handler │ │ │ ├── ApplicationUpdateHandler.java │ │ │ ├── CurseFileHandler.java │ │ │ ├── ForgeHandler.java │ │ │ ├── ModHandler.java │ │ │ └── ThirdPartyModHandler.java │ │ │ ├── processor │ │ │ ├── AbstractProcessor.java │ │ │ ├── DownloadModsProcessor.java │ │ │ ├── MergeManifestsProcessor.java │ │ │ └── UpdateModsProcessor.java │ │ │ ├── status │ │ │ └── DownloadStatus.java │ │ │ ├── summary │ │ │ ├── DownloadSummarizer.java │ │ │ └── UpdateCheckSummarizer.java │ │ │ ├── util │ │ │ ├── Arguments.java │ │ │ ├── DownloadHelper.java │ │ │ ├── FileSystemHelper.java │ │ │ ├── ManifestHelper.java │ │ │ ├── Reference.java │ │ │ ├── URLHelper.java │ │ │ └── VersionHelper.java │ │ │ └── validation │ │ │ └── ReleaseType.java │ └── resources │ │ └── latest.json │ └── test │ ├── java │ └── com │ │ └── nincraft │ │ └── modpackdownloader │ │ └── ModpackDownloaderTest.java │ └── resources │ ├── all-mods.json │ ├── botania-manifest.json │ ├── client-mods.json │ ├── download-test.json │ ├── forge.json │ ├── log4j2.xml │ ├── twbb-manifest.json │ ├── update-test-backup.json │ └── update-test.json ├── modpackdownloader-gui ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── nincraft │ │ └── modpackdownloader │ │ └── gui │ │ ├── Controller.java │ │ ├── ModpackDownloaderGUI.java │ │ └── TextAreaAppender.java │ └── resources │ ├── log4j2.xml │ └── mpd.fxml ├── modpackdownloader-maven-plugin ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── nincraft │ └── modpackdownloader │ └── plugin │ ├── DownloadMojo.java │ └── UpdateMojo.java ├── mvnw ├── mvnw.cmd └── pom.xml /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 11, 17, 11-bullseye, 17-bullseye, 11-buster, 17-buster 4 | ARG VARIANT="17-bullseye" 5 | FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT} 6 | 7 | # [Option] Install Maven 8 | ARG INSTALL_MAVEN="false" 9 | ARG MAVEN_VERSION="" 10 | # [Option] Install Gradle 11 | ARG INSTALL_GRADLE="false" 12 | ARG GRADLE_VERSION="" 13 | RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \ 14 | && if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi 15 | 16 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 17 | ARG NODE_VERSION="none" 18 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 19 | 20 | # [Optional] Uncomment this section to install additional OS packages. 21 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 22 | # && apt-get -y install --no-install-recommends 23 | 24 | # [Optional] Uncomment this line to install global node packages. 25 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java 3 | { 4 | "name": "Java", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | // Update the VARIANT arg to pick a Java version: 11, 17 9 | // Append -bullseye or -buster to pin to an OS version. 10 | // Use the -bullseye variants on local arm64/Apple Silicon. 11 | "VARIANT": "17-buster", 12 | // Options 13 | "INSTALL_MAVEN": "false", 14 | "INSTALL_GRADLE": "false", 15 | "NODE_VERSION": "none" 16 | } 17 | }, 18 | 19 | // Set *default* container specific settings.json values on container create. 20 | "settings": { 21 | "java.home": "/docker-java-home" 22 | }, 23 | 24 | // Add the IDs of extensions you want installed when the container is created. 25 | "extensions": [ 26 | "vscjava.vscode-java-pack" 27 | ], 28 | 29 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 30 | // "forwardPorts": [], 31 | 32 | // Use 'postCreateCommand' to run commands after the container is created. 33 | // "postCreateCommand": "java -version", 34 | 35 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 36 | "remoteUser": "vscode", 37 | "features": { 38 | "git": "latest", 39 | "github-cli": "latest" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = crlf 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = false 7 | max_line_length = 120 8 | tab_width = 4 9 | ij_continuation_indent_size = 8 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | ij_formatter_tags_enabled = true 13 | ij_smart_tabs = false 14 | ij_wrap_on_typing = false 15 | 16 | [*.java] 17 | ij_java_align_consecutive_assignments = false 18 | ij_java_align_consecutive_variable_declarations = false 19 | ij_java_align_group_field_declarations = false 20 | ij_java_align_multiline_annotation_parameters = false 21 | ij_java_align_multiline_array_initializer_expression = false 22 | ij_java_align_multiline_assignment = false 23 | ij_java_align_multiline_binary_operation = false 24 | ij_java_align_multiline_chained_methods = false 25 | ij_java_align_multiline_extends_list = false 26 | ij_java_align_multiline_for = true 27 | ij_java_align_multiline_method_parentheses = false 28 | ij_java_align_multiline_parameters = false 29 | ij_java_align_multiline_parameters_in_calls = false 30 | ij_java_align_multiline_parenthesized_expression = false 31 | ij_java_align_multiline_resources = true 32 | ij_java_align_multiline_ternary_operation = false 33 | ij_java_align_multiline_throws_list = false 34 | ij_java_align_subsequent_simple_methods = false 35 | ij_java_align_throws_keyword = false 36 | ij_java_annotation_parameter_wrap = off 37 | ij_java_array_initializer_new_line_after_left_brace = false 38 | ij_java_array_initializer_right_brace_on_new_line = false 39 | ij_java_array_initializer_wrap = off 40 | ij_java_assert_statement_colon_on_next_line = false 41 | ij_java_assert_statement_wrap = off 42 | ij_java_assignment_wrap = off 43 | ij_java_binary_operation_sign_on_next_line = true 44 | ij_java_binary_operation_wrap = normal 45 | ij_java_blank_lines_after_anonymous_class_header = 0 46 | ij_java_blank_lines_after_class_header = 0 47 | ij_java_blank_lines_after_imports = 1 48 | ij_java_blank_lines_after_package = 1 49 | ij_java_blank_lines_around_class = 1 50 | ij_java_blank_lines_around_field = 0 51 | ij_java_blank_lines_around_field_in_interface = 0 52 | ij_java_blank_lines_around_initializer = 1 53 | ij_java_blank_lines_around_method = 1 54 | ij_java_blank_lines_around_method_in_interface = 1 55 | ij_java_blank_lines_before_class_end = 0 56 | ij_java_blank_lines_before_imports = 1 57 | ij_java_blank_lines_before_method_body = 0 58 | ij_java_blank_lines_before_package = 0 59 | ij_java_block_brace_style = end_of_line 60 | ij_java_block_comment_at_first_column = true 61 | ij_java_call_parameters_new_line_after_left_paren = false 62 | ij_java_call_parameters_right_paren_on_new_line = false 63 | ij_java_call_parameters_wrap = off 64 | ij_java_case_statement_on_separate_line = true 65 | ij_java_catch_on_new_line = false 66 | ij_java_class_annotation_wrap = split_into_lines 67 | ij_java_class_brace_style = end_of_line 68 | ij_java_class_count_to_use_import_on_demand = 5 69 | ij_java_class_names_in_javadoc = 1 70 | ij_java_do_not_indent_top_level_class_members = false 71 | ij_java_do_not_wrap_after_single_annotation = false 72 | ij_java_do_while_brace_force = never 73 | ij_java_doc_add_blank_line_after_description = true 74 | ij_java_doc_add_blank_line_after_param_comments = false 75 | ij_java_doc_add_blank_line_after_return = false 76 | ij_java_doc_add_p_tag_on_empty_lines = true 77 | ij_java_doc_align_exception_comments = true 78 | ij_java_doc_align_param_comments = true 79 | ij_java_doc_do_not_wrap_if_one_line = false 80 | ij_java_doc_enable_formatting = true 81 | ij_java_doc_enable_leading_asterisks = true 82 | ij_java_doc_indent_on_continuation = false 83 | ij_java_doc_keep_empty_lines = true 84 | ij_java_doc_keep_empty_parameter_tag = true 85 | ij_java_doc_keep_empty_return_tag = true 86 | ij_java_doc_keep_empty_throws_tag = true 87 | ij_java_doc_keep_invalid_tags = true 88 | ij_java_doc_param_description_on_new_line = false 89 | ij_java_doc_preserve_line_breaks = false 90 | ij_java_doc_use_throws_not_exception_tag = true 91 | ij_java_else_on_new_line = false 92 | ij_java_entity_dd_suffix = EJB 93 | ij_java_entity_eb_suffix = Bean 94 | ij_java_entity_hi_suffix = Home 95 | ij_java_entity_lhi_prefix = Local 96 | ij_java_entity_lhi_suffix = Home 97 | ij_java_entity_li_prefix = Local 98 | ij_java_entity_pk_class = java.lang.String 99 | ij_java_entity_vo_suffix = VO 100 | ij_java_enum_constants_wrap = off 101 | ij_java_extends_keyword_wrap = off 102 | ij_java_extends_list_wrap = off 103 | ij_java_field_annotation_wrap = split_into_lines 104 | ij_java_finally_on_new_line = false 105 | ij_java_for_brace_force = never 106 | ij_java_for_statement_new_line_after_left_paren = false 107 | ij_java_for_statement_right_paren_on_new_line = false 108 | ij_java_for_statement_wrap = off 109 | ij_java_generate_final_locals = false 110 | ij_java_generate_final_parameters = false 111 | ij_java_if_brace_force = never 112 | ij_java_imports_layout = *, |, javax.**, java.**, |, $* 113 | ij_java_indent_case_from_switch = true 114 | ij_java_insert_inner_class_imports = false 115 | ij_java_insert_override_annotation = true 116 | ij_java_keep_blank_lines_before_right_brace = 2 117 | ij_java_keep_blank_lines_between_package_declaration_and_header = 2 118 | ij_java_keep_blank_lines_in_code = 2 119 | ij_java_keep_blank_lines_in_declarations = 2 120 | ij_java_keep_control_statement_in_one_line = true 121 | ij_java_keep_first_column_comment = true 122 | ij_java_keep_indents_on_empty_lines = false 123 | ij_java_keep_line_breaks = true 124 | ij_java_keep_multiple_expressions_in_one_line = false 125 | ij_java_keep_simple_blocks_in_one_line = false 126 | ij_java_keep_simple_classes_in_one_line = false 127 | ij_java_keep_simple_lambdas_in_one_line = false 128 | ij_java_keep_simple_methods_in_one_line = false 129 | ij_java_lambda_brace_style = end_of_line 130 | ij_java_layout_static_imports_separately = true 131 | ij_java_line_comment_add_space = false 132 | ij_java_line_comment_at_first_column = true 133 | ij_java_message_dd_suffix = EJB 134 | ij_java_message_eb_suffix = Bean 135 | ij_java_method_annotation_wrap = split_into_lines 136 | ij_java_method_brace_style = end_of_line 137 | ij_java_method_call_chain_wrap = on_every_item 138 | ij_java_method_parameters_new_line_after_left_paren = false 139 | ij_java_method_parameters_right_paren_on_new_line = false 140 | ij_java_method_parameters_wrap = normal 141 | ij_java_modifier_list_wrap = false 142 | ij_java_names_count_to_use_import_on_demand = 3 143 | ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.* 144 | ij_java_parameter_annotation_wrap = off 145 | ij_java_parentheses_expression_new_line_after_left_paren = false 146 | ij_java_parentheses_expression_right_paren_on_new_line = false 147 | ij_java_place_assignment_sign_on_next_line = false 148 | ij_java_prefer_longer_names = true 149 | ij_java_prefer_parameters_wrap = false 150 | ij_java_repeat_synchronized = true 151 | ij_java_replace_instanceof_and_cast = false 152 | ij_java_replace_null_check = true 153 | ij_java_replace_sum_lambda_with_method_ref = true 154 | ij_java_resource_list_new_line_after_left_paren = false 155 | ij_java_resource_list_right_paren_on_new_line = false 156 | ij_java_resource_list_wrap = off 157 | ij_java_session_dd_suffix = EJB 158 | ij_java_session_eb_suffix = Bean 159 | ij_java_session_hi_suffix = Home 160 | ij_java_session_lhi_prefix = Local 161 | ij_java_session_lhi_suffix = Home 162 | ij_java_session_li_prefix = Local 163 | ij_java_session_si_suffix = Service 164 | ij_java_space_after_closing_angle_bracket_in_type_argument = false 165 | ij_java_space_after_colon = true 166 | ij_java_space_after_comma = true 167 | ij_java_space_after_comma_in_type_arguments = true 168 | ij_java_space_after_for_semicolon = true 169 | ij_java_space_after_quest = true 170 | ij_java_space_after_type_cast = true 171 | ij_java_space_before_annotation_array_initializer_left_brace = false 172 | ij_java_space_before_annotation_parameter_list = false 173 | ij_java_space_before_array_initializer_left_brace = false 174 | ij_java_space_before_catch_keyword = true 175 | ij_java_space_before_catch_left_brace = true 176 | ij_java_space_before_catch_parentheses = true 177 | ij_java_space_before_class_left_brace = true 178 | ij_java_space_before_colon = true 179 | ij_java_space_before_colon_in_foreach = true 180 | ij_java_space_before_comma = false 181 | ij_java_space_before_do_left_brace = true 182 | ij_java_space_before_else_keyword = true 183 | ij_java_space_before_else_left_brace = true 184 | ij_java_space_before_finally_keyword = true 185 | ij_java_space_before_finally_left_brace = true 186 | ij_java_space_before_for_left_brace = true 187 | ij_java_space_before_for_parentheses = true 188 | ij_java_space_before_for_semicolon = false 189 | ij_java_space_before_if_left_brace = true 190 | ij_java_space_before_if_parentheses = true 191 | ij_java_space_before_method_call_parentheses = false 192 | ij_java_space_before_method_left_brace = true 193 | ij_java_space_before_method_parentheses = false 194 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false 195 | ij_java_space_before_quest = true 196 | ij_java_space_before_switch_left_brace = true 197 | ij_java_space_before_switch_parentheses = true 198 | ij_java_space_before_synchronized_left_brace = true 199 | ij_java_space_before_synchronized_parentheses = true 200 | ij_java_space_before_try_left_brace = true 201 | ij_java_space_before_try_parentheses = true 202 | ij_java_space_before_type_parameter_list = false 203 | ij_java_space_before_while_keyword = true 204 | ij_java_space_before_while_left_brace = true 205 | ij_java_space_before_while_parentheses = true 206 | ij_java_space_inside_one_line_enum_braces = false 207 | ij_java_space_within_empty_array_initializer_braces = false 208 | ij_java_space_within_empty_method_call_parentheses = false 209 | ij_java_space_within_empty_method_parentheses = false 210 | ij_java_spaces_around_additive_operators = true 211 | ij_java_spaces_around_assignment_operators = true 212 | ij_java_spaces_around_bitwise_operators = true 213 | ij_java_spaces_around_equality_operators = true 214 | ij_java_spaces_around_lambda_arrow = true 215 | ij_java_spaces_around_logical_operators = true 216 | ij_java_spaces_around_method_ref_dbl_colon = false 217 | ij_java_spaces_around_multiplicative_operators = true 218 | ij_java_spaces_around_relational_operators = true 219 | ij_java_spaces_around_shift_operators = true 220 | ij_java_spaces_around_type_bounds_in_type_parameters = true 221 | ij_java_spaces_around_unary_operator = false 222 | ij_java_spaces_within_angle_brackets = false 223 | ij_java_spaces_within_annotation_parentheses = false 224 | ij_java_spaces_within_array_initializer_braces = false 225 | ij_java_spaces_within_braces = false 226 | ij_java_spaces_within_brackets = false 227 | ij_java_spaces_within_cast_parentheses = false 228 | ij_java_spaces_within_catch_parentheses = false 229 | ij_java_spaces_within_for_parentheses = false 230 | ij_java_spaces_within_if_parentheses = false 231 | ij_java_spaces_within_method_call_parentheses = false 232 | ij_java_spaces_within_method_parentheses = false 233 | ij_java_spaces_within_parentheses = false 234 | ij_java_spaces_within_switch_parentheses = false 235 | ij_java_spaces_within_synchronized_parentheses = false 236 | ij_java_spaces_within_try_parentheses = false 237 | ij_java_spaces_within_while_parentheses = false 238 | ij_java_special_else_if_treatment = true 239 | ij_java_subclass_name_suffix = Impl 240 | ij_java_ternary_operation_signs_on_next_line = false 241 | ij_java_ternary_operation_wrap = off 242 | ij_java_test_name_suffix = Test 243 | ij_java_throws_keyword_wrap = off 244 | ij_java_throws_list_wrap = off 245 | ij_java_use_external_annotations = false 246 | ij_java_use_fq_class_names = false 247 | ij_java_use_single_class_imports = true 248 | ij_java_variable_annotation_wrap = off 249 | ij_java_visibility = public 250 | ij_java_while_brace_force = never 251 | ij_java_while_on_new_line = false 252 | ij_java_wrap_comments = false 253 | ij_java_wrap_first_method_in_call_chain = false 254 | ij_java_wrap_long_lines = true 255 | 256 | 257 | [.editorconfig] 258 | ij_editorconfig_align_group_field_declarations = false 259 | ij_editorconfig_space_after_colon = false 260 | ij_editorconfig_space_after_comma = true 261 | ij_editorconfig_space_before_colon = false 262 | ij_editorconfig_space_before_comma = false 263 | ij_editorconfig_spaces_around_assignment_operators = true 264 | 265 | 266 | [pom.xml] 267 | ij_xml_block_comment_at_first_column = true 268 | ij_xml_keep_indents_on_empty_lines = false 269 | ij_xml_line_comment_at_first_column = true 270 | ij_xml_use_custom_settings = false 271 | 272 | 273 | [*.json] 274 | indent_size = 2 275 | ij_json_keep_blank_lines_in_code = 0 276 | ij_json_keep_indents_on_empty_lines = false 277 | ij_json_keep_line_breaks = true 278 | ij_json_space_after_colon = true 279 | ij_json_space_after_comma = true 280 | ij_json_space_before_colon = true 281 | ij_json_space_before_comma = false 282 | ij_json_spaces_within_braces = false 283 | ij_json_spaces_within_brackets = false 284 | ij_json_wrap_long_lines = false 285 | 286 | [*.yml] 287 | indent_size = 2 288 | 289 | [*.yaml] 290 | indent_size = 2 291 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for reporting an issue! 2 | 3 | Before you post... 4 | * Try the latest version, it may have fixed the issue already. 5 | * Check other issues to see if your's has already been reported. 6 | 7 | If you think your issue still exists... 8 | * Make sure to post the log and manifest you are using when your issue happens -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up JDK 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 11 16 | - name: Build with Maven 17 | run: mvn -B package --file pom.xml 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | target/ 5 | cache/ 6 | all/ 7 | *.jar 8 | logs/ 9 | .idea/ 10 | *.iml 11 | out/ 12 | /**/dependency-reduced-pom.xml 13 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | Be cool to everyone. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Be cool to everyone. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * Not being cool to everyone. 16 | 17 | ## Our Responsibilities 18 | 19 | Project maintainers are responsible for being cool to everyone. 20 | 21 | ## Scope 22 | 23 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 24 | 25 | ## Enforcement 26 | 27 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@nincraft.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 28 | 29 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 30 | 31 | ## Attribution 32 | 33 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 34 | 35 | [homepage]: http://contributor-covenant.org 36 | [version]: http://contributor-covenant.org/version/1/4/ 37 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | node { 2 | stage 'Checkout' 3 | git branch: env.BRANCH_NAME, url: 'https://github.com/Nincraft/ModPackDownloader.git' 4 | 5 | stage 'Build' 6 | bat 'mvnw.cmd clean install' 7 | 8 | stage 'Archive' 9 | archive excludes: '**/target/Mod*-sources.jar,**/target/Mod*-javadoc.jar', includes: '**/target/Mod*.jar, modpackdownloader-core/target/classes/latest.json' 10 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModPack Downloader [![CI](https://github.com/Nincraft/ModPackDownloader/actions/workflows/maven.yml/badge.svg)](https://github.com/Nincraft/ModPackDownloader/actions/workflows/maven.yml) 2 | This project is not actively maintained. Please consider finding an alternative downloader. 3 | 4 | A simple command line downloader for Minecraft Forge Modpacks. Also works with Curse manifest JSONs. 5 | 6 | # Usage 7 | Execute via command line with two arguments, the manifest json and the folder where you want your mods downloaded. 8 | 9 | Example: java -jar modpackdownloader.jar -manifest mods.json -folder mods 10 | 11 | This will read the mods.json and download all mods to the mods folder. 12 | 13 | It can also be ran without any arguments and default to manifest.json for the manifest and mods for the download folder. 14 | 15 | For additional examples check out some of our modpacks that implement this: 16 | - [NEB3 Modpack Repository](https://github.com/Nincraft/NincraftElectricBoogaloo3TheLightAmongTheLongForgottenDarkness/tree/develop) 17 | - [TWBB Modpack Repository](https://github.com/UndeadZeratul/ThereWillBeBlood/tree/develop) 18 | - [TWBB2 Modpack Repository](https://github.com/UndeadZeratul/ThereWillBeBlood2/tree/develop) 19 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-time-machine -------------------------------------------------------------------------------- /modpackdownloader-cli/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.nincraft 7 | modpackdownloader 8 | 0.7.2 9 | 10 | 11 | modpackdownloader-cli 12 | 13 | Modpack Downloader CLI 14 | 15 | 16 | 17 | com.nincraft 18 | modpackdownloader-core 19 | 20 | 21 | com.beust 22 | jcommander 23 | 24 | 25 | org.projectlombok 26 | lombok 27 | provided 28 | 29 | 30 | junit 31 | junit 32 | 4.13 33 | 34 | 35 | 36 | 37 | ModpackDownloader-cli-${short.project.version} 38 | 39 | 40 | org.codehaus.mojo 41 | build-helper-maven-plugin 42 | 1.7 43 | 44 | 45 | regex-property 46 | process-resources 47 | 48 | regex-property 49 | 50 | 51 | short.project.version 52 | ${project.version} 53 | ^(\d\.\d(?>\.\d)?)(-SNAPSHOT)? 54 | $1 55 | true 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-shade-plugin 63 | 2.4.3 64 | 65 | 66 | package 67 | 68 | shade 69 | 70 | 71 | 72 | 74 | com.nincraft.modpackdownloader.cli.ModpackDownloaderCLI 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /modpackdownloader-cli/src/main/java/com/nincraft/modpackdownloader/cli/ModpackDownloaderCLI.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.cli; 2 | 3 | import com.nincraft.modpackdownloader.ModpackDownloaderManager; 4 | import com.nincraft.modpackdownloader.handler.ApplicationUpdateHandler; 5 | import com.nincraft.modpackdownloader.util.Arguments; 6 | import com.nincraft.modpackdownloader.util.FileSystemHelper; 7 | import lombok.extern.log4j.Log4j2; 8 | 9 | import java.util.Arrays; 10 | 11 | @Log4j2 12 | public class ModpackDownloaderCLI { 13 | 14 | public static void main(final String[] args) throws InterruptedException { 15 | log.info("Starting ModpackDownloaderManager with arguments: {}", Arrays.toString(args)); 16 | ModpackDownloaderManager modpackDownloaderManager = new ModpackDownloaderManager(args); 17 | 18 | Arguments arguments = modpackDownloaderManager.getArguments(); 19 | 20 | if (arguments.isHelpEnabled()) { 21 | modpackDownloaderManager.getJCommander().usage(); 22 | return; 23 | } 24 | 25 | modpackDownloaderManager.init(); 26 | 27 | if (arguments.isClearCache()) { 28 | FileSystemHelper.clearCache(); 29 | return; 30 | } 31 | if (arguments.isUpdateApp()) { 32 | ApplicationUpdateHandler.update(); 33 | return; 34 | } 35 | modpackDownloaderManager.processManifests(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /modpackdownloader-cli/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /modpackdownloader-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.nincraft 7 | modpackdownloader 8 | 0.7.2 9 | 10 | 11 | modpackdownloader-core 12 | 13 | Modpack Downloader Core 14 | 15 | 16 | 2.17.1 17 | 18 | 19 | 20 | 21 | com.googlecode.json-simple 22 | json-simple 23 | 1.1.1 24 | 25 | 26 | org.apache.logging.log4j 27 | log4j-api 28 | ${log4j.version} 29 | 30 | 31 | org.apache.commons 32 | commons-collections4 33 | 4.4 34 | 35 | 36 | org.apache.commons 37 | commons-lang3 38 | 3.11 39 | 40 | 41 | org.apache.logging.log4j 42 | log4j-core 43 | ${log4j.version} 44 | 45 | 46 | commons-io 47 | commons-io 48 | 2.7 49 | 50 | 51 | org.projectlombok 52 | lombok 53 | provided 54 | 55 | 56 | com.google.guava 57 | guava 58 | 29.0-jre 59 | 60 | 61 | com.google.code.gson 62 | gson 63 | 2.8.6 64 | 65 | 66 | net.lingala.zip4j 67 | zip4j 68 | 2.6.1 69 | 70 | 71 | com.beust 72 | jcommander 73 | 74 | 75 | 76 | 77 | ModpackDownloader-core-${short.project.version} 78 | 79 | 80 | src/main/resources 81 | true 82 | 83 | 84 | 85 | 86 | org.codehaus.mojo 87 | build-helper-maven-plugin 88 | 1.7 89 | 90 | 91 | regex-property 92 | process-resources 93 | 94 | regex-property 95 | 96 | 97 | short.project.version 98 | ${project.version} 99 | ^(\d\.\d(?>\.\d)?)(-SNAPSHOT)? 100 | $1 101 | true 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-resources-plugin 109 | 2.6 110 | 111 | 112 | resources 113 | process-resources 114 | 115 | resources 116 | 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-source-plugin 123 | 124 | 125 | attach-sources 126 | 127 | jar 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-javadoc-plugin 135 | 136 | 137 | attach-javadocs 138 | 139 | jar 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/ModpackDownloaderManager.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import com.google.common.base.Strings; 5 | import com.google.common.collect.Lists; 6 | import com.nincraft.modpackdownloader.processor.DownloadModsProcessor; 7 | import com.nincraft.modpackdownloader.processor.MergeManifestsProcessor; 8 | import com.nincraft.modpackdownloader.processor.UpdateModsProcessor; 9 | import com.nincraft.modpackdownloader.util.Arguments; 10 | import com.nincraft.modpackdownloader.util.DownloadHelper; 11 | import com.nincraft.modpackdownloader.util.FileSystemHelper; 12 | import com.nincraft.modpackdownloader.util.Reference; 13 | import lombok.Getter; 14 | import lombok.extern.log4j.Log4j2; 15 | import lombok.val; 16 | import org.apache.commons.collections4.CollectionUtils; 17 | import org.apache.commons.lang3.StringUtils; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * ModpackDownloaderManager is the main class for starting a download/update. 23 | */ 24 | @Log4j2 25 | public class ModpackDownloaderManager { 26 | 27 | private Reference reference = Reference.getInstance(); 28 | private DownloadHelper downloadHelper; 29 | @Getter 30 | private Arguments arguments; 31 | @Getter 32 | private JCommander jCommander; 33 | 34 | /** 35 | * Initializes arguments and jCommander 36 | * 37 | * @param args String[] of arguments for execution. See {@link Arguments} for all current parameters 38 | */ 39 | public ModpackDownloaderManager(String[] args){ 40 | arguments = new Arguments(); 41 | jCommander = initArguments(args); 42 | } 43 | 44 | /** 45 | * Starts processing the manifest files passed in. This includes downloading a modpack, updating the mods in a 46 | * manifest, downloading individual mods, and merging all manifests. 47 | * @throws InterruptedException exception is thrown if a processor is interrupted 48 | */ 49 | public void processManifests() throws InterruptedException { 50 | log.trace("Processing Manifests..."); 51 | downloadHelper = new DownloadHelper(arguments); 52 | updateMods(arguments); 53 | downloadMods(arguments); 54 | mergeManifests(arguments); 55 | 56 | log.trace("Finished Processing Manifests."); 57 | } 58 | 59 | private void updateMods(Arguments arguments) throws InterruptedException { 60 | if (shouldProcessUpdate(arguments)) { 61 | new UpdateModsProcessor(arguments, downloadHelper).process(); 62 | } 63 | } 64 | 65 | private boolean shouldProcessUpdate(Arguments arguments) { 66 | return arguments.isUpdateMods() || !StringUtils.isBlank(arguments.getCheckMCUpdate()); 67 | } 68 | 69 | private void downloadMods(Arguments arguments) throws InterruptedException { 70 | if (arguments.isDownloadMods()) { 71 | new DownloadModsProcessor(arguments, downloadHelper).process(); 72 | } 73 | } 74 | 75 | private void mergeManifests(Arguments arguments) throws InterruptedException { 76 | if (arguments.isMergeManifests()) { 77 | new MergeManifestsProcessor(arguments, downloadHelper).process(); 78 | } 79 | } 80 | 81 | private void defaultArguments(Arguments arguments) { 82 | if (CollectionUtils.isEmpty(arguments.getManifests())) { 83 | log.debug("No manifest supplied, using default {}", reference.getDefaultManifestFile()); 84 | arguments.setManifests(Lists.newArrayList(new File(reference.getDefaultManifestFile()))); 85 | } 86 | if (Strings.isNullOrEmpty(arguments.getModFolder())) { 87 | log.debug("No output folder supplied, using default \"mods\""); 88 | arguments.setModFolder("mods"); 89 | } 90 | if (noProcessArgsSupplied(arguments)) { 91 | arguments.setDownloadMods(true); 92 | } 93 | } 94 | 95 | private boolean noProcessArgsSupplied(Arguments arguments) { 96 | return !arguments.isDownloadMods() && !arguments.isUpdateMods() && !arguments.isMergeManifests() 97 | && StringUtils.isBlank(arguments.getCheckMCUpdate()); 98 | } 99 | 100 | private void setupRepo() { 101 | log.trace("Setting up local repository..."); 102 | StringBuilder stringBuilder = new StringBuilder(); 103 | stringBuilder.append(System.getProperty("user.home")); 104 | log.debug("User Home System Property detected as: {}", stringBuilder.toString()); 105 | 106 | val os = System.getProperty("os.name"); 107 | log.debug("Operating System detected as: {}", os); 108 | 109 | if (os.startsWith("Windows")) { 110 | stringBuilder.append(reference.getWindowsFolder()); 111 | } else if (os.startsWith("Mac")) { 112 | stringBuilder.append(reference.getMacFolder()); 113 | } else { 114 | stringBuilder.append(reference.getOtherFolder()); 115 | } 116 | reference.setUserhome(stringBuilder.toString()); 117 | 118 | log.debug("User Home Folder set to: {}", reference.getUserhome()); 119 | 120 | FileSystemHelper.createFolder(reference.getUserhome()); 121 | 122 | log.debug("Setting User Agent..."); 123 | System.setProperty("http.agent", "Mozilla/4.0"); 124 | 125 | log.trace("Finished setting up local repository."); 126 | } 127 | 128 | /** 129 | * Initializes the default arguments and sets up the local repo if needed 130 | */ 131 | public void init() { 132 | defaultArguments(arguments); 133 | setupRepo(); 134 | } 135 | 136 | private JCommander initArguments(final String[] args) { 137 | // Initialize application arguments 138 | arguments = new Arguments(); 139 | return new JCommander(arguments, null, args); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/CurseFile.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.gson.annotations.Expose; 5 | import com.google.gson.annotations.SerializedName; 6 | import com.nincraft.modpackdownloader.util.Reference; 7 | import com.nincraft.modpackdownloader.util.URLHelper; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.extern.log4j.Log4j2; 11 | import lombok.val; 12 | import org.apache.commons.lang3.math.NumberUtils; 13 | import org.json.simple.parser.ParseException; 14 | 15 | import java.io.IOException; 16 | 17 | @Log4j2 18 | @Data 19 | @EqualsAndHashCode(callSuper = false, exclude = {"fileID"}) 20 | public class CurseFile extends Mod { 21 | 22 | @SerializedName("fileID") 23 | @Expose 24 | public Integer fileID; 25 | @SerializedName("projectID") 26 | @Expose 27 | public Integer projectID; 28 | @SerializedName("release") 29 | @Expose 30 | public String releaseType; 31 | @SerializedName("skipUpdate") 32 | @Expose 33 | private Boolean skipUpdate; 34 | private String projectUrl; 35 | private String projectName; 36 | private boolean curseForge; 37 | private String fileExtension; 38 | private Reference reference = Reference.getInstance(); 39 | 40 | public CurseFile() { 41 | curseForge = true; 42 | fileExtension = reference.getJarFileExt(); 43 | } 44 | 45 | public CurseFile(CurseFile curseFile) { 46 | super(curseFile); 47 | fileID = curseFile.fileID; 48 | projectID = curseFile.projectID; 49 | releaseType = curseFile.releaseType; 50 | skipUpdate = curseFile.skipUpdate; 51 | projectUrl = curseFile.projectUrl; 52 | projectName = curseFile.projectName; 53 | curseForge = curseFile.curseForge; 54 | fileExtension = curseFile.fileExtension; 55 | } 56 | 57 | public CurseFile(String projectId, String projectName) { 58 | if (NumberUtils.isParsable(projectId)) { 59 | setProjectID(Integer.parseInt(projectId)); 60 | } 61 | setProjectName(projectName); 62 | curseForge = true; 63 | } 64 | 65 | public String getCurseforgeWidgetJson() { 66 | return reference.getCurseforgeWidgetJsonMod(); 67 | } 68 | 69 | @Override 70 | public void init() { 71 | resolveProjectName(); 72 | setProjectUrl(buildProjectUrl()); 73 | setDownloadUrl(getCurseForgeDownloadUrl()); 74 | } 75 | 76 | private void resolveProjectName() { 77 | if (Strings.isNullOrEmpty(getProjectName()) || Strings.isNullOrEmpty(getName())) { 78 | try { 79 | val response = URLHelper.getJsonFromUrl("https://addons-ecs.forgesvc.net/api/v2/addon/" + projectID); 80 | val newProjectName = response.get("slug").toString(); 81 | 82 | if (Strings.isNullOrEmpty(getProjectName())) { 83 | setProjectName(newProjectName); 84 | } 85 | 86 | if (Strings.isNullOrEmpty(getName())) { 87 | setName(newProjectName); 88 | } 89 | } catch (IOException | ParseException e) { 90 | log.error(e); 91 | } 92 | } 93 | } 94 | 95 | private String buildProjectUrl() { 96 | return String.format(reference.getCurseforgeBaseUrl() + "%s" + reference.getCookieTest1(), getProjectID()); 97 | } 98 | 99 | public String getCurseForgeDownloadUrl() { 100 | val baseUrl = curseForge ? reference.getCurseForgeBaseDownloadUrl() : reference.getFtbBaseUrl(); 101 | return String.format(baseUrl + "%s/download/%s/file", getProjectName(), getFileID()); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/CurseModpackFile.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import com.nincraft.modpackdownloader.util.Reference; 4 | import lombok.EqualsAndHashCode; 5 | 6 | @EqualsAndHashCode(callSuper = false) 7 | public class CurseModpackFile extends CurseFile { 8 | 9 | private Reference reference = Reference.getInstance(); 10 | 11 | public CurseModpackFile(String projectId, String projectName, int fileId) { 12 | super(projectId, projectName); 13 | setFileID(fileId); 14 | setFileExtension(reference.getZipFileExt()); 15 | } 16 | 17 | @Override 18 | public String getCurseforgeWidgetJson() { 19 | return reference.getCurseforgeWidgetJsonModpack(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/DownloadableFile.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.gson.annotations.Expose; 5 | import com.google.gson.annotations.SerializedName; 6 | import lombok.Data; 7 | 8 | @Data 9 | public abstract class DownloadableFile { 10 | @SerializedName("name") 11 | @Expose 12 | public String name; 13 | @SerializedName("rename") 14 | @Expose 15 | private String rename; 16 | @SerializedName("skipDownload") 17 | @Expose 18 | private Boolean skipDownload; 19 | @SerializedName("folder") 20 | @Expose 21 | private String folder; 22 | private String fileName; 23 | private String downloadUrl; 24 | 25 | DownloadableFile() { 26 | 27 | } 28 | 29 | DownloadableFile(DownloadableFile downloadableFile) { 30 | name = downloadableFile.name; 31 | rename = downloadableFile.rename; 32 | skipDownload = downloadableFile.skipDownload; 33 | folder = downloadableFile.folder; 34 | fileName = downloadableFile.fileName; 35 | downloadUrl = downloadableFile.downloadUrl; 36 | } 37 | 38 | public String getFileName() { 39 | if (!Strings.isNullOrEmpty(getRename())) { 40 | return getRename(); 41 | } 42 | return this.fileName; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/Manifest.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @Getter 12 | @Setter 13 | public class Manifest { 14 | 15 | @SerializedName("minecraft") 16 | @Expose 17 | private Minecraft minecraft; 18 | @SerializedName("manifestType") 19 | @Expose 20 | private String manifestType; 21 | @SerializedName("manifestVersion") 22 | @Expose 23 | private Integer manifestVersion; 24 | @SerializedName("name") 25 | @Expose 26 | private String name; 27 | @SerializedName("version") 28 | @Expose 29 | private String version; 30 | @SerializedName("author") 31 | @Expose 32 | private String author; 33 | @SerializedName("files") 34 | @Expose 35 | private List curseFiles = new ArrayList<>(); 36 | @SerializedName("thirdParty") 37 | @Expose 38 | private List thirdParty = new ArrayList<>(); 39 | @SerializedName("batchAddCurse") 40 | @Expose 41 | private List batchAddCurse = new ArrayList<>(); 42 | @SerializedName("overrides") 43 | @Expose 44 | private String overrides; 45 | 46 | public String getMinecraftVersion() { 47 | if (minecraft != null) { 48 | return minecraft.getVersion(); 49 | } 50 | return null; 51 | } 52 | 53 | public String getForgeVersion() { 54 | if (isMinecraftEmpty()) { 55 | return minecraft.getModLoaders().get(0).getId(); 56 | } 57 | return null; 58 | } 59 | 60 | private boolean isMinecraftEmpty() { 61 | return minecraft != null && !minecraft.getModLoaders().isEmpty(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/Minecraft.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @Getter 12 | @Setter 13 | public class Minecraft { 14 | 15 | @SerializedName("version") 16 | @Expose 17 | public String version; 18 | @SerializedName("modLoaders") 19 | @Expose 20 | public List modLoaders = new ArrayList<>(); 21 | 22 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/Mod.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | @EqualsAndHashCode(callSuper = true) 7 | @Data 8 | public abstract class Mod extends DownloadableFile implements Cloneable { 9 | private String version; 10 | 11 | 12 | Mod() { 13 | } 14 | 15 | Mod(Mod mod) { 16 | super(mod); 17 | version = mod.version; 18 | } 19 | 20 | public abstract void init(); 21 | } 22 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/ModLoader.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | 9 | 10 | @Getter 11 | @Setter 12 | @EqualsAndHashCode(callSuper = false) 13 | public class ModLoader extends DownloadableFile { 14 | 15 | @SerializedName("id") 16 | @Expose 17 | private String id; 18 | @SerializedName("primary") 19 | @Expose 20 | private Boolean primary; 21 | @SerializedName("downloadInstaller") 22 | @Expose 23 | private Boolean downloadInstaller; 24 | @SerializedName("downloadUniversal") 25 | @Expose 26 | private Boolean downloadUniversal; 27 | @SerializedName("renameInstaller") 28 | @Expose 29 | private String renameInstaller; 30 | @SerializedName("renameUniversal") 31 | @Expose 32 | private String renameUniversal; 33 | @SerializedName("release") 34 | @Expose 35 | private String release; 36 | 37 | public ModLoader() { 38 | setName("forge"); 39 | } 40 | 41 | public String getRename(boolean downloadInstaller) { 42 | return downloadInstaller ? getRenameInstaller() : getRenameUniversal(); 43 | } 44 | 45 | public String getForgeId() { 46 | return getId().substring(getId().indexOf("-") + 1); 47 | } 48 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/container/ThirdParty.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.container; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import lombok.EqualsAndHashCode; 6 | 7 | 8 | @EqualsAndHashCode(callSuper = false) 9 | public class ThirdParty extends Mod { 10 | 11 | @SerializedName("url") 12 | @Expose 13 | private String url; 14 | 15 | private String buildFileName() { 16 | return getDownloadUrl().contains(".jar") ? getDownloadUrl().substring(getDownloadUrl().lastIndexOf('/') + 1, 17 | getDownloadUrl().lastIndexOf(".jar") + 4) : getRename(); 18 | } 19 | 20 | @Override 21 | public void init() { 22 | setDownloadUrl(url); 23 | setFileName(buildFileName()); 24 | } 25 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/handler/ApplicationUpdateHandler.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.handler; 2 | 3 | import com.nincraft.modpackdownloader.util.FileSystemHelper; 4 | import com.nincraft.modpackdownloader.util.Reference; 5 | import com.nincraft.modpackdownloader.util.URLHelper; 6 | import lombok.experimental.UtilityClass; 7 | import lombok.extern.log4j.Log4j2; 8 | import lombok.val; 9 | import org.apache.commons.io.FileUtils; 10 | import org.json.simple.JSONObject; 11 | import org.json.simple.parser.ParseException; 12 | 13 | import java.io.IOException; 14 | import java.net.URL; 15 | import java.net.URLDecoder; 16 | 17 | @UtilityClass 18 | @Log4j2 19 | public class ApplicationUpdateHandler { 20 | 21 | private Reference reference = Reference.getInstance(); 22 | 23 | public void update() { 24 | JSONObject appJson; 25 | try { 26 | appJson = URLHelper.getJsonFromUrl(reference.getUpdateAppUrl()); 27 | } catch (IOException | ParseException e) { 28 | log.error("Failed to get latest download link, Nincraft server down?", e); 29 | return; 30 | } 31 | val downloadUrl = (String) appJson.get("url"); 32 | 33 | val appName = downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1); 34 | String decodedAppName = null; 35 | try { 36 | decodedAppName = URLDecoder.decode(appName, "UTF-8"); 37 | } catch (IOException e) { 38 | log.error("Error Decoding App Name: {}", appName, e); 39 | } 40 | 41 | val updatedApp = FileSystemHelper.getDownloadedFile(decodedAppName, "."); 42 | if (updatedApp.exists()) { 43 | log.info("No new updates found"); 44 | return; 45 | } else { 46 | log.info("Update found, downloading {}", decodedAppName); 47 | } 48 | try { 49 | FileUtils.copyURLToFile(new URL(downloadUrl), updatedApp); 50 | log.info("Downloaded {}", decodedAppName); 51 | } catch (IOException e) { 52 | log.error("Failed to download update", e); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/handler/CurseFileHandler.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.handler; 2 | 3 | import com.google.common.base.Strings; 4 | import com.nincraft.modpackdownloader.container.CurseFile; 5 | import com.nincraft.modpackdownloader.container.CurseModpackFile; 6 | import com.nincraft.modpackdownloader.container.Mod; 7 | import com.nincraft.modpackdownloader.summary.UpdateCheckSummarizer; 8 | import com.nincraft.modpackdownloader.util.Arguments; 9 | import com.nincraft.modpackdownloader.util.DownloadHelper; 10 | import com.nincraft.modpackdownloader.util.Reference; 11 | import com.nincraft.modpackdownloader.util.URLHelper; 12 | import lombok.extern.log4j.Log4j2; 13 | import lombok.val; 14 | import org.apache.commons.collections4.CollectionUtils; 15 | import org.apache.commons.lang3.BooleanUtils; 16 | import org.json.simple.JSONArray; 17 | import org.json.simple.JSONObject; 18 | import org.json.simple.parser.ParseException; 19 | 20 | import javax.net.ssl.HttpsURLConnection; 21 | import javax.net.ssl.SSLContext; 22 | import javax.net.ssl.TrustManager; 23 | import javax.net.ssl.X509TrustManager; 24 | import java.io.FileNotFoundException; 25 | import java.io.IOException; 26 | import java.net.HttpURLConnection; 27 | import java.net.URL; 28 | import java.net.URLEncoder; 29 | import java.util.ArrayList; 30 | import java.util.Collections; 31 | import java.util.List; 32 | 33 | @Log4j2 34 | public class CurseFileHandler implements ModHandler { 35 | 36 | private Reference reference = Reference.getInstance(); 37 | private UpdateCheckSummarizer updateCheckSummarizer = UpdateCheckSummarizer.getInstance(); 38 | private Arguments arguments; 39 | private DownloadHelper downloadHelper; 40 | 41 | public CurseFileHandler(Arguments arguments, DownloadHelper downloadHelper) { 42 | this.arguments = arguments; 43 | this.downloadHelper = downloadHelper; 44 | } 45 | 46 | private void downloadCurseMod(CurseFile curseFile) { 47 | try { 48 | downloadHelper.downloadFile(getCurseForgeDownloadLocation(curseFile)); 49 | } catch (Exception e) { 50 | log.error(e); 51 | } 52 | } 53 | 54 | public CurseFile getCurseForgeDownloadLocation(final CurseFile curseFile) throws IOException, ParseException { 55 | val url = curseFile.getCurseForgeDownloadUrl(); 56 | val projectName = curseFile.getName(); 57 | String encodedFilename = URLEncoder.encode(projectName, "UTF-8"); 58 | 59 | if (!encodedFilename.contains(reference.getJarFileExt()) 60 | || !encodedFilename.contains(reference.getZipFileExt())) { 61 | val fileInfoResponse = URLHelper.getJsonFromUrl(String.format("https://addons-ecs.forgesvc" 62 | + ".net/api/v2/addon/%s/file/%s", curseFile 63 | .getProjectID(), curseFile.getFileID())); 64 | val fileName = fileInfoResponse.get("fileName").toString(); 65 | val downloadUrl = fileInfoResponse.get("downloadUrl").toString(); 66 | curseFile.setDownloadUrl(downloadUrl); 67 | encodedFilename = getEncodedFilename(projectName, fileName); 68 | } 69 | curseFile.setFileName(encodedFilename); 70 | 71 | return curseFile; 72 | } 73 | 74 | private String getEncodedFilename(String projectName, String filename) { 75 | if (filename.contains(reference.getJarFileExt()) || filename.contains(reference.getZipFileExt())) { 76 | return filename; 77 | } else { 78 | return projectName + reference.getJarFileExt(); 79 | } 80 | } 81 | 82 | public void updateCurseFile(final CurseFile curseFile) { 83 | if (BooleanUtils.isTrue(curseFile.getSkipUpdate())) { 84 | log.debug("Skipped updating {}", curseFile.getName()); 85 | return; 86 | } 87 | disableCertValidation(); 88 | JSONObject fileListJson = new JSONObject(); 89 | try { 90 | val conn = (HttpURLConnection) new URL(curseFile.getProjectUrl()).openConnection(); 91 | conn.setInstanceFollowRedirects(false); 92 | conn.connect(); 93 | 94 | fileListJson.put("curse", getCurseProjectJson(curseFile).get("files")); 95 | 96 | if (fileListJson.get("curse") == null) { 97 | log.error("No file list found for {}, and will be skipped.", curseFile.getName()); 98 | return; 99 | } 100 | } catch (IOException | ParseException e) { 101 | log.error(e); 102 | return; 103 | } 104 | 105 | val newMod = getLatestVersion(curseFile.getReleaseType() 106 | != null ? curseFile.getReleaseType() : arguments.getReleaseType(), curseFile, fileListJson, null); 107 | if (curseFile.getFileID().compareTo(newMod.getFileID()) < 0) { 108 | log.debug("Update found for {}. Most recent version is {}.", curseFile.getName(), newMod.getVersion()); 109 | updateCurseFile(curseFile, newMod); 110 | updateCheckSummarizer.getModList().add(curseFile); 111 | } 112 | } 113 | 114 | private void disableCertValidation() { 115 | TrustManager[] trustAllCerts = new TrustManager[]{ 116 | new X509TrustManager() { 117 | public java.security.cert.X509Certificate[] getAcceptedIssuers() { 118 | return null; 119 | } 120 | 121 | public void checkClientTrusted( 122 | java.security.cert.X509Certificate[] certs, String authType) { 123 | } 124 | 125 | public void checkServerTrusted( 126 | java.security.cert.X509Certificate[] certs, String authType) { 127 | } 128 | } 129 | }; 130 | 131 | try { 132 | SSLContext sc = SSLContext.getInstance("SSL"); 133 | sc.init(null, trustAllCerts, new java.security.SecureRandom()); 134 | HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 135 | } catch (Exception e) { 136 | } 137 | } 138 | 139 | private void updateCurseFile(CurseFile curseFile, CurseFile newMod) { 140 | curseFile.setFileID(newMod.getFileID()); 141 | curseFile.setVersion(newMod.getVersion()); 142 | if (curseFile instanceof CurseModpackFile) { 143 | curseFile.setFileName(newMod.getVersion()); 144 | } 145 | } 146 | 147 | private CurseFile getLatestVersion(String releaseType, 148 | CurseFile curseFile, final JSONObject fileListJson, String mcVersion) { 149 | log.trace("Getting most recent available file..."); 150 | boolean backup = true; 151 | if (Strings.isNullOrEmpty(mcVersion)) { 152 | mcVersion = arguments.getMcVersion(); 153 | backup = false; 154 | } 155 | releaseType = defaultReleaseType(releaseType); 156 | CurseFile newMod = new CurseFile(curseFile); 157 | curseFile = checkFileId(curseFile); 158 | 159 | JSONArray fileList = (JSONArray) fileListJson.get("curse"); 160 | List fileIds = new ArrayList<>(); 161 | checkFileIds(releaseType, mcVersion, fileList, fileIds); 162 | Collections.sort(fileIds); 163 | Collections.reverse(fileIds); 164 | setUpdatedFileId(curseFile, fileList, newMod, fileIds); 165 | 166 | if (!"alpha".equals(releaseType) && fileIds.isEmpty()) { 167 | if (CollectionUtils.isEmpty(arguments.getBackupVersions())) { 168 | log.debug("No files found for Minecraft {}, disabling download of {}", mcVersion, curseFile.getName()); 169 | curseFile.setSkipDownload(true); 170 | } else if (!backup) { 171 | newMod = checkBackupVersions(releaseType, curseFile, fileListJson, mcVersion, newMod); 172 | } else if (fileIds.isEmpty()) { 173 | curseFile.setSkipDownload(true); 174 | newMod.setSkipDownload(true); 175 | } 176 | } 177 | if (BooleanUtils.isTrue(curseFile.getSkipDownload()) && !fileIds.isEmpty()) { 178 | log.debug("Found files for Minecraft {}, enabling download of {}", mcVersion, curseFile.getName()); 179 | newMod.setSkipDownload(null); 180 | } 181 | 182 | log.trace("Finished getting most recent available file."); 183 | return newMod; 184 | } 185 | 186 | private void setUpdatedFileId(CurseFile curseFile, JSONArray fileListJson, CurseFile newMod, List fileIds) { 187 | if (!fileIds.isEmpty() && fileIds.get(0).intValue() != curseFile.getFileID()) { 188 | newMod.setFileID(fileIds.get(0).intValue()); 189 | newMod.setVersion(getJSONFileNode(fileListJson, newMod.getFileID()).get("version").toString()); 190 | } 191 | } 192 | 193 | private JSONObject getJSONFileNode(JSONArray fileListJson, Integer fileID) { 194 | for (val jsonNode : fileListJson) { 195 | if (fileID.equals(((Long) ((JSONObject) jsonNode).get("id")).intValue())) { 196 | return (JSONObject) jsonNode; 197 | } 198 | } 199 | return new JSONObject(); 200 | } 201 | 202 | private void checkFileIds(String releaseType, String mcVersion, List fileList, List fileIds) { 203 | for (JSONObject file : fileList) { 204 | if (equalOrLessThan((String) file.get("type"), releaseType) 205 | && isMcVersion((String) file.get("version"), mcVersion)) { 206 | fileIds.add((Long) file.get("id")); 207 | } 208 | } 209 | } 210 | 211 | private String defaultReleaseType(String releaseType) { 212 | return Strings.isNullOrEmpty(releaseType) ? "release" : releaseType; 213 | } 214 | 215 | private CurseFile checkBackupVersions(String releaseType, CurseFile curseFile, JSONObject fileListJson, 216 | String mcVersion, CurseFile newMod) { 217 | CurseFile returnMod = newMod; 218 | for (String backupVersion : arguments.getBackupVersions()) { 219 | log.debug("No files found for Minecraft {}, checking backup version {}", mcVersion, backupVersion); 220 | returnMod = getLatestVersion(releaseType, curseFile, fileListJson, backupVersion); 221 | if (BooleanUtils.isFalse(newMod.getSkipDownload())) { 222 | curseFile.setSkipDownload(null); 223 | log.debug("Found update for {} in Minecraft {}", curseFile.getName(), backupVersion); 224 | break; 225 | } 226 | } 227 | return returnMod; 228 | } 229 | 230 | private boolean isMcVersion(String modVersion, String argVersion) { 231 | return "*".equals(argVersion) || modVersion.equals(argVersion); 232 | } 233 | 234 | private CurseFile checkFileId(CurseFile curseFile) { 235 | if (curseFile.getFileID() == null) { 236 | curseFile.setFileID(0); 237 | } 238 | return curseFile; 239 | } 240 | 241 | private boolean equalOrLessThan(final String modRelease, final String releaseType) { 242 | return "alpha".equals(releaseType) || releaseType.equals(modRelease) 243 | || "beta".equals(releaseType) && "release".equals(modRelease); 244 | } 245 | 246 | private JSONObject getCurseProjectJson(final CurseFile curseFile) throws ParseException, IOException { 247 | log.trace("Getting CurseForge Widget JSON..."); 248 | val projectId = curseFile.getProjectID(); 249 | val projectName = curseFile.getProjectName(); 250 | val modOrModPack = curseFile.getCurseforgeWidgetJson(); 251 | String urlStr = String.format(reference.getCurseforgeWidgetJsonUrl(), modOrModPack, projectName); 252 | log.debug(urlStr); 253 | try { 254 | return URLHelper.getJsonFromUrl(urlStr); 255 | } catch (final FileNotFoundException e) { 256 | urlStr = String.format(reference.getCurseforgeWidgetJsonUrl(), modOrModPack, projectId + "-" + projectName); 257 | log.debug(urlStr, e); 258 | return URLHelper.getJsonFromUrl(urlStr); 259 | } 260 | } 261 | 262 | @Override 263 | public void downloadMod(final Mod mod) { 264 | downloadCurseMod((CurseFile) mod); 265 | } 266 | 267 | @Override 268 | public void updateMod(final Mod mod) { 269 | updateCurseFile((CurseFile) mod); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/handler/ForgeHandler.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.handler; 2 | 3 | import com.google.common.base.Strings; 4 | import com.nincraft.modpackdownloader.container.ModLoader; 5 | import com.nincraft.modpackdownloader.status.DownloadStatus; 6 | import com.nincraft.modpackdownloader.util.*; 7 | import lombok.extern.log4j.Log4j2; 8 | import org.apache.commons.collections4.CollectionUtils; 9 | import org.apache.commons.lang3.BooleanUtils; 10 | import org.json.simple.JSONObject; 11 | import org.json.simple.parser.ParseException; 12 | 13 | import java.io.IOException; 14 | import java.util.List; 15 | 16 | @Log4j2 17 | public class ForgeHandler { 18 | 19 | private Reference reference = Reference.getInstance(); 20 | private Arguments arguments; 21 | private DownloadHelper downloadHelper; 22 | 23 | public ForgeHandler(Arguments arguments, DownloadHelper downloadHelper) { 24 | this.arguments = arguments; 25 | this.downloadHelper = downloadHelper; 26 | } 27 | 28 | public void downloadForge(String minecraftVersion, List modLoaders) { 29 | if (CollectionUtils.isEmpty(modLoaders) || Strings.isNullOrEmpty(minecraftVersion)) { 30 | log.debug("No Forge or Minecraft version found in manifest, skipping"); 31 | return; 32 | } 33 | 34 | for (ModLoader modLoader : modLoaders) { 35 | if (BooleanUtils.isTrue(modLoader.getDownloadInstaller())) { 36 | log.debug("Downloading Forge installer version {}", modLoader.getId()); 37 | downloadForgeFile(minecraftVersion, modLoader, true); 38 | } 39 | if (BooleanUtils.isTrue(modLoader.getDownloadUniversal())) { 40 | log.debug("Downloading Forge universal version {}", modLoader.getId()); 41 | downloadForgeFile(minecraftVersion, modLoader, false); 42 | } 43 | } 44 | } 45 | 46 | private void downloadForgeFile(String minecraftVersion, ModLoader modLoader, boolean downloadInstaller) { 47 | downloadForgeFile(minecraftVersion, modLoader, downloadInstaller, true); 48 | } 49 | 50 | private void downloadForgeFile(String minecraftVersion, ModLoader modLoader, boolean downloadInstaller, boolean alternateDownloadUrl) { 51 | modLoader.setRename(modLoader.getRename(downloadInstaller)); 52 | String forgeFileName = "forge-" + minecraftVersion + "-" + modLoader.getForgeId(); 53 | String forgeURL = reference.getForgeUrl() + minecraftVersion + "-" + modLoader.getForgeId(); 54 | if (alternateDownloadUrl) { 55 | forgeFileName += "-" + minecraftVersion; 56 | forgeURL += "-" + minecraftVersion; 57 | } 58 | 59 | forgeFileName += downloadInstaller ? reference.getForgeInstaller() : reference.getForgeUniversal(); 60 | forgeURL += "/" + forgeFileName; 61 | 62 | modLoader.setDownloadUrl(forgeURL); 63 | modLoader.setFileName(forgeFileName); 64 | if (DownloadStatus.FAILURE.equals(downloadHelper.downloadFile(modLoader)) && alternateDownloadUrl) { 65 | log.warn("Attempting alternate Forge download URL"); 66 | downloadForgeFile(minecraftVersion, modLoader, downloadInstaller, false); 67 | } 68 | } 69 | 70 | public List updateForge(String minecraftVersion, List modLoaders) { 71 | if (!arguments.isUpdateForge()) { 72 | log.trace("Updating Forge disabled"); 73 | return modLoaders; 74 | } 75 | 76 | for (ModLoader modLoader : modLoaders) { 77 | JSONObject fileListJson; 78 | if (modLoader.getRelease() == null) { 79 | log.warn("No Forge release type set for update, defaulting to recommended"); 80 | modLoader.setRelease("recommended"); 81 | } 82 | try { 83 | fileListJson = (JSONObject) (URLHelper.getJsonFromUrl(reference.getForgeUpdateUrl())).get("promos"); 84 | String updatedForgeVersion = (String) fileListJson.get(minecraftVersion + "-" + modLoader.getRelease()); 85 | String manifestForgeVersion = modLoader.getId().substring(modLoader.getId().indexOf('-') + 1); 86 | 87 | if (VersionHelper.compareVersions(manifestForgeVersion, updatedForgeVersion) < 0) { 88 | log.debug("Newer version of Forge found, updating to {}", updatedForgeVersion); 89 | modLoader.setId("forge-" + updatedForgeVersion); 90 | } 91 | 92 | } catch (IOException | ParseException e) { 93 | log.error("Failed to update Forge", e); 94 | } 95 | } 96 | 97 | return modLoaders; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/handler/ModHandler.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.handler; 2 | 3 | import com.nincraft.modpackdownloader.container.Mod; 4 | 5 | public interface ModHandler { 6 | void downloadMod(final Mod mod); 7 | 8 | void updateMod(final Mod mod); 9 | } 10 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/handler/ThirdPartyModHandler.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.handler; 2 | 3 | import com.nincraft.modpackdownloader.container.Mod; 4 | import com.nincraft.modpackdownloader.util.DownloadHelper; 5 | import lombok.extern.log4j.Log4j2; 6 | 7 | @Log4j2 8 | public class ThirdPartyModHandler implements ModHandler { 9 | 10 | private DownloadHelper downloadHelper; 11 | 12 | public ThirdPartyModHandler(DownloadHelper downloadHelper) { 13 | this.downloadHelper = downloadHelper; 14 | } 15 | 16 | @Override 17 | public void downloadMod(final Mod mod) { 18 | downloadHelper.downloadFile(mod); 19 | } 20 | 21 | @Override 22 | public void updateMod(final Mod mod) { 23 | log.debug("Updating Third Party Mods is not supported."); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/processor/AbstractProcessor.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.processor; 2 | 3 | import com.google.gson.Gson; 4 | import com.nincraft.modpackdownloader.container.CurseFile; 5 | import com.nincraft.modpackdownloader.container.Manifest; 6 | import com.nincraft.modpackdownloader.container.Mod; 7 | import com.nincraft.modpackdownloader.container.ThirdParty; 8 | import com.nincraft.modpackdownloader.handler.CurseFileHandler; 9 | import com.nincraft.modpackdownloader.handler.ModHandler; 10 | import com.nincraft.modpackdownloader.handler.ThirdPartyModHandler; 11 | import com.nincraft.modpackdownloader.util.Arguments; 12 | import com.nincraft.modpackdownloader.util.DownloadHelper; 13 | import lombok.Getter; 14 | import lombok.Setter; 15 | import lombok.extern.log4j.Log4j2; 16 | import lombok.val; 17 | import org.apache.commons.lang3.StringUtils; 18 | import org.json.simple.JSONObject; 19 | import org.json.simple.parser.JSONParser; 20 | import org.json.simple.parser.ParseException; 21 | 22 | import java.io.File; 23 | import java.io.FileReader; 24 | import java.io.IOException; 25 | import java.util.*; 26 | import java.util.Map.Entry; 27 | import java.util.concurrent.ExecutorService; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | @Log4j2 31 | public abstract class AbstractProcessor { 32 | 33 | final Comparator modComparator = Comparator.comparing(mod -> mod.getName().toLowerCase()); 34 | final Map, ModHandler> modHandlerHashMap = new HashMap<>(); 35 | @Getter 36 | @Setter 37 | private ExecutorService executorService; 38 | private Gson gson = new Gson(); 39 | Map manifestMap = new HashMap<>(); 40 | Arguments arguments; 41 | DownloadHelper downloadHelper; 42 | 43 | AbstractProcessor(Arguments arguments, DownloadHelper downloadHelper) { 44 | modHandlerHashMap.put(CurseFile.class, new CurseFileHandler(arguments, downloadHelper)); 45 | modHandlerHashMap.put(ThirdParty.class, new ThirdPartyModHandler(downloadHelper)); 46 | this.arguments = arguments; 47 | this.downloadHelper = downloadHelper; 48 | buildManifestList(arguments.getManifests()); 49 | } 50 | 51 | private void buildManifestList(final List manifestFiles) { 52 | for (val manifestFile : manifestFiles) { 53 | manifestMap.put(manifestFile, buildManifest(manifestFile)); 54 | } 55 | } 56 | 57 | private Manifest buildManifest(final File manifestFile) { 58 | JSONObject jsonLists; 59 | try { 60 | jsonLists = (JSONObject) new JSONParser().parse(new FileReader(manifestFile)); 61 | } catch (IOException | ParseException e) { 62 | log.error(e); 63 | return null; 64 | } 65 | 66 | return gson.fromJson(jsonLists.toString(), Manifest.class); 67 | } 68 | 69 | public void process() throws InterruptedException { 70 | init(manifestMap); 71 | 72 | for (val manifestEntry : manifestMap.entrySet()) { 73 | preprocess(manifestEntry); 74 | 75 | process(manifestEntry); 76 | waitFinishProcessingMods(); 77 | 78 | postProcess(manifestEntry); 79 | } 80 | } 81 | 82 | protected abstract void init(Map manifestMap); 83 | 84 | boolean preprocess(Entry manifest) { 85 | return true; 86 | } 87 | 88 | boolean process(Entry manifest) throws InterruptedException { 89 | return true; 90 | } 91 | 92 | boolean postProcess(Entry manifest) { 93 | return true; 94 | } 95 | 96 | List buildModList(final File file, final Manifest manifest) { 97 | log.trace("Building Mod List..."); 98 | 99 | val modList = new ArrayList(); 100 | if (manifest.getMinecraftVersion() != null && StringUtils.isBlank(arguments.getCheckMCUpdate())) { 101 | arguments.setMcVersion(manifest.getMinecraftVersion()); 102 | } 103 | 104 | modList.addAll(manifest.getCurseFiles()); 105 | modList.addAll(manifest.getThirdParty()); 106 | 107 | val modSet = new HashSet(modList); 108 | modList.clear(); 109 | modList.addAll(modSet); 110 | 111 | modList.forEach(Mod::init); 112 | 113 | modList.sort(modComparator); 114 | 115 | log.trace("Finished Building Mod List."); 116 | return modList; 117 | } 118 | 119 | private void waitFinishProcessingMods() throws InterruptedException { 120 | getExecutorService().awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/processor/DownloadModsProcessor.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.processor; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.nincraft.modpackdownloader.container.Manifest; 5 | import com.nincraft.modpackdownloader.container.Mod; 6 | import com.nincraft.modpackdownloader.handler.ForgeHandler; 7 | import com.nincraft.modpackdownloader.util.Arguments; 8 | import com.nincraft.modpackdownloader.util.DownloadHelper; 9 | import com.nincraft.modpackdownloader.util.Reference; 10 | import lombok.extern.log4j.Log4j2; 11 | import lombok.val; 12 | import org.apache.commons.io.FileUtils; 13 | import org.apache.commons.lang3.StringUtils; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Map.Entry; 20 | import java.util.concurrent.Executors; 21 | 22 | @Log4j2 23 | public class DownloadModsProcessor extends AbstractProcessor { 24 | private final List modList = Lists.newArrayList(); 25 | private Reference reference = Reference.getInstance(); 26 | 27 | public DownloadModsProcessor(Arguments arguments, DownloadHelper downloadHelper) { 28 | super(arguments, downloadHelper); 29 | } 30 | 31 | private void downloadMods(final Manifest manifest) { 32 | setExecutorService(Executors.newFixedThreadPool(arguments.getMaxDownloadThreads() > 0 ? arguments.getMaxDownloadThreads() : modList.size() + 1)); 33 | 34 | val minecraft = manifest.getMinecraft(); 35 | if (minecraft != null) { 36 | ForgeHandler forgeHandler = new ForgeHandler(arguments, downloadHelper); 37 | Runnable forgeThread = new Thread(() -> forgeHandler.downloadForge(manifest.getMinecraftVersion(), minecraft.getModLoaders())); 38 | 39 | getExecutorService().execute(forgeThread); 40 | } 41 | 42 | log.trace("Downloading {} mods...", modList.size()); 43 | int downloadCount = 1; 44 | for (val mod : modList) { 45 | log.debug(reference.getDownloadingModXOfY(), mod.getName(), downloadCount++, 46 | Reference.downloadTotal); 47 | 48 | Runnable modDownload = new Thread(() -> { 49 | modHandlerHashMap.get(mod.getClass()).downloadMod(mod); 50 | Reference.downloadCount++; 51 | log.trace("Finished downloading {}", mod.getName()); 52 | }); 53 | getExecutorService().execute(modDownload); 54 | } 55 | getExecutorService().shutdown(); 56 | log.trace("Finished downloading {} mods.", modList.size()); 57 | } 58 | 59 | @Override 60 | protected void init(final Map manifestMap) { 61 | for (val manifestEntry : manifestMap.entrySet()) { 62 | modList.addAll(buildModList(manifestEntry.getKey(), manifestEntry.getValue())); 63 | } 64 | 65 | Reference.downloadTotal = modList.size(); 66 | log.debug("A total of {} mods will be downloaded.", Reference.downloadTotal); 67 | } 68 | 69 | @Override 70 | protected boolean process(final Entry manifestEntry) { 71 | downloadMods(manifestEntry.getValue()); 72 | return true; 73 | } 74 | 75 | @Override 76 | protected boolean postProcess(final Entry manifestEntry) { 77 | moveOverrides(manifestEntry.getValue()); 78 | downloadHelper.getDownloadSummarizer().summarize(); 79 | return true; 80 | } 81 | 82 | private void moveOverrides(Manifest manifest) { 83 | if (!StringUtils.isBlank(manifest.getOverrides())) { 84 | try { 85 | File overridesDirectory = new File(manifest.getOverrides()); 86 | if (overridesDirectory.exists()) { 87 | FileUtils.copyDirectory(overridesDirectory, new File(".")); 88 | FileUtils.deleteDirectory(overridesDirectory); 89 | log.debug("Successfully moved overrides: {}", manifest.getOverrides()); 90 | } 91 | } catch (IOException e) { 92 | log.error("Unable to move {} folder", manifest.getOverrides(), e); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/processor/MergeManifestsProcessor.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.processor; 2 | 3 | import com.google.common.collect.Sets; 4 | import com.nincraft.modpackdownloader.container.CurseFile; 5 | import com.nincraft.modpackdownloader.container.Manifest; 6 | import com.nincraft.modpackdownloader.util.Arguments; 7 | import com.nincraft.modpackdownloader.util.DownloadHelper; 8 | import com.nincraft.modpackdownloader.util.FileSystemHelper; 9 | import com.nincraft.modpackdownloader.util.ManifestHelper; 10 | import lombok.extern.log4j.Log4j2; 11 | import lombok.val; 12 | 13 | import java.io.File; 14 | import java.util.Map; 15 | import java.util.Map.Entry; 16 | import java.util.Set; 17 | 18 | @Log4j2 19 | public class MergeManifestsProcessor extends AbstractProcessor { 20 | private final Set curseModSet; 21 | private final Manifest newManifest; 22 | 23 | public MergeManifestsProcessor(Arguments arguments, DownloadHelper downloadHelper) { 24 | super(arguments, downloadHelper); 25 | 26 | curseModSet = Sets.newHashSet(); 27 | newManifest = new Manifest(); 28 | } 29 | 30 | @Override 31 | protected void init(Map manifestMap) { 32 | // no-op 33 | } 34 | 35 | @Override 36 | public void process() throws InterruptedException { 37 | for(val entry : manifestMap.entrySet()) { 38 | process(entry); 39 | } 40 | 41 | newManifest.getCurseFiles().addAll(curseModSet); 42 | newManifest.getCurseFiles().sort(modComparator); 43 | 44 | newManifest.setOverrides("overrides"); 45 | 46 | ManifestHelper.cleanupManifest(newManifest); 47 | FileSystemHelper.writeManifest(newManifest, "manifest.json"); 48 | } 49 | 50 | @Override 51 | protected boolean process(Entry manifestEntry) { 52 | val manifest = manifestEntry.getValue(); 53 | 54 | processManifestHeaders(manifest); 55 | 56 | if (newManifest.getMinecraft() == null && manifest.getMinecraft() != null) { 57 | newManifest.setMinecraft(manifest.getMinecraft()); 58 | } 59 | 60 | curseModSet.addAll(manifest.getCurseFiles()); 61 | 62 | return true; 63 | } 64 | 65 | private void processManifestHeaders(Manifest manifest) { 66 | 67 | if (newManifest.getAuthor() == null) { 68 | newManifest.setAuthor(manifest.getAuthor()); 69 | } 70 | 71 | if (newManifest.getManifestType() == null) { 72 | newManifest.setManifestType(manifest.getManifestType()); 73 | } 74 | 75 | if (newManifest.getMinecraftVersion() == null) { 76 | newManifest.setManifestVersion(manifest.getManifestVersion()); 77 | } 78 | 79 | if (newManifest.getName() == null) { 80 | newManifest.setName(manifest.getName()); 81 | } 82 | 83 | if (newManifest.getVersion() == null) { 84 | newManifest.setVersion(manifest.getVersion()); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/processor/UpdateModsProcessor.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.processor; 2 | 3 | import com.nincraft.modpackdownloader.container.CurseFile; 4 | import com.nincraft.modpackdownloader.container.Manifest; 5 | import com.nincraft.modpackdownloader.container.Mod; 6 | import com.nincraft.modpackdownloader.handler.ForgeHandler; 7 | import com.nincraft.modpackdownloader.summary.UpdateCheckSummarizer; 8 | import com.nincraft.modpackdownloader.util.*; 9 | import lombok.Getter; 10 | import lombok.extern.log4j.Log4j2; 11 | import lombok.val; 12 | import org.apache.commons.collections4.CollectionUtils; 13 | import org.apache.commons.io.FileUtils; 14 | import org.apache.commons.lang3.StringUtils; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.Map.Entry; 21 | import java.util.concurrent.Executors; 22 | 23 | @Log4j2 24 | public class UpdateModsProcessor extends AbstractProcessor { 25 | 26 | private Reference reference = Reference.getInstance(); 27 | private UpdateCheckSummarizer updateCheckSummarizer = UpdateCheckSummarizer.getInstance(); 28 | @Getter 29 | private boolean checkUpdate; 30 | 31 | public UpdateModsProcessor(Arguments arguments, DownloadHelper downloadHelper) { 32 | super(arguments, downloadHelper); 33 | checkUpdate = !StringUtils.isBlank(arguments.getCheckMCUpdate()); 34 | } 35 | 36 | private void backupCurseManifest(final File manifestFile) { 37 | try { 38 | FileUtils.copyFile(manifestFile, new File(manifestFile.getAbsolutePath() + ".bak"), true); 39 | } catch (IOException e) { 40 | log.error("Could not backup Curse manifest file", e); 41 | } 42 | } 43 | 44 | private void updateMods(final Manifest manifest, final List modList) { 45 | if (!manifest.getBatchAddCurse().isEmpty()) { 46 | log.debug("Found batch add for Curse"); 47 | addBatch(manifest, modList); 48 | } 49 | 50 | Reference.updateTotal = modList.size(); 51 | 52 | setExecutorService(Executors.newFixedThreadPool(Reference.updateTotal + 1)); 53 | 54 | val minecraft = manifest.getMinecraft(); 55 | if (minecraft != null) { 56 | ForgeHandler forgeHandler = new ForgeHandler(arguments, downloadHelper); 57 | Runnable forgeThread = new Thread(() -> minecraft.setModLoaders( 58 | forgeHandler.updateForge(manifest.getMinecraftVersion(), minecraft.getModLoaders()))); 59 | 60 | getExecutorService().execute(forgeThread); 61 | } 62 | 63 | log.trace("Updating {} mods...", Reference.updateTotal); 64 | 65 | int updateCount = 1; 66 | for (val mod : modList) { 67 | log.info(reference.getUpdatingModXOfY(), mod.getName(), updateCount++, Reference.updateTotal); 68 | Runnable modUpdate = new Thread(() -> { 69 | modHandlerHashMap.get(mod.getClass()).updateMod(mod); 70 | Reference.updateCount++; 71 | log.trace("Finished updating {}", mod.getName()); 72 | }); 73 | getExecutorService().execute(modUpdate); 74 | } 75 | getExecutorService().shutdown(); 76 | log.trace("Finished updating {} mods.", Reference.updateTotal); 77 | } 78 | 79 | private void addBatch(final Manifest manifestFile, final List modList) { 80 | CurseFile curseFile; 81 | String projectIdPattern = "(\\d)+"; 82 | String projectNamePattern = "(((?:[a-z][a-z]+))(-)?)+"; 83 | for (String projectUrl : manifestFile.getBatchAddCurse()) { 84 | String projectId = URLHelper.parseCurseUrl(projectIdPattern, projectUrl); 85 | String projectName = URLHelper.parseCurseUrl(projectNamePattern, projectUrl); 86 | 87 | if (projectId != null && projectName != null) { 88 | curseFile = new CurseFile(projectId, projectName); 89 | curseFile.init(); 90 | log.debug("Adding {} from batch add", curseFile.getName()); 91 | modList.add(curseFile); 92 | manifestFile.getCurseFiles().add(curseFile); 93 | } else { 94 | log.warn("Unable to add {} from batch add", projectUrl); 95 | } 96 | } 97 | } 98 | 99 | private void updateManifest(final File file, final Manifest manifest) { 100 | log.trace("Updating Manifest File..."); 101 | // Sort Mod Lists 102 | manifest.getCurseFiles().sort(modComparator); 103 | manifest.getThirdParty().sort(modComparator); 104 | 105 | ManifestHelper.cleanupModLists(manifest); 106 | FileSystemHelper.writeManifest(manifest, file); 107 | } 108 | 109 | 110 | 111 | @Override 112 | protected void init(final Map manifestMap) { 113 | // no-op 114 | } 115 | 116 | @Override 117 | protected boolean preprocess(final Entry manifestEntry) { 118 | if (!isCheckUpdate()) { 119 | backupManifest(manifestEntry.getKey(), manifestEntry.getValue()); 120 | } else { 121 | arguments.setMcVersion(arguments.getCheckMCUpdate()); 122 | } 123 | return true; 124 | } 125 | 126 | @Override 127 | protected boolean process(final Entry manifestEntry) { 128 | updateMods(manifestEntry.getValue(), buildModList(manifestEntry.getKey(), manifestEntry.getValue())); 129 | return true; 130 | } 131 | 132 | @Override 133 | protected boolean postProcess(final Entry manifestEntry) { 134 | updateCheckSummarizer.summarize(); 135 | if (arguments.isUpdateMods()) { 136 | updateManifest(manifestEntry.getKey(), manifestEntry.getValue()); 137 | } 138 | return true; 139 | } 140 | 141 | private void backupManifest(final File manifestFile, final Manifest manifest) { 142 | if (CollectionUtils.isNotEmpty(manifest.getCurseFiles())) { 143 | backupCurseManifest(manifestFile); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/status/DownloadStatus.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.status; 2 | 3 | public enum DownloadStatus { 4 | FAILURE(""), SUCCESS_DOWNLOAD("downloaded"), SUCCESS_CACHE("moved from cache"), SKIPPED(""); 5 | String message; 6 | 7 | DownloadStatus(String message) { 8 | this.message = message; 9 | } 10 | 11 | @Override 12 | public String toString() { 13 | return message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/summary/DownloadSummarizer.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.summary; 2 | 3 | import com.nincraft.modpackdownloader.status.DownloadStatus; 4 | import lombok.extern.log4j.Log4j2; 5 | 6 | import java.util.Observable; 7 | import java.util.Observer; 8 | 9 | @Log4j2 10 | public class DownloadSummarizer implements Observer { 11 | 12 | private int successTotal = 0; 13 | private int failureTotal = 0; 14 | private int skipTotal = 0; 15 | 16 | @Override 17 | public void update(Observable o, Object arg) { 18 | if (arg instanceof DownloadStatus) { 19 | DownloadStatus downloadStatus = (DownloadStatus) arg; 20 | switch (downloadStatus) { 21 | case FAILURE: 22 | failureTotal++; 23 | break; 24 | case SUCCESS_CACHE: 25 | case SUCCESS_DOWNLOAD: 26 | successTotal++; 27 | break; 28 | case SKIPPED: 29 | skipTotal++; 30 | break; 31 | default: 32 | break; 33 | } 34 | } 35 | } 36 | 37 | public void summarize() { 38 | if (successTotal != 0) { 39 | log.info("Successfully downloaded {} {}", successTotal, getEnding(successTotal)); 40 | } 41 | if (failureTotal != 0) { 42 | log.info("Failed to download {} {}", failureTotal, getEnding(failureTotal)); 43 | } 44 | if (skipTotal != 0) { 45 | log.info("Skipped downloading {} {}", skipTotal, getEnding(skipTotal)); 46 | } 47 | } 48 | 49 | private String getEnding(int total) { 50 | return total == 1 ? "mod" : "mods"; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/summary/UpdateCheckSummarizer.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.summary; 2 | 3 | import com.nincraft.modpackdownloader.container.Mod; 4 | import lombok.Getter; 5 | import lombok.extern.log4j.Log4j2; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @Log4j2 11 | public class UpdateCheckSummarizer { 12 | 13 | @Getter 14 | private static UpdateCheckSummarizer instance = new UpdateCheckSummarizer(); 15 | 16 | @Getter 17 | private List modList = new ArrayList<>(); 18 | 19 | public void summarize() { 20 | log.info("Number of updates found: {}", modList.size()); 21 | if (!modList.isEmpty()) { 22 | log.info("Updatable mod list: "); 23 | for (Mod mod : modList) { 24 | log.info(mod.getName()); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/util/Arguments.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.util; 2 | 3 | import com.beust.jcommander.Parameter; 4 | import com.beust.jcommander.converters.FileConverter; 5 | import com.nincraft.modpackdownloader.validation.ReleaseType; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.File; 10 | import java.util.List; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class Arguments { 15 | @Parameter(names = {"-modFolder", "-folder", "-mods"}, description = "Folder where mods will be downloaded") 16 | private String modFolder; 17 | @Parameter(names = {"-mcVersion"}, description = "Minecraft version") 18 | private String mcVersion; 19 | @Parameter(names = {"-backupVersion", "-backupVersions"}, description = "Backup Minecraft version, during updates" + 20 | "when no version is found for the main Minecraft version, this version will be checked as well") 21 | private List backupVersions; 22 | @Parameter(names = {"-releaseType"}, description = "Release type for Curse updates. Acceptable parameters" + 23 | "are release, beta, and alpha", validateWith = ReleaseType.class) 24 | private String releaseType; 25 | @Parameter(names = {"-maxDownloadThreads"}, description = "Max number of threads for downloading mods. The default is however many mods are in your manifest") 26 | private int maxDownloadThreads; 27 | @Parameter(names = {"-forceDownload"}, description = "Forces downloading instead of pulling from the cache") 28 | private boolean forceDownload = false; 29 | @Parameter(names = {"-generateUrlTxt"}, description = "Generates URL txt files for SKCraft Launcher." + 30 | "Currently not implemented") 31 | private boolean generateUrlTxt; 32 | @Parameter(names = {"-updateForge"}, description = "Updates manifest to the latest version of Forge for the" + 33 | "specified Minecraft version") 34 | private boolean updateForge; 35 | @Parameter(names = {"-updateCurseModPack"}, description = "Updates the Curse modpack instance, takes in a Curse modpack slug") 36 | private String updateCurseModPack; 37 | @Parameter(names = {"-curseFileId"}, description = "File id of the Curse modpack you want to download") 38 | private String curseFileId; 39 | @Parameter(names = {"-updateApp"}, description = "Downloads latest version of ModPack Downloader") 40 | private boolean updateApp; 41 | @Parameter(names = {"-clearCache"}, description = "Clears ModPack Downloader's cache") 42 | private boolean clearCache; 43 | @Parameter(names = {"-manifest", "-manifests"}, description = "List of manifests to use for downloading/updating", 44 | listConverter = FileConverter.class) 45 | private List manifests; 46 | @Parameter(names = {"-downloadMods"}, description = "Downloads mods in the given manifests." + 47 | "Enabled by default if update and merge are not") 48 | private boolean downloadMods; 49 | @Parameter(names = {"-updateMods"}, description = "Updates mods in the given manifests") 50 | private boolean updateMods; 51 | @Parameter(names = {"-checkMCUpdate"}, description = "Checks mods for updates given the Minecraft version (passed to this parameter) and manifests") 52 | private String checkMCUpdate; 53 | @Parameter(names = {"-mergeManifests"}, description = "Merges the given manifests into one manifest") 54 | private boolean mergeManifests; 55 | @Parameter(names = {"-help"}, description = "Displays this great message", help = true) 56 | private boolean helpEnabled; 57 | } 58 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/util/DownloadHelper.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.util; 2 | 3 | import com.nincraft.modpackdownloader.container.DownloadableFile; 4 | import com.nincraft.modpackdownloader.status.DownloadStatus; 5 | import com.nincraft.modpackdownloader.summary.DownloadSummarizer; 6 | import lombok.Getter; 7 | import lombok.extern.log4j.Log4j2; 8 | import lombok.val; 9 | import org.apache.commons.io.FileUtils; 10 | import org.apache.commons.lang3.BooleanUtils; 11 | 12 | import java.io.IOException; 13 | import java.net.URL; 14 | import java.util.Observable; 15 | 16 | @Log4j2 17 | public class DownloadHelper extends Observable { 18 | 19 | @Getter 20 | private DownloadSummarizer downloadSummarizer = new DownloadSummarizer(); 21 | private Arguments arguments; 22 | 23 | public DownloadHelper(Arguments arguments) { 24 | this.addObserver(downloadSummarizer); 25 | this.arguments = arguments; 26 | } 27 | 28 | /** 29 | * Downloads a {@link DownloadableFile} moves it to the correct folder. Downloads to the local cache and then 30 | * copies to the download folder. 31 | * 32 | * @param downloadableFile a DownloadableFile with a download URL 33 | * @return status of the download, failed, skipped, or success 34 | */ 35 | public DownloadStatus downloadFile(final DownloadableFile downloadableFile) { 36 | return downloadFile(downloadableFile, true); 37 | } 38 | 39 | /** 40 | * Downloads a {@link DownloadableFile} moves it to the correct folder. Downloads to the local cache if 41 | * downloadToLocalRepo is set to true and then copies to the download folder. 42 | * 43 | * @param downloadableFile a DownloadableFile with a download URL 44 | * @param downloadToLocalRepo set to true to keep a copy of the DownloadableFile in local cache 45 | * @return status of the download, failed, skipped, or success 46 | */ 47 | public DownloadStatus downloadFile(final DownloadableFile downloadableFile, boolean downloadToLocalRepo) { 48 | DownloadStatus status = DownloadStatus.FAILURE; 49 | if (BooleanUtils.isTrue(downloadableFile.getSkipDownload())) { 50 | log.debug("Skipped downloading {}", downloadableFile.getName()); 51 | return notifyStatus(DownloadStatus.SKIPPED); 52 | } 53 | String fileName = downloadableFile.getFileName(); 54 | 55 | if (FileSystemHelper.getDownloadedFile(fileName, downloadableFile.getFolder()).exists() && !arguments.isForceDownload()) { 56 | log.debug("Found {} already downloaded, skipping", fileName); 57 | return notifyStatus(DownloadStatus.SKIPPED); 58 | } 59 | 60 | if (!FileSystemHelper.isInLocalRepo(downloadableFile.getName(), fileName) || arguments.isForceDownload()) { 61 | val downloadedFile = FileSystemHelper.getLocalFile(downloadableFile); 62 | try { 63 | FileUtils.copyURLToFile(new URL(downloadableFile.getDownloadUrl()), downloadedFile); 64 | } catch (final IOException e) { 65 | log.error("Could not download {}.", fileName, e); 66 | Reference.downloadCount++; 67 | if ("forge".equals(downloadableFile.getName())) { 68 | return status; 69 | } 70 | return notifyStatus(status); 71 | } 72 | status = DownloadStatus.SUCCESS_DOWNLOAD; 73 | } else { 74 | status = DownloadStatus.SUCCESS_CACHE; 75 | } 76 | FileSystemHelper.moveFromLocalRepo(downloadableFile, fileName, downloadToLocalRepo, arguments.getModFolder()); 77 | log.info("Successfully {} {}", status, fileName); 78 | return notifyStatus(status); 79 | } 80 | 81 | private DownloadStatus notifyStatus(DownloadStatus status) { 82 | setChanged(); 83 | notifyObservers(status); 84 | return status; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/util/FileSystemHelper.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.util; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.gson.GsonBuilder; 5 | import com.nincraft.modpackdownloader.container.DownloadableFile; 6 | import com.nincraft.modpackdownloader.container.Manifest; 7 | import lombok.experimental.UtilityClass; 8 | import lombok.extern.log4j.Log4j2; 9 | import lombok.val; 10 | import org.apache.commons.io.FileUtils; 11 | 12 | import java.io.File; 13 | import java.io.FileWriter; 14 | import java.io.IOException; 15 | 16 | @Log4j2 17 | @UtilityClass 18 | public final class FileSystemHelper { 19 | 20 | private Reference reference = Reference.getInstance(); 21 | 22 | public static void createFolder(final String folder) { 23 | if (folder != null) { 24 | final File dir = new File(folder); 25 | if (!dir.exists()) { 26 | dir.mkdirs(); 27 | } 28 | } 29 | } 30 | 31 | static void moveFromLocalRepo(final DownloadableFile downloadableFile, final String fileName, boolean downloadToLocalRepo, String modFolder) { 32 | val newProjectName = getProjectNameOrDefault(downloadableFile.getName()); 33 | String folder = downloadableFile.getFolder(); 34 | if (Strings.isNullOrEmpty(folder)) { 35 | folder = modFolder; 36 | } 37 | try { 38 | File downloadedFile = getDownloadedFile(fileName, folder); 39 | if (downloadToLocalRepo) { 40 | FileUtils.copyFileToDirectory(getLocalFile(fileName, newProjectName), new File(folder)); 41 | } else if (!downloadedFile.exists()) { 42 | FileUtils.moveFileToDirectory(getLocalFile(fileName, newProjectName), new File(folder), true); 43 | } 44 | if (!Strings.isNullOrEmpty(downloadableFile.getRename())) { 45 | downloadedFile.renameTo(new File(downloadedFile.getParent() + File.separator + downloadableFile.getRename())); 46 | } 47 | } catch (final IOException e) { 48 | log.error("Could not copy {} from local repo.", newProjectName, e); 49 | } 50 | } 51 | 52 | static boolean isInLocalRepo(final String projectName, final String fileName) { 53 | return getLocalFile(fileName, getProjectNameOrDefault(projectName)).exists(); 54 | } 55 | 56 | public static File getDownloadedFile(final String fileName, String modFolder) { 57 | if (modFolder != null) { 58 | createFolder(modFolder); 59 | return new File(modFolder + File.separator + fileName); 60 | } else { 61 | return new File(fileName); 62 | } 63 | } 64 | 65 | private static String getProjectNameOrDefault(final String projectName) { 66 | return projectName != null ? projectName : "thirdParty"; 67 | } 68 | 69 | static File getLocalFile(DownloadableFile downloadableFile) { 70 | return getLocalFile(downloadableFile.getFileName(), downloadableFile.getName()); 71 | } 72 | 73 | private static File getLocalFile(final String fileName, final String newProjectName) { 74 | return new File(reference.getUserhome() + newProjectName + File.separator + fileName); 75 | } 76 | 77 | public static void clearCache() { 78 | File cache = new File(reference.getUserhome()); 79 | log.info("Clearing cache at {}", reference.getUserhome()); 80 | try { 81 | FileUtils.deleteDirectory(cache); 82 | } catch (IOException e) { 83 | log.error("Unable to clear cache", e); 84 | } 85 | } 86 | 87 | public static void writeManifest(final Manifest manifest, final String filename) { 88 | writeManifest(manifest, new File(filename)); 89 | } 90 | 91 | public static void writeManifest(final Manifest manifest, final File file) { 92 | val prettyGson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation() 93 | .disableHtmlEscaping().create(); 94 | try (val fileWriter = new FileWriter(file)) { 95 | fileWriter.write(prettyGson.toJson(manifest)); 96 | } catch (final IOException e) { 97 | log.error(e); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/util/ManifestHelper.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.util; 2 | 3 | import com.nincraft.modpackdownloader.container.CurseFile; 4 | import com.nincraft.modpackdownloader.container.Manifest; 5 | import com.nincraft.modpackdownloader.container.Minecraft; 6 | import com.nincraft.modpackdownloader.container.ModLoader; 7 | import lombok.val; 8 | 9 | import java.util.List; 10 | 11 | public class ManifestHelper { 12 | 13 | /** 14 | * Cleanup Mod Lists after an update 15 | * @param manifest 16 | */ 17 | public static void cleanupModLists(Manifest manifest) { 18 | 19 | val minecraft = manifest.getMinecraft(); 20 | if (minecraft != null) { 21 | val modLoaders = minecraft.getModLoaders(); 22 | if (modLoaders != null && modLoaders.isEmpty()) { 23 | minecraft.setModLoaders(null); 24 | } 25 | } 26 | 27 | if (manifest.getCurseFiles().isEmpty()) { 28 | manifest.setCurseFiles(null); 29 | } 30 | if (manifest.getThirdParty().isEmpty()) { 31 | manifest.setThirdParty(null); 32 | } 33 | 34 | // Always Clean up Batch Add 35 | manifest.setBatchAddCurse(null); 36 | } 37 | 38 | /** 39 | * Clean up Merged Manifest for Curseforge deployment 40 | * 41 | * @param manifest 42 | */ 43 | public static void cleanupManifest(Manifest manifest) { 44 | val minecraft = manifest.getMinecraft(); 45 | 46 | if (minecraft != null) { 47 | cleanupMinecraft(minecraft); 48 | } 49 | 50 | val curseFiles = manifest.getCurseFiles(); 51 | if (curseFiles != null) { 52 | cleanupCurseFiles(curseFiles); 53 | } 54 | 55 | manifest.setThirdParty(null); 56 | manifest.setBatchAddCurse(null); 57 | } 58 | 59 | private static void cleanupMinecraft(Minecraft minecraft) { 60 | val modLoaders = minecraft.getModLoaders(); 61 | 62 | if (modLoaders != null) { 63 | if (modLoaders.isEmpty()) { 64 | minecraft.setModLoaders(null); 65 | } else { 66 | cleanupModLoaders(modLoaders); 67 | } 68 | } 69 | } 70 | 71 | private static void cleanupModLoaders(List modLoaders) { 72 | for (val modLoader : modLoaders) { 73 | cleanupModLoader(modLoader); 74 | } 75 | } 76 | 77 | private static void cleanupModLoader(ModLoader modLoader) { 78 | modLoader.setName(null); 79 | modLoader.setFolder(null); 80 | modLoader.setRelease(null); 81 | modLoader.setDownloadInstaller(null); 82 | modLoader.setDownloadUniversal(null); 83 | modLoader.setRenameInstaller(null); 84 | modLoader.setRenameUniversal(null); 85 | } 86 | 87 | private static void cleanupCurseFiles(List curseFiles) { 88 | for (val curseFile : curseFiles) { 89 | cleanupCurseFile(curseFile); 90 | } 91 | } 92 | 93 | private static void cleanupCurseFile(CurseFile curseFile) { 94 | curseFile.setName(null); 95 | curseFile.setSkipDownload(null); 96 | curseFile.setSkipUpdate(null); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/util/Reference.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.util; 2 | 3 | import lombok.Data; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | import static lombok.AccessLevel.PRIVATE; 8 | 9 | @Data 10 | @NoArgsConstructor(access = PRIVATE) 11 | public class Reference { 12 | @Getter 13 | private static final Reference instance = new Reference(); 14 | public static int downloadCount = 0; 15 | public static int downloadTotal = 0; 16 | public static int updateCount = 0; 17 | public static int updateTotal = 0; 18 | private String curseforgeBaseUrl = "https://www.curseforge.com/projects/"; 19 | private String curseForgeBaseDownloadUrl = "https://www.curseforge.com/minecraft/mc-mods/"; 20 | private String ftbBaseUrl = "https://www.feed-the-beast.com/projects/"; 21 | private String curseforgeWidgetJsonMod = "mc-mods"; 22 | private String curseforgeWidgetJsonModpack = "modpacks"; 23 | private String curseforgeWidgetJsonUrl = "https://api.cfwidget.com/%s/minecraft/%s.json"; 24 | private String cookieTest1 = "?cookieTest=1"; 25 | private String downloadingModXOfY = "Downloading {}. Mod {} of {}."; 26 | private String updatingModXOfY = "Updating {}. Mod {} of {}."; 27 | private String windowsFolder = "\\.modpackdownloader\\"; 28 | private String macFolder = "/Library/Application Support/modpackdownloader/"; 29 | private String otherFolder = "/.modpackdownloader/"; 30 | private String jarFileExt = ".jar"; 31 | private String zipFileExt = ".zip"; 32 | private int retryCounter = 5; 33 | private char urlDelimiter = '/'; 34 | private String defaultManifestFile = "manifest.json"; 35 | private String userhome; 36 | private String updateAppUrl = "http://play.nincraft.com:8080/job/Mod%20Pack%20Downloader/lastSuccessfulBuild/artifact/modpackdownloader-core/target/classes/latest.json"; 37 | private String forgeUrl = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/"; 38 | private String forgeInstaller = "-installer.jar"; 39 | private String forgeUniversal = "-universal.jar"; 40 | private String forgeUpdateUrl = "http://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json"; 41 | private String javaContentType = "application/java-archive"; 42 | } 43 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/util/URLHelper.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.json.simple.JSONObject; 6 | import org.json.simple.parser.JSONParser; 7 | import org.json.simple.parser.ParseException; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.net.URL; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | @UtilityClass 17 | @Log4j2 18 | public final class URLHelper { 19 | public static JSONObject getJsonFromUrl(final String url) throws ParseException, IOException { 20 | JSONParser parser = new JSONParser(); 21 | return (JSONObject) parser.parse(new BufferedReader(new InputStreamReader(new URL(url).openStream()))); 22 | } 23 | 24 | public static String parseCurseUrl(String projectIdPattern, String projectUrl) { 25 | String projectIdName = projectUrl.substring(projectUrl.lastIndexOf('/') + 1); 26 | Pattern pId = Pattern.compile(projectIdPattern); 27 | Matcher m = pId.matcher(projectIdName); 28 | if (m.find()) { 29 | return m.group(); 30 | } 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/util/VersionHelper.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import lombok.val; 5 | 6 | @UtilityClass 7 | public class VersionHelper { 8 | public static int compareVersions(String manifestForgeVersion, String updatedForgeVersion) { 9 | val manArr = manifestForgeVersion.split("\\."); 10 | val updateArr = updatedForgeVersion.split("\\."); 11 | int i = 0; 12 | while (i < manArr.length || i < updateArr.length) { 13 | if (Integer.parseInt(manArr[i]) < Integer.parseInt(updateArr[i])) { 14 | return -1; 15 | } else if (Integer.parseInt(manArr[i]) > Integer.parseInt(updateArr[i])) { 16 | return 1; 17 | } 18 | i++; 19 | } 20 | return 0; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/java/com/nincraft/modpackdownloader/validation/ReleaseType.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.validation; 2 | 3 | import com.beust.jcommander.IParameterValidator; 4 | import com.beust.jcommander.ParameterException; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class ReleaseType implements IParameterValidator { 10 | @Override 11 | public void validate(String name, String value) throws ParameterException { 12 | List validReleaseTypes = Arrays.asList("release", "beta", "alpha"); 13 | if (!validReleaseTypes.contains(value.toLowerCase())) { 14 | throw new ParameterException(String.format("Parameter %s is not a valid release type (Found %s)", name, value)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/main/resources/latest.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "http://play.nincraft.com:8080/job/Mod%20Pack%20Downloader/lastSuccessfulBuild/artifact/modpackdownloader-core/target/${project.build.finalName}.jar" 3 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/java/com/nincraft/modpackdownloader/ModpackDownloaderTest.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader; 2 | 3 | import com.google.gson.Gson; 4 | import com.nincraft.modpackdownloader.container.CurseFile; 5 | import com.nincraft.modpackdownloader.container.Manifest; 6 | import com.nincraft.modpackdownloader.handler.ApplicationUpdateHandler; 7 | import com.nincraft.modpackdownloader.util.VersionHelper; 8 | import lombok.extern.log4j.Log4j2; 9 | import org.apache.commons.collections4.CollectionUtils; 10 | import org.apache.commons.io.FileUtils; 11 | import org.apache.commons.io.filefilter.WildcardFileFilter; 12 | import org.json.simple.JSONObject; 13 | import org.json.simple.parser.JSONParser; 14 | import org.json.simple.parser.ParseException; 15 | import org.junit.After; 16 | import org.junit.Assert; 17 | import org.junit.Ignore; 18 | import org.junit.Test; 19 | 20 | import java.io.File; 21 | import java.io.FileFilter; 22 | import java.io.FileReader; 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | @Log4j2 30 | public class ModpackDownloaderTest { 31 | 32 | private final String RESOURCES = "src/test/resources/"; 33 | 34 | @After 35 | public void cleanUp() throws IOException { 36 | String[] deleteFiles = {RESOURCES + "update-test.json.bak", RESOURCES + "download-test.json.bak", RESOURCES + "update-test.json"}; 37 | for (String s : deleteFiles) { 38 | new File(s).delete(); 39 | } 40 | File backupUpdate = new File(RESOURCES + "update-test-backup.json"); 41 | File updateFile = new File(RESOURCES + "update-test.json"); 42 | FileUtils.copyFile(backupUpdate, updateFile); 43 | } 44 | 45 | @Ignore 46 | @Test 47 | public void testDownloadRelease() throws InterruptedException { 48 | ModpackDownloaderManager manager = new ModpackDownloaderManager(new String[]{"-manifest", RESOURCES + "download-test.json", "-releaseType", "release", "-forceDownload"}); 49 | manager.init(); 50 | manager.processManifests(); 51 | File mod; 52 | List mods = new ArrayList<>(Arrays.asList("Thaumcraft-1.8.9-5.2.4.jar", "DimensionalDoors-2.2.5-test9.jar", "pants.jar", "forge-1.8.9-11.15.1.1902-1.8.9-installer.jar")); 53 | List checkFiles = addMods(mods); 54 | 55 | for (String fileCheck : checkFiles) { 56 | mod = new File(fileCheck); 57 | Assert.assertTrue(mod.exists()); 58 | mod.deleteOnExit(); 59 | } 60 | } 61 | 62 | @Ignore 63 | @Test 64 | public void testDownloadMaxThreads() throws InterruptedException { 65 | ModpackDownloaderManager manager = new ModpackDownloaderManager(new String[]{"-manifest", RESOURCES + "download-test.json", "-maxDownloadThreads", "1"}); 66 | manager.init(); 67 | manager.processManifests(); 68 | File mod; 69 | List mods = new ArrayList<>(Arrays.asList("Thaumcraft-1.8.9-5.2.4.jar", "DimensionalDoors-2.2.5-test9.jar", "pants.jar", "forge-1.8.9-11.15.1.1902-1.8.9-installer.jar")); 70 | List checkFiles = addMods(mods); 71 | 72 | for (String fileCheck : checkFiles) { 73 | mod = new File(fileCheck); 74 | Assert.assertTrue(mod.exists()); 75 | mod.deleteOnExit(); 76 | } 77 | } 78 | 79 | @Ignore 80 | @Test 81 | public void testUpdate() throws InterruptedException, IOException, ParseException { 82 | String manifestName = RESOURCES + "update-test.json"; 83 | File manifestFile = new File(manifestName); 84 | String[] args = {"-manifest", manifestName, "-updateMods", "-updateForge", "-backupVersion", "1.8.9"}; 85 | 86 | Gson gson = new Gson(); 87 | JSONObject jsonLists = (JSONObject) new JSONParser().parse(new FileReader(manifestFile)); 88 | Manifest manifest = gson.fromJson(jsonLists.toString(), Manifest.class); 89 | String oldForgeVersion = manifest.getForgeVersion(); 90 | ModpackDownloaderManager manager = new ModpackDownloaderManager(args); 91 | manager.init(); 92 | manager.processManifests(); 93 | jsonLists = (JSONObject) new JSONParser().parse(new FileReader(manifestFile)); 94 | manifest = gson.fromJson(jsonLists.toString(), Manifest.class); 95 | for (CurseFile curseFile : manifest.getCurseFiles()) { 96 | Assert.assertTrue(curseFile.getFileID() > 0); 97 | } 98 | Assert.assertTrue(VersionHelper.compareVersions(oldForgeVersion.substring(oldForgeVersion.indexOf('-') + 1), 99 | manifest.getForgeVersion().substring(manifest.getForgeVersion().indexOf('-') + 1)) < 0); 100 | } 101 | 102 | @Ignore 103 | @Test 104 | public void testCheckUpdate() throws InterruptedException { 105 | String manifestName = RESOURCES + "update-test.json"; 106 | String[] args = {"-manifest", manifestName, "-checkMCUpdate", "1.10.2"}; 107 | ModpackDownloaderManager manager = new ModpackDownloaderManager(args); 108 | manager.init(); 109 | manager.processManifests(); 110 | } 111 | 112 | @Ignore 113 | @Test 114 | public void testAppUpdate() { 115 | String[] args = {"-updateApp"}; 116 | ModpackDownloaderManager manager = new ModpackDownloaderManager(args); 117 | manager.init(); 118 | ApplicationUpdateHandler.update(); 119 | FileFilter fileFilter = new WildcardFileFilter("ModpackDownloader*jar"); 120 | File directory = new File("."); 121 | List files = Arrays.asList(directory.listFiles(fileFilter)); 122 | Assert.assertTrue(!CollectionUtils.isEmpty(files)); 123 | files.forEach(File::deleteOnExit); 124 | } 125 | 126 | private List addMods(List mods) { 127 | String folder = "mods" + File.separator; 128 | return mods.stream().map(s -> folder + s).collect(Collectors.toList()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/all-mods.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "fileID": 2277394, 5 | "name": "AppleCore", 6 | "projectID": 224472 7 | }, 8 | { 9 | "fileID": 2284622, 10 | "name": "Aroma1997Core", 11 | "projectID": 223735 12 | }, 13 | { 14 | "fileID": 2277001, 15 | "name": "AromaBackup", 16 | "projectID": 225658 17 | }, 18 | { 19 | "fileID": 2273274, 20 | "name": "Baubles", 21 | "projectID": 227083 22 | }, 23 | { 24 | "fileID": 2274840, 25 | "name": "Baubles HUD", 26 | "projectID": 230751 27 | }, 28 | { 29 | "fileID": 2279388, 30 | "name": "Biomes O' Plenty", 31 | "projectID": 220318 32 | }, 33 | { 34 | "fileID": 2279356, 35 | "name": "Blood Magic", 36 | "projectID": 224791 37 | }, 38 | { 39 | "fileID": 2269766, 40 | "name": "Bloodmoon", 41 | "projectID": 226321 42 | }, 43 | { 44 | "fileID": 2280253, 45 | "name": "Chisels and Bits", 46 | "projectID": 231095 47 | }, 48 | { 49 | "fileID": 2273148, 50 | "name": "Durability Show", 51 | "projectID": 226099 52 | }, 53 | { 54 | "fileID": 2269767, 55 | "name": "Fast Leaf Decay", 56 | "projectID": 225839 57 | }, 58 | { 59 | "fileID": 2272962, 60 | "name": "Help Fixer", 61 | "projectID": 223797 62 | }, 63 | { 64 | "fileID": 2281942, 65 | "name": "Immersive Craft", 66 | "projectID": 241268 67 | }, 68 | { 69 | "fileID": 2283028, 70 | "name": "In Case of Emergency", 71 | "projectID": 241925 72 | }, 73 | { 74 | "fileID": 2280666, 75 | "name": "Intangible", 76 | "projectID": 241350 77 | }, 78 | { 79 | "fileID": 2275675, 80 | "name": "Inventory Tweaks", 81 | "projectID": 223094 82 | }, 83 | { 84 | "fileID": 2278859, 85 | "name": "Iron Chests", 86 | "projectID": 228756 87 | }, 88 | { 89 | "fileID": 2282784, 90 | "name": "Just Enough Items", 91 | "projectID": 238222 92 | }, 93 | { 94 | "fileID": 2276884, 95 | "name": "Mantle", 96 | "projectID": 74924 97 | }, 98 | { 99 | "fileID": 2276306, 100 | "name": "MCMultipart", 101 | "projectID": 239431 102 | }, 103 | { 104 | "fileID": 2280806, 105 | "name": "Not Enough Wands", 106 | "projectID": 235595 107 | }, 108 | { 109 | "fileID": 2280569, 110 | "name": "Open Computers", 111 | "projectID": 223008 112 | }, 113 | { 114 | "fileID": 2284425, 115 | "name": "Psi", 116 | "projectID": 241665 117 | }, 118 | { 119 | "fileID": 2283433, 120 | "name": "Random Things", 121 | "projectID": 59816 122 | }, 123 | { 124 | "fileID": 2275910, 125 | "name": "SimpleRetrogen", 126 | "projectID": 240566 127 | }, 128 | { 129 | "fileID": 2279246, 130 | "name": "Substratum", 131 | "projectID": 241113 132 | }, 133 | { 134 | "fileID": 2279928, 135 | "name": "Thaumcraft", 136 | "projectID": 223628 137 | }, 138 | { 139 | "fileID": 2276883, 140 | "name": "Tinkers' Construct", 141 | "projectID": 74072 142 | }, 143 | { 144 | "fileID": 2279788, 145 | "name": "TIS 3D", 146 | "projectID": 238603 147 | } 148 | ], 149 | "thirdParty": [ 150 | { 151 | "name": "Botania", 152 | "url": "https://github.com/williewillus/Botania/releases/download/v0.6.2-beta/Botania.r1.8-248-unofficial-0.6.2.jar" 153 | }, 154 | { 155 | "name": "BetterStorage", 156 | "url": "https://github.com/copygirl/BetterStorage/releases/download/v0.13.1.127/BetterStorage-1.7.10-0.13.1.127.jar" 157 | }, 158 | { 159 | "name": "Buildcraft", 160 | "url": "https://www.dropbox.com/s/q1qppz3sq447173/buildcraft-7.2.0-pre6.jar?dl=1" 161 | }, 162 | { 163 | "name": "Charset", 164 | "url": "http://charset.asie.pl/files/Charset-0.2.1-945baf5589832f727c42cf77.jar" 165 | }, 166 | { 167 | "name": "Dimensional Doors", 168 | "url": "https://github.com/CannibalVox/DimDoors/releases/download/2.2.5-test9/DimensionalDoors-2.2.5-test9.jar" 169 | } 170 | ] 171 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/botania-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "minecraft": { 3 | "version": "1.7.10", 4 | "modLoaders": [ 5 | { 6 | "id": "forge-10.13.4.1566", 7 | "primary": true 8 | } 9 | ] 10 | }, 11 | "manifestType": "minecraftModpack", 12 | "manifestVersion": 1, 13 | "name": "Botania Skyblock The Modpack The Mod The Modpack", 14 | "version": "1.0.28", 15 | "author": "Vazkii", 16 | "projectID": 233293, 17 | "files": [ 18 | { 19 | "projectID": 227706, 20 | "fileID": 2226922, 21 | "required": true 22 | }, 23 | { 24 | "projectID": 227979, 25 | "fileID": 2229254, 26 | "required": true 27 | }, 28 | { 29 | "projectID": 231454, 30 | "fileID": 2242852, 31 | "required": true 32 | }, 33 | { 34 | "projectID": 232502, 35 | "fileID": 2246106, 36 | "required": true 37 | }, 38 | { 39 | "projectID": 232919, 40 | "fileID": 2248061, 41 | "required": true 42 | }, 43 | { 44 | "projectID": 231879, 45 | "fileID": 2243821, 46 | "required": true 47 | }, 48 | { 49 | "projectID": 223094, 50 | "fileID": 2210792, 51 | "required": true 52 | }, 53 | { 54 | "projectID": 229068, 55 | "fileID": 2232104, 56 | "required": true 57 | }, 58 | { 59 | "projectID": 235577, 60 | "fileID": 2267308, 61 | "required": true 62 | }, 63 | { 64 | "projectID": 233716, 65 | "fileID": 2254315, 66 | "required": true 67 | }, 68 | { 69 | "projectID": 238372, 70 | "fileID": 2268525, 71 | "required": true 72 | }, 73 | { 74 | "projectID": 238618, 75 | "fileID": 2269669, 76 | "required": true 77 | }, 78 | { 79 | "projectID": 233342, 80 | "fileID": 2270358, 81 | "required": true 82 | }, 83 | { 84 | "projectID": 228932, 85 | "fileID": 2255823, 86 | "required": true 87 | }, 88 | { 89 | "projectID": 231453, 90 | "fileID": 2242214, 91 | "required": true 92 | }, 93 | { 94 | "projectID": 227083, 95 | "fileID": 2224857, 96 | "required": true 97 | }, 98 | { 99 | "projectID": 227441, 100 | "fileID": 2272557, 101 | "required": true 102 | }, 103 | { 104 | "projectID": 237852, 105 | "fileID": 2272796, 106 | "required": true 107 | }, 108 | { 109 | "projectID": 227795, 110 | "fileID": 2271214, 111 | "required": true 112 | }, 113 | { 114 | "projectID": 224472, 115 | "fileID": 2277393, 116 | "required": true 117 | }, 118 | { 119 | "projectID": 238534, 120 | "fileID": 2275426, 121 | "required": true 122 | }, 123 | { 124 | "projectID": 232131, 125 | "fileID": 2277197, 126 | "required": true 127 | }, 128 | { 129 | "projectID": 230114, 130 | "fileID": 2281148, 131 | "required": true 132 | }, 133 | { 134 | "projectID": 69118, 135 | "fileID": 2280761, 136 | "required": true 137 | }, 138 | { 139 | "projectID": 225643, 140 | "fileID": 2283837, 141 | "required": true 142 | }, 143 | { 144 | "projectID": 239049, 145 | "fileID": 2271870, 146 | "required": true 147 | }, 148 | { 149 | "projectID": 238891, 150 | "fileID": 2282307, 151 | "required": true 152 | }, 153 | { 154 | "projectID": 222789, 155 | "fileID": 2224003, 156 | "required": true 157 | }, 158 | { 159 | "projectID": 230898, 160 | "fileID": 2274165, 161 | "required": true 162 | }, 163 | { 164 | "projectID": 241895, 165 | "fileID": 2284547, 166 | "required": true 167 | }, 168 | { 169 | "projectID": 228525, 170 | "fileID": 2278984, 171 | "required": true 172 | }, 173 | { 174 | "projectID": 222213, 175 | "fileID": 2262089, 176 | "required": true 177 | }, 178 | { 179 | "projectID": 222211, 180 | "fileID": 2262091, 181 | "required": true 182 | }, 183 | { 184 | "projectID": 222303, 185 | "fileID": 2284819, 186 | "required": true 187 | }, 188 | { 189 | "projectID": 233071, 190 | "fileID": 2285865, 191 | "required": true 192 | }, 193 | { 194 | "projectID": 223852, 195 | "fileID": 2284904, 196 | "required": true 197 | } 198 | ], 199 | "overrides": "overrides" 200 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/client-mods.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "fileID": 2275454, 5 | "projectID": 224223 6 | }, 7 | { 8 | "fileID": 2269324, 9 | "projectID": 32274 10 | }, 11 | { 12 | "fileID": 2275101, 13 | "projectID": 238372 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/download-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "minecraft": { 3 | "version": "1.8.9", 4 | "modLoaders": [ 5 | { 6 | "id": "forge-11.15.1.1902", 7 | "primary": true, 8 | "downloadInstaller": true, 9 | "release": "latest", 10 | "name": "forge", 11 | "folder": "mods" 12 | } 13 | ] 14 | }, 15 | "files": [ 16 | { 17 | "fileID": 2287496, 18 | "projectID": 223628, 19 | "name": "Thaumcraft" 20 | }, 21 | { 22 | "fileID": 2423802, 23 | "projectID": 59882, 24 | "name": "modular-forcefield-system", 25 | "rename": "pants.jar" 26 | }, 27 | { 28 | "fileID": 2287496, 29 | "projectID": 223628, 30 | "name": "Thaumcraft", 31 | "release": "beta" 32 | } 33 | ], 34 | "thirdParty": [ 35 | { 36 | "url": "https://github.com/CannibalVox/DimDoors/releases/download/2.2.5-test9/DimensionalDoors-2.2.5-test9.jar", 37 | "name": "Dimensional Doors" 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/forge.json: -------------------------------------------------------------------------------- 1 | { 2 | "thirdParty": [ 3 | { 4 | "name": "Forge", 5 | "url": "http://files.minecraftforge.net/maven/net/minecraftforge/forge/1.8.9-11.15.1.1724/forge-1.8.9-11.15.1.1724-installer.jar" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/twbb-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "minecraft": { 3 | "version": "1.7.10", 4 | "modLoaders": [ 5 | { 6 | "id": "forge-10.13.4.1566", 7 | "primary": true, 8 | "folder": "common/base/loaders", 9 | "downloadInstaller": true, 10 | "downloadUniversal": true 11 | } 12 | ] 13 | }, 14 | "files": [ 15 | { 16 | "fileID": 2284130, 17 | "projectID": 225635, 18 | "name": "Agricraft" 19 | }, 20 | { 21 | "fileID": 2273753, 22 | "projectID": 221863, 23 | "name": "Another One Bites The Dust" 24 | }, 25 | { 26 | "fileID": 2263870, 27 | "projectID": 230292, 28 | "name": "Another One Bites The Dust Berry Bushes" 29 | }, 30 | { 31 | "fileID": 2271214, 32 | "projectID": 227795, 33 | "name": "Antique Atlas" 34 | }, 35 | { 36 | "fileID": 2277393, 37 | "projectID": 224472, 38 | "name": "AppleCore" 39 | }, 40 | { 41 | "fileID": 2257644, 42 | "projectID": 223735, 43 | "name": "Aroma1997Core" 44 | }, 45 | { 46 | "fileID": 2284754, 47 | "projectID": 225658, 48 | "name": "AromaBackup" 49 | }, 50 | { 51 | "fileID": 2231153, 52 | "projectID": 228356, 53 | "name": "ATG" 54 | }, 55 | { 56 | "fileID": 2274344, 57 | "projectID": 230120, 58 | "name": "Better Beginnings" 59 | }, 60 | { 61 | "fileID": 2281726, 62 | "projectID": 220318, 63 | "name": "Biomes O' Plenty" 64 | }, 65 | { 66 | "fileID": 2276097, 67 | "projectID": 230368, 68 | "name": "Block Properties" 69 | }, 70 | { 71 | "fileID": 2264826, 72 | "projectID": 224791, 73 | "name": "Blood Magic" 74 | }, 75 | { 76 | "fileID": 2227503, 77 | "projectID": 59218, 78 | "name": "bspkrsCore" 79 | }, 80 | { 81 | "fileID": 2262089, 82 | "projectID": 222213, 83 | "name": "CodeChickenCore" 84 | }, 85 | { 86 | "fileID": 2283118, 87 | "projectID": 69162, 88 | "name": "CoFHCore" 89 | }, 90 | { 91 | "fileID": 2248699, 92 | "projectID": 222908, 93 | "name": "CookieCore" 94 | }, 95 | { 96 | "fileID": 2263858, 97 | "projectID": 231484, 98 | "name": "Cooking For Blockheads" 99 | }, 100 | { 101 | "fileID": 2285744, 102 | "projectID": 237749, 103 | "name": "CoroUtil" 104 | }, 105 | { 106 | "fileID": 2264037, 107 | "projectID": 237130, 108 | "name": "Custom Ore Gen" 109 | }, 110 | { 111 | "fileID": 2240043, 112 | "projectID": 224039, 113 | "name": "Dark Menagerie" 114 | }, 115 | { 116 | "fileID": 2214317, 117 | "projectID": 60098, 118 | "name": "Deadly World" 119 | }, 120 | { 121 | "fileID": 2234692, 122 | "projectID": 224942, 123 | "name": "Dense Ores" 124 | }, 125 | { 126 | "fileID": 2248782, 127 | "projectID": 74246, 128 | "name": "Doomlike Dungeons" 129 | }, 130 | { 131 | "fileID": 2266611, 132 | "projectID": 231868, 133 | "name": "EnderCore" 134 | }, 135 | { 136 | "fileID": 2269710, 137 | "projectID": 230236, 138 | "name": "Enviromine" 139 | }, 140 | { 141 | "fileID": 2288067, 142 | "projectID": 233180, 143 | "name": "ExCore" 144 | }, 145 | { 146 | "fileID": 2242266, 147 | "projectID": 66776, 148 | "name": "Falling Meteors" 149 | }, 150 | { 151 | "fileID": 2281555, 152 | "projectID": 229708, 153 | "name": "Farseek API" 154 | }, 155 | { 156 | "fileID": 2265558, 157 | "projectID": 224867, 158 | "name": "Flaxbeard's Steam Power" 159 | }, 160 | { 161 | "fileID": 2242993, 162 | "projectID": 229323, 163 | "name": "Forge Multipart" 164 | }, 165 | { 166 | "fileID": 2280216, 167 | "projectID": 238928, 168 | "name": "Hammerz" 169 | }, 170 | { 171 | "fileID": 2262352, 172 | "projectID": 225957, 173 | "name": "Hardcore Darkness" 174 | }, 175 | { 176 | "fileID": 2274119, 177 | "projectID": 228015, 178 | "name": "Hardcore Ender Expansion" 179 | }, 180 | { 181 | "fileID": 2282932, 182 | "projectID": 232257, 183 | "name": "Harder Wildlife" 184 | }, 185 | { 186 | "fileID": 2282930, 187 | "projectID": 232071, 188 | "name": "HardLib" 189 | }, 190 | { 191 | "fileID": 2237679, 192 | "projectID": 224476, 193 | "name": "Hunger Overhaul" 194 | }, 195 | { 196 | "fileID": 2245004, 197 | "projectID": 228798, 198 | "name": "Iguana Tweaks" 199 | }, 200 | { 201 | "fileID": 2237600, 202 | "projectID": 223248, 203 | "name": "Iguana's Tinker Tweaks" 204 | }, 205 | { 206 | "fileID": 2227584, 207 | "projectID": 227875, 208 | "name": "Infernal Mobs" 209 | }, 210 | { 211 | "fileID": 2238507, 212 | "projectID": 223792, 213 | "name": "INPureCore" 214 | }, 215 | { 216 | "fileID": 2210792, 217 | "projectID": 223094, 218 | "name": "Inventory Tweaks" 219 | }, 220 | { 221 | "fileID": 2283911, 222 | "projectID": 237746, 223 | "name": "Localized Weather and Storms 2" 224 | }, 225 | { 226 | "fileID": 2225146, 227 | "projectID": 225605, 228 | "name": "LunatriusCore" 229 | }, 230 | { 231 | "fileID": 2268607, 232 | "projectID": 224770, 233 | "name": "Lycanite's Mobs" 234 | }, 235 | { 236 | "fileID": 2264244, 237 | "projectID": 74924, 238 | "name": "Mantle" 239 | }, 240 | { 241 | "fileID": 2217136, 242 | "projectID": 59641, 243 | "name": "Metallurgy" 244 | }, 245 | { 246 | "fileID": 2215714, 247 | "projectID": 78640, 248 | "name": "Metallurgy Core" 249 | }, 250 | { 251 | "fileID": 2262994, 252 | "projectID": 59710, 253 | "name": "Mine and Blade Battlegear 2" 254 | }, 255 | { 256 | "fileID": 2259434, 257 | "projectID": 224029, 258 | "name": "MineTweaker3" 259 | }, 260 | { 261 | "fileID": 2281386, 262 | "projectID": 224298, 263 | "name": "Mob Properties" 264 | }, 265 | { 266 | "fileID": 2232516, 267 | "projectID": 76734, 268 | "name": "MobiusCore" 269 | }, 270 | { 271 | "fileID": 2270009, 272 | "projectID": 220954, 273 | "name": "ModTweaker" 274 | }, 275 | { 276 | "fileID": 2216125, 277 | "projectID": 74120, 278 | "name": "Natura" 279 | }, 280 | { 281 | "fileID": 2214321, 282 | "projectID": 224296, 283 | "name": "Natural Absorption" 284 | }, 285 | { 286 | "fileID": 2282522, 287 | "projectID": 225251, 288 | "name": "NEI Integration" 289 | }, 290 | { 291 | "fileID": 2277488, 292 | "projectID": 66675, 293 | "name": "Nether Ores" 294 | }, 295 | { 296 | "fileID": 2288662, 297 | "projectID": 238622, 298 | "name": "NincraftLib" 299 | }, 300 | { 301 | "fileID": 2262091, 302 | "projectID": 222211, 303 | "name": "Not Enough Items" 304 | }, 305 | { 306 | "fileID": 2272129, 307 | "projectID": 233472, 308 | "name": "Ore Flowers" 309 | }, 310 | { 311 | "fileID": 2270206, 312 | "projectID": 221857, 313 | "name": "Pam's Harvestcraft" 314 | }, 315 | { 316 | "fileID": 2206430, 317 | "projectID": 221916, 318 | "name": "Pam's Temperate Plants" 319 | }, 320 | { 321 | "fileID": 2285499, 322 | "projectID": 224125, 323 | "name": "PneumaticCraft" 324 | }, 325 | { 326 | "fileID": 2229278, 327 | "projectID": 224492, 328 | "name": "PoorOres" 329 | }, 330 | { 331 | "fileID": 2225310, 332 | "projectID": 59816, 333 | "name": "Random Things" 334 | }, 335 | { 336 | "fileID": 2209081, 337 | "projectID": 220611, 338 | "name": "RPG Advanced Mod" 339 | }, 340 | { 341 | "fileID": 2280980, 342 | "projectID": 234840, 343 | "name": "Salty Mod" 344 | }, 345 | { 346 | "fileID": 2264022, 347 | "projectID": 231126, 348 | "name": "Simple Achievements" 349 | }, 350 | { 351 | "fileID": 2281145, 352 | "projectID": 224301, 353 | "name": "Special AI" 354 | }, 355 | { 356 | "fileID": 2279900, 357 | "projectID": 59968, 358 | "name": "Special Mobs" 359 | }, 360 | { 361 | "fileID": 2248339, 362 | "projectID": 225606, 363 | "name": "Stackie" 364 | }, 365 | { 366 | "fileID": 2246908, 367 | "projectID": 227886, 368 | "name": "Stalker Creepers" 369 | }, 370 | { 371 | "fileID": 2281587, 372 | "projectID": 229769, 373 | "name": "Streams" 374 | }, 375 | { 376 | "fileID": 2277396, 377 | "projectID": 220811, 378 | "name": "The Spice of Life" 379 | }, 380 | { 381 | "fileID": 2282520, 382 | "projectID": 222880, 383 | "name": "Thermal Foundation" 384 | }, 385 | { 386 | "fileID": 2277012, 387 | "projectID": 74072, 388 | "name": "Tinkers' Construct" 389 | }, 390 | { 391 | "fileID": 2269014, 392 | "projectID": 233076, 393 | "name": "Tinkers' Steelworks" 394 | }, 395 | { 396 | "fileID": 2281619, 397 | "projectID": 227449, 398 | "name": "TNTUtils" 399 | }, 400 | { 401 | "fileID": 2261887, 402 | "projectID": 220845, 403 | "name": "Too Much Loot" 404 | }, 405 | { 406 | "fileID": 2276961, 407 | "projectID": 241052, 408 | "name": "TWBB Tweaks" 409 | }, 410 | { 411 | "fileID": 2238492, 412 | "projectID": 227639, 413 | "name": "Twilight Forest" 414 | }, 415 | { 416 | "fileID": 2272810, 417 | "projectID": 236923, 418 | "name": "Ye Olde Tanks" 419 | }, 420 | { 421 | "fileID": 2265743, 422 | "projectID": 237754, 423 | "name": "Zombie Awareness" 424 | } 425 | ], 426 | "thirdParty": [ 427 | { 428 | "url": "http://files.vex.tty.sh/AsieLib/AsieLib-1.7.10-0.4.5.jar", 429 | "name": "AsieLib" 430 | }, 431 | { 432 | "url": "https://github.com/copygirl/BetterStorage/releases/download/v0.13.1.127/BetterStorage-1.7.10-0.13.1.127.jar", 433 | "name": "BetterStorage" 434 | }, 435 | { 436 | "url": "http://download1645.mediafire.com/54mtw8wd48bg/ciaw1m3e7nt1cw8/ChameleonCreepers-1.2-1.7.10.jar", 437 | "name": "Chameleon Creepers" 438 | }, 439 | { 440 | "url": "https://github.com/INpureProjects/INpureCore/raw/master/libs/commons-codec-1.9.jar", 441 | "name": "common Codec", 442 | "folder": "common/base/mods/1.7.10" 443 | }, 444 | { 445 | "url": "https://github.com/INpureProjects/INpureCore/raw/master/libs/commons-compress-1.8.1.jar", 446 | "name": "Common Compress", 447 | "folder": "common/base/mods/1.7.10" 448 | }, 449 | { 450 | "url": "http://download1507.mediafire.com/3ham0imat20g/2gaj88zw41opn8q/AvoidExplodingCreepers-1.7.10-LATEST.jar", 451 | "name": "Creeper Awareness" 452 | }, 453 | { 454 | "url": "https://github.com/CannibalVox/DimDoors/releases/download/2.2.5-test9/DimensionalDoors-2.2.5-test9.jar", 455 | "name": "Dimensional Doors" 456 | }, 457 | { 458 | "url": "http://download2218.mediafire.com/arly2nfr77sg/kdxf6q3kzef75fa/airoverhaul-1.7.10_1.0.jar", 459 | "name": "Drowning Overhaul" 460 | }, 461 | { 462 | "url": "http://ci.tterrag.com/job/ModpackTweaks/26/artifact/build/libs/ModpackTweaks-MC1.7.10-1.2.0-26.jar", 463 | "name": "Modpack Tweaks" 464 | }, 465 | { 466 | "url": "http://dl.dropboxusercontent.com/u/4295615/OpenEye-0.6-1.7.10.jar", 467 | "name": "OpenEye" 468 | }, 469 | { 470 | "url": "https://www.dropbox.com/s/6t4vp3wn4xr83xn/primitivemobs-1.0c-1.7.10.jar?dl=1", 471 | "name": "Primitive Mobs" 472 | }, 473 | { 474 | "url": "http://codeheist.net:8080/view/Somnia/job/Somnia%20-%201.7.10/55/artifact/dist/Somnia-1.4.8.55.jar", 475 | "name": "Somnia" 476 | } 477 | ] 478 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/update-test-backup.json: -------------------------------------------------------------------------------- 1 | { 2 | "minecraft": { 3 | "version": "1.9.4", 4 | "modLoaders": [ 5 | { 6 | "id": "forge-12.17.0.1990", 7 | "primary": true, 8 | "downloadInstaller": true, 9 | "release": "latest", 10 | "name": "forge", 11 | "folder": "mods" 12 | } 13 | ] 14 | }, 15 | "files": [ 16 | { 17 | "fileID": 0, 18 | "projectID": 223628, 19 | "name": "Thaumcraft" 20 | } 21 | ], 22 | "thirdParty": [ 23 | { 24 | "url": "https://github.com/CannibalVox/DimDoors/releases/download/2.2.5-test9/DimensionalDoors-2.2.5-test9.jar", 25 | "name": "Dimensional Doors" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /modpackdownloader-core/src/test/resources/update-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "minecraft": { 3 | "version": "1.9.4", 4 | "modLoaders": [ 5 | { 6 | "id": "forge-12.17.0.1990", 7 | "primary": true, 8 | "downloadInstaller": true, 9 | "release": "latest", 10 | "name": "forge", 11 | "folder": "mods" 12 | } 13 | ] 14 | }, 15 | "files": [ 16 | { 17 | "fileID": 0, 18 | "projectID": 223628, 19 | "name": "Thaumcraft" 20 | } 21 | ], 22 | "thirdParty": [ 23 | { 24 | "url": "https://github.com/CannibalVox/DimDoors/releases/download/2.2.5-test9/DimensionalDoors-2.2.5-test9.jar", 25 | "name": "Dimensional Doors" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /modpackdownloader-gui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.nincraft 7 | modpackdownloader 8 | 0.7.2 9 | 10 | 11 | modpackdownloader-gui 12 | 13 | Modpack Downloader GUI 14 | 15 | 16 | 17 | com.nincraft 18 | modpackdownloader-core 19 | 20 | 21 | org.projectlombok 22 | lombok 23 | 24 | 25 | org.openjfx 26 | javafx-controls 27 | 14 28 | 29 | 30 | org.openjfx 31 | javafx-fxml 32 | 14.0.2.1 33 | 34 | 35 | 36 | 37 | ModpackDownloader-gui-${short.project.version} 38 | 39 | 40 | org.codehaus.mojo 41 | build-helper-maven-plugin 42 | 1.7 43 | 44 | 45 | regex-property 46 | process-resources 47 | 48 | regex-property 49 | 50 | 51 | short.project.version 52 | ${project.version} 53 | ^(\d\.\d(?>\.\d)?)(-SNAPSHOT)? 54 | $1 55 | true 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-shade-plugin 63 | 2.4.3 64 | 65 | 66 | package 67 | 68 | shade 69 | 70 | 71 | 72 | 74 | com.nincraft.modpackdownloader.gui.ModpackDownloaderGUI 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /modpackdownloader-gui/src/main/java/com/nincraft/modpackdownloader/gui/Controller.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.gui; 2 | 3 | import com.nincraft.modpackdownloader.ModpackDownloaderManager; 4 | import javafx.application.HostServices; 5 | import javafx.fxml.FXML; 6 | import javafx.scene.control.*; 7 | import lombok.extern.log4j.Log4j2; 8 | 9 | import java.util.concurrent.ExecutorService; 10 | import java.util.concurrent.Executors; 11 | 12 | @Log4j2 13 | public class Controller { 14 | 15 | @FXML 16 | private ProgressBar progressBar; 17 | @FXML 18 | private TextArea logTextArea; 19 | @FXML 20 | private TextField projectIdTextBox; 21 | @FXML 22 | private TextField fileIdTextBox; 23 | @FXML 24 | private Button downloadButton; 25 | @FXML 26 | private Hyperlink reportIssue; 27 | @FXML 28 | private Hyperlink help; 29 | private HostServices hostServices; 30 | 31 | void init() { 32 | progressBar.setProgress(0); 33 | help.setOnAction(event -> hostServices.showDocument("https://github.com/Nincraft/ModPackDownloader/wiki/GUI-Usage")); 34 | reportIssue.setOnAction(event -> hostServices.showDocument("https://github.com/Nincraft/ModPackDownloader/issues/new")); 35 | downloadButton.setOnAction(event -> startDownload()); 36 | logTextArea.setEditable(false); 37 | TextAreaAppender.setTextArea(logTextArea); 38 | } 39 | 40 | private void startDownload() { 41 | ExecutorService executor = Executors.newSingleThreadExecutor(); 42 | executor.submit(() -> { 43 | try { 44 | String projectUrl = projectIdTextBox.getText(); 45 | String fileIdText = fileIdTextBox.getText(); 46 | String projectIdName = projectUrl.substring(projectUrl.lastIndexOf('/') + 1); 47 | String[] args = {"-updateCurseModPack", projectIdName, "-curseFileId", fileIdText}; 48 | ModpackDownloaderManager modpackDownloaderManager = new ModpackDownloaderManager(args); 49 | modpackDownloaderManager.init(); 50 | modpackDownloaderManager.processManifests(); 51 | } catch (InterruptedException e) { 52 | log.error(e); 53 | executor.shutdownNow(); 54 | } 55 | }); 56 | executor.shutdownNow(); 57 | } 58 | 59 | void setHostServices(HostServices hostServices) { 60 | this.hostServices = hostServices; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /modpackdownloader-gui/src/main/java/com/nincraft/modpackdownloader/gui/ModpackDownloaderGUI.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.gui; 2 | 3 | import javafx.application.Application; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.scene.Parent; 6 | import javafx.scene.Scene; 7 | import javafx.stage.Stage; 8 | 9 | 10 | public class ModpackDownloaderGUI extends Application { 11 | 12 | public static void main(String[] args) { 13 | launch(args); 14 | } 15 | 16 | @Override 17 | public void start(Stage primaryStage) throws Exception { 18 | FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource("mpd.fxml")); 19 | Parent root = loader.load(); 20 | Controller controller = loader.getController(); 21 | controller.setHostServices(getHostServices()); 22 | controller.init(); 23 | primaryStage.setTitle("Modpack Downloader by Nincraft Team"); 24 | primaryStage.setScene(new Scene(root, 600, 410)); 25 | primaryStage.show(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modpackdownloader-gui/src/main/java/com/nincraft/modpackdownloader/gui/TextAreaAppender.java: -------------------------------------------------------------------------------- 1 | package com.nincraft.modpackdownloader.gui; 2 | 3 | import javafx.application.Platform; 4 | import javafx.scene.control.TextArea; 5 | import org.apache.logging.log4j.core.Filter; 6 | import org.apache.logging.log4j.core.Layout; 7 | import org.apache.logging.log4j.core.LogEvent; 8 | import org.apache.logging.log4j.core.appender.AbstractAppender; 9 | import org.apache.logging.log4j.core.config.plugins.Plugin; 10 | import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 11 | import org.apache.logging.log4j.core.config.plugins.PluginElement; 12 | import org.apache.logging.log4j.core.config.plugins.PluginFactory; 13 | import org.apache.logging.log4j.core.layout.PatternLayout; 14 | 15 | import java.io.Serializable; 16 | import java.util.concurrent.locks.Lock; 17 | import java.util.concurrent.locks.ReadWriteLock; 18 | import java.util.concurrent.locks.ReentrantReadWriteLock; 19 | 20 | @Plugin( 21 | name = "TextAreaAppender", 22 | category = "Core", 23 | elementType = "appender", 24 | printObject = true) 25 | public final class TextAreaAppender extends AbstractAppender { 26 | 27 | private static TextArea textArea; 28 | 29 | 30 | private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); 31 | private final Lock readLock = rwLock.readLock(); 32 | 33 | 34 | protected TextAreaAppender(String name, Filter filter, 35 | Layout layout, 36 | final boolean ignoreExceptions) { 37 | super(name, filter, layout, ignoreExceptions); 38 | } 39 | 40 | /** 41 | * Factory method. Log4j will parse the configuration and call this factory 42 | * method to construct the appender with 43 | * the configured attributes. 44 | * 45 | * @param name Name of appender 46 | * @param layout Log layout of appender 47 | * @param filter Filter for appender 48 | * @return The TextAreaAppender 49 | */ 50 | @PluginFactory 51 | public static TextAreaAppender createAppender( 52 | @PluginAttribute("name") String name, 53 | @PluginElement("Layout") Layout layout, 54 | @PluginElement("Filter") final Filter filter) { 55 | if (name == null) { 56 | LOGGER.error("No name provided for TextAreaAppender"); 57 | return null; 58 | } 59 | if (layout == null) { 60 | layout = PatternLayout.createDefaultLayout(); 61 | } 62 | return new TextAreaAppender(name, filter, layout, true); 63 | } 64 | 65 | /** 66 | * Set TextArea to append 67 | * 68 | * @param textArea TextArea to append 69 | */ 70 | public static void setTextArea(TextArea textArea) { 71 | TextAreaAppender.textArea = textArea; 72 | } 73 | 74 | @Override 75 | public void append(LogEvent event) { 76 | readLock.lock(); 77 | 78 | final String message = new String(getLayout().toByteArray(event)); 79 | 80 | try { 81 | Platform.runLater(() -> { 82 | try { 83 | if (textArea != null) { 84 | if (textArea.getText().length() == 0) { 85 | textArea.setText(message); 86 | } else { 87 | textArea.selectEnd(); 88 | textArea.insertText(textArea.getText().length(), 89 | message); 90 | } 91 | } 92 | } catch (final Throwable t) { 93 | // 94 | } 95 | }); 96 | } catch (final IllegalStateException ex) { 97 | ex.printStackTrace(); 98 | 99 | } finally { 100 | readLock.unlock(); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /modpackdownloader-gui/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /modpackdownloader-gui/src/main/resources/mpd.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 |
9 | 10 | 11 | 12 | 13 |