├── .editorconfig ├── .github └── workflows │ ├── check_build.yml │ ├── crowdin.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── res └── screenshot1.jpg ├── settings.gradle └── src └── main ├── java └── com │ └── terraformersmc │ └── modmenu │ ├── ModMenu.java │ ├── ModMenuModMenuCompat.java │ ├── TextPlaceholderApiCompat.java │ ├── api │ ├── ConfigScreenFactory.java │ ├── ModMenuApi.java │ ├── UpdateChannel.java │ ├── UpdateChecker.java │ └── UpdateInfo.java │ ├── config │ ├── FileOnlyConfig.java │ ├── ModMenuConfig.java │ ├── ModMenuConfigManager.java │ └── option │ │ ├── BooleanConfigOption.java │ │ ├── ConfigOptionStorage.java │ │ ├── EnumConfigOption.java │ │ ├── OptionConvertible.java │ │ └── StringSetConfigOption.java │ ├── event │ └── ModMenuEventHandler.java │ ├── gui │ ├── ModMenuOptionsScreen.java │ ├── ModsScreen.java │ └── widget │ │ ├── DescriptionListWidget.java │ │ ├── LegacyTexturedButtonWidget.java │ │ ├── ModListWidget.java │ │ ├── ModMenuButtonWidget.java │ │ ├── UpdateAvailableBadge.java │ │ ├── UpdateCheckerTexturedButtonWidget.java │ │ └── entries │ │ ├── ChildEntry.java │ │ ├── IndependentEntry.java │ │ ├── ModListEntry.java │ │ └── ParentEntry.java │ ├── mixin │ ├── AccessorGridWidget.java │ ├── MixinGameMenu.java │ └── MixinTitleScreen.java │ └── util │ ├── DrawingUtil.java │ ├── EnumToLowerCaseJsonConverter.java │ ├── HttpUtil.java │ ├── JsonUtil.java │ ├── ModMenuScreenTexts.java │ ├── OptionalUtil.java │ ├── TranslationUtil.java │ ├── UpdateCheckerThreadFactory.java │ ├── UpdateCheckerUtil.java │ ├── VersionUtil.java │ └── mod │ ├── Mod.java │ ├── ModBadgeRenderer.java │ ├── ModSearch.java │ ├── ModrinthUpdateInfo.java │ ├── fabric │ ├── CustomValueUtil.java │ ├── FabricDummyParentMod.java │ ├── FabricIconHandler.java │ ├── FabricLoaderUpdateChecker.java │ └── FabricMod.java │ └── quilt │ ├── QuiltLoaderUpdateChecker.java │ └── QuiltMod.java └── resources ├── assets └── modmenu │ ├── fabric.png │ ├── icon.png │ ├── java_icon.png │ ├── lang │ ├── af_za.json │ ├── an-ES.json │ ├── ar_sa.json │ ├── ast_es.json │ ├── az_az.json │ ├── ba_ru.json │ ├── bak.json │ ├── bar.json │ ├── be_by.json │ ├── bg_bg.json │ ├── bn-BD.json │ ├── br_fr.json │ ├── brb.json │ ├── bs_ba.json │ ├── ca_es.json │ ├── chn.json │ ├── ckb-IR.json │ ├── cs_cz.json │ ├── csb-PL.json │ ├── cv-CU.json │ ├── cy_gb.json │ ├── da_dk.json │ ├── de_at.json │ ├── de_ch.json │ ├── de_de.json │ ├── dsb-DE.json │ ├── egl.json │ ├── el_gr.json │ ├── en_au.json │ ├── en_ca.json │ ├── en_gb.json │ ├── en_nz.json │ ├── en_pt.json │ ├── en_ud.json │ ├── en_us.json │ ├── enp.json │ ├── enws.json │ ├── eo_uy.json │ ├── es_ar.json │ ├── es_cl.json │ ├── es_ec.json │ ├── es_es.json │ ├── es_mx.json │ ├── es_uy.json │ ├── es_ve.json │ ├── esan.json │ ├── et_ee.json │ ├── eu_es.json │ ├── fa_ir.json │ ├── fi_fi.json │ ├── fil_ph.json │ ├── fo_fo.json │ ├── fr_ca.json │ ├── fr_fr.json │ ├── fra_de.json │ ├── fur_it.json │ ├── fy_nl.json │ ├── ga_ie.json │ ├── gd_gb.json │ ├── gl_es.json │ ├── got_de.json │ ├── gv_im.json │ ├── haw_us.json │ ├── he_il.json │ ├── hes.json │ ├── hi_in.json │ ├── hr_hr.json │ ├── hsb-DE.json │ ├── hu_hu.json │ ├── hy_am.json │ ├── id_id.json │ ├── ig_ng.json │ ├── io_en.json │ ├── is_is.json │ ├── isv.json │ ├── it_it.json │ ├── ja_jp.json │ ├── jbo_en.json │ ├── ka_ge.json │ ├── kab-KAB.json │ ├── kk_kz.json │ ├── kn_in.json │ ├── ko_kr.json │ ├── ksh.json │ ├── kw_gb.json │ ├── la_la.json │ ├── lb_lu.json │ ├── li_li.json │ ├── lmo.json │ ├── lol_us.json │ ├── lt_lt.json │ ├── lv_lv.json │ ├── lzh.json │ ├── me-ME.json │ ├── mi_NZ.json │ ├── mk_mk.json │ ├── mn_mn.json │ ├── moe.json │ ├── moh-CA.json │ ├── ms_my.json │ ├── mt_mt.json │ ├── nah.json │ ├── nds_de.json │ ├── ne-NP.json │ ├── nl_be.json │ ├── nl_nl.json │ ├── nn_no.json │ ├── no_no.json │ ├── nuk.json │ ├── oc_fr.json │ ├── oj-CA.json │ ├── ovd.json │ ├── pl_pl.json │ ├── pt_br.json │ ├── pt_pt.json │ ├── qya_aa.json │ ├── ro_ro.json │ ├── rpr.json │ ├── ru_ru.json │ ├── sah-SAH.json │ ├── scn.json │ ├── se_no.json │ ├── sjd.json │ ├── sk_sk.json │ ├── sl_si.json │ ├── so_so.json │ ├── sq_al.json │ ├── sr-Cyrl-ME.json │ ├── sr_sp.json │ ├── sv_se.json │ ├── swg.json │ ├── sxu.json │ ├── szl.json │ ├── ta_in.json │ ├── th_th.json │ ├── tl_ph.json │ ├── tlh_aa.json │ ├── tok.json │ ├── tr_tr.json │ ├── tt_ru.json │ ├── tzl_tzl.json │ ├── uk_ua.json │ ├── ur-PK.json │ ├── uz-UZ.json │ ├── val_es.json │ ├── vec_it.json │ ├── vi_vn.json │ ├── yi_de.json │ ├── yo_ng.json │ ├── zh_cn.json │ ├── zh_hk.json │ ├── zh_tw.json │ └── zlm-Arab.json │ ├── minecraft_icon.png │ ├── textures │ └── gui │ │ ├── configure_button.png │ │ ├── filters_button.png │ │ ├── mod_configuration.png │ │ ├── mods_button.png │ │ ├── mods_button_alt.png │ │ ├── mods_button_alt3.png │ │ └── parent_mod.png │ ├── unknown_icon.png │ └── unknown_parent.png ├── fabric.mod.json ├── high_contrast └── assets │ └── modmenu │ └── textures │ └── gui │ ├── configure_button.png │ ├── filters_button.png │ └── mods_button.png ├── mixins.modmenu.json └── programmer_art └── assets └── modmenu └── textures └── gui ├── configure_button.png ├── filters_button.png ├── mods_button.png └── parent_mod.png /.github/workflows/check_build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: Check Build 7 | on: [push, pull_request] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | # Use these Java versions 14 | java: [ 21 ] 15 | distro: [ temurin ] 16 | # and run on both Linux and Windows 17 | os: [ ubuntu-latest ] 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | - name: Validate Gradle wrapper 25 | uses: gradle/wrapper-validation-action@v1 26 | - name: Setup JDK ${{ matrix.java }} 27 | uses: actions/setup-java@v4 28 | with: 29 | distribution: ${{ matrix.distro }} 30 | java-version: ${{ matrix.java }} 31 | - name: Make Gradle wrapper executable 32 | if: ${{ runner.os != 'Windows' }} 33 | run: chmod +x ./gradlew 34 | - name: Build 35 | run: ./gradlew build --stacktrace --parallel 36 | - name: Capture build artifacts 37 | if: ${{ runner.os == 'Linux' && matrix.java == '21' }} # Only upload artifacts built from LTS java on one OS 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: Artifacts 41 | path: build/libs/ 42 | -------------------------------------------------------------------------------- /.github/workflows/crowdin.yml: -------------------------------------------------------------------------------- 1 | # This action synchronizes translations with Crowdin: 2 | # - new source strings from en_us.json are uploaded 3 | # - new translations added in Crowdin are downloaded 4 | # - if translations were downloaded a pull request is created 5 | # 6 | # Adding a new branch: 7 | # - add branch to matrix 8 | # - manually run the action once to initialize the new branch on Crowdin 9 | # - make sure Crowdin knows about the new branch 10 | 11 | name: Crowdin 12 | 13 | # can be run manually 14 | # also automatically runs once a week (only on the default branch) 15 | on: 16 | workflow_dispatch: ~ 17 | schedule: 18 | - cron: 1 2 * * 3 # At 02:01 on Wednesday. 19 | 20 | jobs: 21 | crowdin: 22 | runs-on: ubuntu-22.04 23 | strategy: 24 | max-parallel: 1 25 | matrix: 26 | branch: ["1.17", "1.18", "1.19", "1.19.3", "1.19.4"] 27 | steps: 28 | - uses: actions/checkout@v3 29 | with: 30 | ref: ${{ matrix.branch }} 31 | - uses: crowdin/github-action@master 32 | with: 33 | upload_sources: true 34 | 35 | download_translations: true 36 | # skip_untranslated_strings: true 37 | 38 | source: src/main/resources/assets/modmenu/lang/en_us.json 39 | translation: src/main/resources/assets/modmenu/lang/%locale%.json 40 | # locale/language mappings must be added in Crowdin 41 | # see https://github.com/jackassmc/minecraft-crowdin-languages 42 | 43 | crowdin_branch_name: ${{ matrix.branch }} 44 | localization_branch_name: l10n_${{ matrix.branch }} 45 | pull_request_base_branch_name: ${{ matrix.branch }} 46 | 47 | pull_request_title: Update ${{ matrix.branch }} translations 48 | commit_message: | 49 | Update ${{ matrix.branch }} translations 50 | 51 | - Updated translations 52 | 53 | project_id: 520764 54 | token: ${{ secrets.CROWDIN_TOKEN }} 55 | skip_ref_checkout: true 56 | env: 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | previousVersion: 6 | description: 'Previous Version (Do not include v prefix, must be same as the last version tag! Example: 1.4.1)' 7 | required: true 8 | version: 9 | description: 'Version (Do not include v prefix! Example: 1.4.2)' 10 | required: true 11 | jobs: 12 | release: 13 | strategy: 14 | matrix: 15 | # Use these Java versions 16 | java: [21] 17 | # and run on both Linux and Windows 18 | os: [ubuntu-22.04] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v3 23 | with: 24 | fetch-depth: 0 25 | - name: Create version tag 26 | uses: actions/github-script@v6 27 | with: 28 | script: | 29 | github.rest.git.createRef({ 30 | owner: context.repo.owner, 31 | repo: context.repo.repo, 32 | ref: "refs/tags/v${{ github.event.inputs.version }}", 33 | sha: context.sha 34 | }) 35 | - name: Fetch tags 36 | run: git fetch --tags 37 | - name: Validate Gradle wrapper 38 | uses: gradle/wrapper-validation-action@v1 39 | - name: Setup JDK ${{ matrix.java }} 40 | uses: actions/setup-java@v3 41 | with: 42 | distribution: zulu 43 | java-version: ${{ matrix.java }} 44 | - name: Make Gradle wrapper executable 45 | if: ${{ runner.os != 'Windows' }} 46 | run: chmod +x ./gradlew 47 | - name: Build 48 | run: ./gradlew generateChangelog build publish github modrinth curseforge --stacktrace --parallel -PlastTag="v${{ github.event.inputs.previousVersion }}" -PcurrentTag="v${{ github.event.inputs.version }}" 49 | env: 50 | MAVEN_URL: ${{ secrets.MAVEN_URL }} 51 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 52 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 53 | CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }} 54 | GITHUB_TOKEN: ${{ secrets.GH_API_KEY }} 55 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} 56 | DISCORD_ANNOUNCEMENT_WEBHOOK: ${{ secrets.DISCORD_ANNOUNCEMENT_WEBHOOK }} 57 | - name: Capture build artifacts 58 | if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from LTS java on one OS 59 | uses: actions/upload-artifact@v4 60 | with: 61 | name: Artifacts 62 | path: build/libs/ 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | classes 9 | 10 | # idea 11 | out 12 | *.ipr 13 | *.iws 14 | *.iml 15 | .idea 16 | 17 | # vscode 18 | .vscode 19 | 20 | # gradle 21 | build 22 | .gradle 23 | 24 | # other 25 | eclipse 26 | run 27 | logs 28 | 29 | Thumbs.db 30 | *.psd 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 Prospector 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 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'fabric-loom' version '1.10-SNAPSHOT' 3 | } 4 | 5 | apply from: 'https://raw.githubusercontent.com/TerraformersMC/GradleScripts/2.7/ferry.gradle' 6 | 7 | dependencies { 8 | minecraft "com.mojang:minecraft:$project.minecraft_version" 9 | mappings "net.fabricmc:yarn:$project.yarn_mappings:v2" 10 | 11 | mod "fabric-loader", "net.fabricmc:fabric-loader:$project.loader_version" 12 | 13 | includeMod "fabric-api", fabricApi.module("fabric-api-base", project.fabric_version) 14 | includeMod "fabric-api", fabricApi.module("fabric-resource-loader-v0", project.fabric_version) 15 | includeMod "fabric-api", fabricApi.module("fabric-screen-api-v1", project.fabric_version) 16 | includeMod "fabric-api", fabricApi.module("fabric-key-binding-api-v1", project.fabric_version) 17 | includeMod "fabric-api", fabricApi.module("fabric-lifecycle-events-v1", project.fabric_version) 18 | 19 | includeMod "text-placeholder-api", "eu.pb4:placeholder-api:${project.text_placeholder_api_version}" 20 | 21 | compileOnly "org.quiltmc:quilt-loader:$project.quilt_loader_version" 22 | } 23 | 24 | repositories { 25 | maven { 26 | name = 'TerraformersMC' 27 | url = 'https://maven.terraformersmc.com/' 28 | } 29 | 30 | maven { 31 | name = 'Quilt' 32 | url = 'https://maven.quiltmc.org/repository/release' 33 | } 34 | 35 | maven { 36 | name = 'Nucleoid' 37 | url = 'https://maven.nucleoid.xyz/' 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2G 2 | 3 | maven_group=com.terraformersmc 4 | archive_name=modmenu 5 | 6 | minecraft_version=1.21.5-rc1 7 | yarn_mappings=1.21.5-rc1+build.2 8 | loader_version=0.16.10 9 | fabric_version=0.119.2+1.21.5 10 | text_placeholder_api_version=2.6.0+1.21.5 11 | quilt_loader_version=0.28.1-beta.1 12 | 13 | # Project Metadata 14 | project_name=Mod Menu 15 | project_url=https://modrinth.com/mod/modmenu 16 | project_logo=https://raw.githubusercontent.com/Prospector/images/master/modmenu-logo.png 17 | project_color=0x134bff 18 | # default_release_type can be stable, beta, or alpha 19 | default_release_type=stable 20 | 21 | # Modrinth Metadata 22 | modrinth_slug=modmenu 23 | modrinth_id=mOgUt4GM 24 | modrinth_game_versions=1.21.5-rc1, 1.21.5-rc2, 1.21.5 25 | modrinth_mod_loaders=fabric, quilt 26 | modrinth_required_dependencies=fabric-api, placeholder-api 27 | 28 | # CurseForge Metadata 29 | curseforge_slug=modmenu 30 | curseforge_id=308702 31 | curseforge_game_versions=1.21.5-Snapshot, 1.21.5, Fabric, Quilt 32 | curseforge_required_dependencies=fabric-api, text-placeholder-api 33 | curseforge_optional_dependencies= 34 | 35 | # Mod Loader Metadata 36 | loader_icon=https://raw.githubusercontent.com/Prospector/images/master/fabric-quilt-tiny.png 37 | loader_name=Fabric/Quilt 38 | 39 | # Discord Emotes 40 | modrinth_emote=<:modrinth:802414390510354453> 41 | github_emote=<:github:698031289223217152> 42 | curseforge_emote=<:curseforge:1078567270239981628> 43 | 44 | # Webhook Options 45 | use_project_username=false 46 | 47 | # Changelog Options 48 | # changelog_hide_unimportant_commits tries to hide Merge commits at the moment 49 | changelog_hide_unimportant_commits=true 50 | changelog_max_commit_search=200 51 | discord_webhook_changelog_line_limit=10 52 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /res/screenshot1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/res/screenshot1.jpg -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | 11 | if (JavaVersion.current().ordinal() + 1 < 17) { 12 | throw new IllegalStateException("Please run gradle with Java 17+!") 13 | } 14 | 15 | rootProject.name = "modmenu" 16 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/ModMenuModMenuCompat.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu; 2 | 3 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 4 | import com.terraformersmc.modmenu.api.ModMenuApi; 5 | import com.terraformersmc.modmenu.api.UpdateChecker; 6 | import com.terraformersmc.modmenu.gui.ModMenuOptionsScreen; 7 | import com.terraformersmc.modmenu.util.mod.fabric.FabricLoaderUpdateChecker; 8 | import com.terraformersmc.modmenu.util.mod.quilt.QuiltLoaderUpdateChecker; 9 | import net.minecraft.client.MinecraftClient; 10 | import net.minecraft.client.gui.screen.option.OptionsScreen; 11 | 12 | import java.util.Map; 13 | 14 | public class ModMenuModMenuCompat implements ModMenuApi { 15 | @Override 16 | public ConfigScreenFactory getModConfigScreenFactory() { 17 | return ModMenuOptionsScreen::new; 18 | } 19 | 20 | @Override 21 | public Map> getProvidedConfigScreenFactories() { 22 | return Map.of("minecraft", parent -> new OptionsScreen(parent, MinecraftClient.getInstance().options)); 23 | } 24 | 25 | @Override 26 | public Map getProvidedUpdateCheckers() { 27 | if (ModMenu.RUNNING_QUILT) { 28 | return Map.of("quilt_loader", new QuiltLoaderUpdateChecker()); 29 | } else { 30 | return Map.of("fabricloader", new FabricLoaderUpdateChecker()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/TextPlaceholderApiCompat.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu; 2 | 3 | import eu.pb4.placeholders.api.parsers.NodeParser; 4 | 5 | public class TextPlaceholderApiCompat { 6 | public static final NodeParser PARSER = NodeParser.builder().quickText().build(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/api/ConfigScreenFactory.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.api; 2 | 3 | import net.minecraft.client.gui.screen.Screen; 4 | 5 | @FunctionalInterface 6 | public interface ConfigScreenFactory { 7 | S create(Screen parent); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/api/ModMenuApi.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.api; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.gui.ModsScreen; 5 | import net.minecraft.client.gui.screen.Screen; 6 | import net.minecraft.text.Text; 7 | 8 | import java.util.Map; 9 | import java.util.function.Consumer; 10 | 11 | public interface ModMenuApi { 12 | /** 13 | * Used for creating a {@link Screen} instance of the Mod Menu 14 | * "Mods" screen 15 | * 16 | * @param previous The screen before opening 17 | * @return A "Mods" Screen 18 | */ 19 | static Screen createModsScreen(Screen previous) { 20 | return new ModsScreen(previous); 21 | } 22 | 23 | /** 24 | * Used for creating a {@link Text} just like what would appear 25 | * on a Mod Menu Mods button 26 | * 27 | * @return The text that would be displayed on a Mods button 28 | */ 29 | static Text createModsButtonText() { 30 | return ModMenu.createModsButtonText(true); 31 | } 32 | 33 | /** 34 | * Used to construct a new config screen instance when your mod's 35 | * configuration button is selected on the mod menu screen. The 36 | * screen instance parameter is the active mod menu screen. 37 | * 38 | * @return A factory for constructing config screen instances. 39 | */ 40 | default ConfigScreenFactory getModConfigScreenFactory() { 41 | return screen -> null; 42 | } 43 | 44 | /** 45 | * Used for mods that have their own update checking logic. 46 | * By returning your own {@link UpdateChecker} instance, you can override ModMenus built-in update checking logic. 47 | * 48 | * @return An {@link UpdateChecker} or null if ModMenu should handle update checking. 49 | */ 50 | default UpdateChecker getUpdateChecker() { 51 | return null; 52 | } 53 | 54 | /** 55 | * Used to provide config screen factories for other mods. This takes second 56 | * priority to a mod's own config screen factory provider. For example, if 57 | * mod `xyz` supplies a config screen factory, mod `abc` providing a config 58 | * screen to `xyz` will be pointless, as the one provided by `xyz` will be 59 | * used. 60 | *

61 | * This method is NOT meant to be used to add a config screen factory to 62 | * your own mod. 63 | * 64 | * @return a map of mod ids to screen factories. 65 | */ 66 | default Map> getProvidedConfigScreenFactories() { 67 | return Map.of(); 68 | } 69 | 70 | /** 71 | * Used to provide update checkers for other mods. A mod registering its own 72 | * update checker will take priority over any provided ones should both exist. 73 | * 74 | * @return a map of mod ids to update checkers. 75 | */ 76 | default Map getProvidedUpdateCheckers() { 77 | return Map.of(); 78 | } 79 | 80 | /** 81 | * Used to mark mods with a badge indicating that they are 82 | * provided by a modpack. 83 | *

84 | * Builtin mods such as `minecraft` cannot be marked as 85 | * provided by a modpack. 86 | */ 87 | default void attachModpackBadges(Consumer consumer) { 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/api/UpdateChannel.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.api; 2 | 3 | import com.terraformersmc.modmenu.config.ModMenuConfig; 4 | 5 | /** 6 | * Supported update channels, in ascending order by stability. 7 | */ 8 | public enum UpdateChannel { 9 | ALPHA, 10 | BETA, 11 | RELEASE; 12 | 13 | /** 14 | * @return the user's preferred update channel. 15 | */ 16 | public static UpdateChannel getUserPreference() { 17 | return ModMenuConfig.UPDATE_CHANNEL.getValue(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/api/UpdateChecker.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.api; 2 | 3 | public interface UpdateChecker { 4 | /** 5 | * Gets called when ModMenu is checking for updates. 6 | * This is done in a separate thread, so this call can/should be blocking. 7 | * 8 | *

Your update checker should aim to return an update on the same or a more stable channel than the user's preference which you can get via {@link UpdateChannel#getUserPreference()}.

9 | * 10 | * @return The update info 11 | */ 12 | UpdateInfo checkForUpdates(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/api/UpdateInfo.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.api; 2 | 3 | import net.minecraft.text.Text; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public interface UpdateInfo { 7 | /** 8 | * @return If an update for the mod is available. 9 | */ 10 | boolean isUpdateAvailable(); 11 | 12 | /** 13 | * @return The message that is getting displayed when an update is available or null to let ModMenu handle displaying the message. 14 | */ 15 | @Nullable 16 | default Text getUpdateMessage() { 17 | return null; 18 | } 19 | 20 | /** 21 | * @return The URL to the mod download. 22 | */ 23 | String getDownloadLink(); 24 | 25 | /** 26 | * @return The update channel this update is available for. 27 | */ 28 | UpdateChannel getUpdateChannel(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/FileOnlyConfig.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.FIELD) 10 | public @interface FileOnlyConfig { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/ModMenuConfig.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.terraformersmc.modmenu.api.UpdateChannel; 5 | import com.terraformersmc.modmenu.config.option.BooleanConfigOption; 6 | import com.terraformersmc.modmenu.config.option.EnumConfigOption; 7 | import com.terraformersmc.modmenu.config.option.OptionConvertible; 8 | import com.terraformersmc.modmenu.config.option.StringSetConfigOption; 9 | import com.terraformersmc.modmenu.util.mod.Mod; 10 | import net.minecraft.client.option.SimpleOption; 11 | 12 | import java.lang.reflect.Field; 13 | import java.lang.reflect.Modifier; 14 | import java.util.ArrayList; 15 | import java.util.Comparator; 16 | import java.util.HashSet; 17 | import java.util.Locale; 18 | 19 | public class ModMenuConfig { 20 | public static final EnumConfigOption SORTING = new EnumConfigOption<>("sorting", Sorting.ASCENDING); 21 | public static final BooleanConfigOption COUNT_LIBRARIES = new BooleanConfigOption("count_libraries", true); 22 | public static final BooleanConfigOption COMPACT_LIST = new BooleanConfigOption("compact_list", false); 23 | public static final BooleanConfigOption COUNT_CHILDREN = new BooleanConfigOption("count_children", true); 24 | public static final EnumConfigOption MODS_BUTTON_STYLE = new EnumConfigOption<>("mods_button_style", TitleMenuButtonStyle.CLASSIC); 25 | public static final EnumConfigOption GAME_MENU_BUTTON_STYLE = new EnumConfigOption<>("game_menu_button_style", GameMenuButtonStyle.REPLACE); 26 | public static final BooleanConfigOption COUNT_HIDDEN_MODS = new BooleanConfigOption("count_hidden_mods", true); 27 | public static final EnumConfigOption MOD_COUNT_LOCATION = new EnumConfigOption<>("mod_count_location", ModCountLocation.TITLE_SCREEN); 28 | public static final BooleanConfigOption HIDE_MOD_LINKS = new BooleanConfigOption("hide_mod_links", false); 29 | public static final BooleanConfigOption SHOW_LIBRARIES = new BooleanConfigOption("show_libraries", false); 30 | public static final BooleanConfigOption HIDE_MOD_LICENSE = new BooleanConfigOption("hide_mod_license", false); 31 | public static final BooleanConfigOption HIDE_BADGES = new BooleanConfigOption("hide_badges", false); 32 | public static final BooleanConfigOption HIDE_MOD_CREDITS = new BooleanConfigOption("hide_mod_credits", false); 33 | public static final BooleanConfigOption EASTER_EGGS = new BooleanConfigOption("easter_eggs", true); 34 | public static final BooleanConfigOption RANDOM_JAVA_COLORS = new BooleanConfigOption("random_java_colors", false); 35 | public static final BooleanConfigOption TRANSLATE_NAMES = new BooleanConfigOption("translate_names", true); 36 | public static final BooleanConfigOption TRANSLATE_DESCRIPTIONS = new BooleanConfigOption("translate_descriptions", true); 37 | public static final BooleanConfigOption UPDATE_CHECKER = new BooleanConfigOption("update_checker", true); 38 | public static final BooleanConfigOption BUTTON_UPDATE_BADGE = new BooleanConfigOption("button_update_badge", true); 39 | public static final EnumConfigOption UPDATE_CHANNEL = new EnumConfigOption<>("update_channel", UpdateChannel.RELEASE); 40 | public static final BooleanConfigOption QUICK_CONFIGURE = new BooleanConfigOption("quick_configure", true); 41 | 42 | @FileOnlyConfig 43 | public static final BooleanConfigOption MODIFY_TITLE_SCREEN = new BooleanConfigOption("modify_title_screen", true); 44 | @FileOnlyConfig 45 | public static final BooleanConfigOption MODIFY_GAME_MENU = new BooleanConfigOption("modify_game_menu", true); 46 | @FileOnlyConfig 47 | public static final BooleanConfigOption HIDE_CONFIG_BUTTONS = new BooleanConfigOption("hide_config_buttons", false); 48 | @FileOnlyConfig 49 | public static final BooleanConfigOption CONFIG_MODE = new BooleanConfigOption("config_mode", false); 50 | @FileOnlyConfig 51 | public static final BooleanConfigOption DISABLE_DRAG_AND_DROP = new BooleanConfigOption("disable_drag_and_drop", false); 52 | @FileOnlyConfig 53 | public static final StringSetConfigOption HIDDEN_MODS = new StringSetConfigOption("hidden_mods", new HashSet<>()); 54 | @FileOnlyConfig 55 | public static final StringSetConfigOption HIDDEN_CONFIGS = new StringSetConfigOption("hidden_configs", new HashSet<>()); 56 | @FileOnlyConfig 57 | public static final StringSetConfigOption DISABLE_UPDATE_CHECKER = new StringSetConfigOption("disable_update_checker", new HashSet<>()); 58 | 59 | public static SimpleOption[] asOptions() { 60 | ArrayList> options = new ArrayList<>(); 61 | for (Field field : ModMenuConfig.class.getDeclaredFields()) { 62 | if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && 63 | OptionConvertible.class.isAssignableFrom(field.getType()) && 64 | !field.isAnnotationPresent(FileOnlyConfig.class)) { 65 | try { 66 | options.add(((OptionConvertible) field.get(null)).asOption()); 67 | } catch (IllegalAccessException e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | } 72 | 73 | return options.toArray(SimpleOption[]::new); 74 | } 75 | 76 | public enum Sorting { 77 | ASCENDING(Comparator.comparing(mod -> mod.getTranslatedName().toLowerCase(Locale.ROOT))), 78 | DESCENDING(ASCENDING.getComparator().reversed()), 79 | HAS_UPDATE(Comparator.comparing(Mod::hasUpdate).reversed()); 80 | 81 | private final Comparator comparator; 82 | 83 | Sorting(Comparator comparator) { 84 | this.comparator = comparator; 85 | } 86 | 87 | public Comparator getComparator() { 88 | return comparator; 89 | } 90 | } 91 | 92 | public enum ModCountLocation { 93 | TITLE_SCREEN(true, false), 94 | MODS_BUTTON(false, true), 95 | TITLE_SCREEN_AND_MODS_BUTTON(true, true), 96 | NONE(false, false); 97 | 98 | private final boolean titleScreen, modsButton; 99 | 100 | ModCountLocation(boolean titleScreen, boolean modsButton) { 101 | this.titleScreen = titleScreen; 102 | this.modsButton = modsButton; 103 | } 104 | 105 | public boolean isOnTitleScreen() { 106 | return titleScreen; 107 | } 108 | 109 | public boolean isOnModsButton() { 110 | return modsButton; 111 | } 112 | } 113 | 114 | public enum TitleMenuButtonStyle { 115 | CLASSIC, 116 | REPLACE_REALMS, 117 | SHRINK, 118 | ICON 119 | } 120 | 121 | public enum GameMenuButtonStyle { 122 | @SerializedName(value = "replace", alternate = {"replace_bugs"}) REPLACE, @SerializedName(value = "insert", alternate = {"below_bugs"}) INSERT, ICON 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/ModMenuConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config; 2 | 3 | import com.google.common.collect.Sets; 4 | import com.google.gson.*; 5 | import com.terraformersmc.modmenu.ModMenu; 6 | import com.terraformersmc.modmenu.config.option.BooleanConfigOption; 7 | import com.terraformersmc.modmenu.config.option.ConfigOptionStorage; 8 | import com.terraformersmc.modmenu.config.option.EnumConfigOption; 9 | import com.terraformersmc.modmenu.config.option.StringSetConfigOption; 10 | import net.fabricmc.loader.api.FabricLoader; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.BufferedWriter; 14 | import java.io.IOException; 15 | import java.lang.reflect.Field; 16 | import java.lang.reflect.Modifier; 17 | import java.lang.reflect.ParameterizedType; 18 | import java.lang.reflect.Type; 19 | import java.nio.file.Files; 20 | import java.nio.file.Path; 21 | import java.util.Locale; 22 | import java.util.stream.Collectors; 23 | 24 | public class ModMenuConfigManager { 25 | private static Path path; 26 | 27 | private static void prepareConfigPath() { 28 | if (path == null) { 29 | path = FabricLoader.getInstance().getConfigDir().resolve(ModMenu.MOD_ID + ".json"); 30 | } 31 | } 32 | 33 | public static void initializeConfig() { 34 | load(); 35 | } 36 | 37 | @SuppressWarnings("unchecked") 38 | private static void load() { 39 | prepareConfigPath(); 40 | 41 | try { 42 | if (!Files.exists(path)) { 43 | save(); 44 | } 45 | 46 | if (Files.exists(path)) { 47 | BufferedReader br = Files.newBufferedReader(path); 48 | JsonObject json = JsonParser.parseReader(br).getAsJsonObject(); 49 | for (Field field : ModMenuConfig.class.getDeclaredFields()) { 50 | if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { 51 | if (StringSetConfigOption.class.isAssignableFrom(field.getType())) { 52 | JsonArray jsonArray = json.getAsJsonArray(field.getName().toLowerCase(Locale.ROOT)); 53 | if (jsonArray != null) { 54 | StringSetConfigOption option = (StringSetConfigOption) field.get(null); 55 | ConfigOptionStorage.setStringSet( 56 | option.getKey(), 57 | Sets.newHashSet(jsonArray) 58 | .stream() 59 | .map(JsonElement::getAsString) 60 | .collect(Collectors.toSet()) 61 | ); 62 | } 63 | } else if (BooleanConfigOption.class.isAssignableFrom(field.getType())) { 64 | JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive(field.getName().toLowerCase(Locale.ROOT)); 65 | if (jsonPrimitive != null && jsonPrimitive.isBoolean()) { 66 | BooleanConfigOption option = (BooleanConfigOption) field.get(null); 67 | ConfigOptionStorage.setBoolean(option.getKey(), jsonPrimitive.getAsBoolean()); 68 | } 69 | } else if (EnumConfigOption.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType) { 70 | JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive(field.getName().toLowerCase(Locale.ROOT)); 71 | if (jsonPrimitive != null && jsonPrimitive.isString()) { 72 | Type generic = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; 73 | if (generic instanceof Class) { 74 | EnumConfigOption option = (EnumConfigOption) field.get(null); 75 | Enum found = null; 76 | for (Enum value : ((Class>) generic).getEnumConstants()) { 77 | if (value.name().toLowerCase(Locale.ROOT).equals(jsonPrimitive.getAsString())) { 78 | found = value; 79 | break; 80 | } 81 | } 82 | 83 | if (found != null) { 84 | ConfigOptionStorage.setEnumTypeless(option.getKey(), found); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } catch (IOException | IllegalAccessException e) { 93 | System.err.println("Couldn't load Mod Menu configuration file; reverting to defaults"); 94 | e.printStackTrace(); 95 | } 96 | } 97 | 98 | @SuppressWarnings("unchecked") 99 | public static void save() { 100 | ModMenu.clearModCountCache(); 101 | prepareConfigPath(); 102 | JsonObject config = new JsonObject(); 103 | try { 104 | for (Field field : ModMenuConfig.class.getDeclaredFields()) { 105 | if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) { 106 | if (BooleanConfigOption.class.isAssignableFrom(field.getType())) { 107 | BooleanConfigOption option = (BooleanConfigOption) field.get(null); 108 | config.addProperty(field.getName().toLowerCase(Locale.ROOT), ConfigOptionStorage.getBoolean(option.getKey())); 109 | } else if (StringSetConfigOption.class.isAssignableFrom(field.getType())) { 110 | StringSetConfigOption option = (StringSetConfigOption) field.get(null); 111 | JsonArray array = new JsonArray(); 112 | ConfigOptionStorage.getStringSet(option.getKey()).forEach(array::add); 113 | config.add(field.getName().toLowerCase(Locale.ROOT), array); 114 | } else if (EnumConfigOption.class.isAssignableFrom(field.getType()) && field.getGenericType() instanceof ParameterizedType) { 115 | Type generic = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; 116 | if (generic instanceof Class) { 117 | EnumConfigOption option = (EnumConfigOption) field.get(null); 118 | config.addProperty(field.getName().toLowerCase(Locale.ROOT), ConfigOptionStorage.getEnumTypeless(option.getKey(), (Class>) generic).name().toLowerCase(Locale.ROOT)); 119 | } 120 | } 121 | } 122 | } 123 | } catch (IllegalAccessException e) { 124 | e.printStackTrace(); 125 | } 126 | 127 | String jsonString = ModMenu.GSON.toJson(config); 128 | try (BufferedWriter fileWriter = Files.newBufferedWriter(path)) { 129 | fileWriter.write(jsonString); 130 | } catch (IOException e) { 131 | System.err.println("Couldn't save Mod Menu configuration file"); 132 | e.printStackTrace(); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/option/BooleanConfigOption.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config.option; 2 | 3 | import com.terraformersmc.modmenu.util.TranslationUtil; 4 | import net.minecraft.client.option.SimpleOption; 5 | import net.minecraft.screen.ScreenTexts; 6 | import net.minecraft.text.Text; 7 | 8 | public class BooleanConfigOption implements OptionConvertible { 9 | private final String key, translationKey; 10 | private final boolean defaultValue; 11 | private final Text enabledText; 12 | private final Text disabledText; 13 | 14 | public BooleanConfigOption(String key, boolean defaultValue, String enabledKey, String disabledKey) { 15 | ConfigOptionStorage.setBoolean(key, defaultValue); 16 | this.key = key; 17 | this.translationKey = TranslationUtil.translationKeyOf("option", key); 18 | this.defaultValue = defaultValue; 19 | this.enabledText = Text.translatable(translationKey + "." + enabledKey); 20 | this.disabledText = Text.translatable(translationKey + "." + disabledKey); 21 | } 22 | 23 | public BooleanConfigOption(String key, boolean defaultValue) { 24 | this(key, defaultValue, "true", "false"); 25 | } 26 | 27 | public String getKey() { 28 | return key; 29 | } 30 | 31 | public boolean getValue() { 32 | return ConfigOptionStorage.getBoolean(key); 33 | } 34 | 35 | public void setValue(boolean value) { 36 | ConfigOptionStorage.setBoolean(key, value); 37 | } 38 | 39 | public void toggleValue() { 40 | ConfigOptionStorage.toggleBoolean(key); 41 | } 42 | 43 | public boolean getDefaultValue() { 44 | return defaultValue; 45 | } 46 | 47 | public Text getButtonText() { 48 | return ScreenTexts.composeGenericOptionText(Text.translatable(translationKey), getValue() ? enabledText : disabledText); 49 | } 50 | 51 | @Override 52 | public SimpleOption asOption() { 53 | if (enabledText != null && disabledText != null) { 54 | return new SimpleOption<>( 55 | translationKey, 56 | SimpleOption.emptyTooltip(), 57 | (text, value) -> value ? enabledText : disabledText, 58 | SimpleOption.BOOLEAN, 59 | getValue(), 60 | newValue -> ConfigOptionStorage.setBoolean(key, newValue) 61 | ); 62 | } 63 | 64 | return SimpleOption.ofBoolean( 65 | translationKey, 66 | getValue(), 67 | (value) -> ConfigOptionStorage.setBoolean(key, value) 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/option/ConfigOptionStorage.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config.option; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | public class ConfigOptionStorage { 8 | private static final Map BOOLEAN_OPTIONS = new HashMap<>(); 9 | private static final Map> ENUM_OPTIONS = new HashMap<>(); 10 | private static final Map> STRING_SET_OPTIONS = new HashMap<>(); 11 | 12 | public static void setStringSet(String key, Set value) { 13 | STRING_SET_OPTIONS.put(key, value); 14 | } 15 | 16 | public static Set getStringSet(String key) { 17 | return STRING_SET_OPTIONS.get(key); 18 | } 19 | 20 | public static void setBoolean(String key, boolean value) { 21 | BOOLEAN_OPTIONS.put(key, value); 22 | } 23 | 24 | public static void toggleBoolean(String key) { 25 | setBoolean(key, !getBoolean(key)); 26 | } 27 | 28 | public static boolean getBoolean(String key) { 29 | return BOOLEAN_OPTIONS.get(key); 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | public static > E getEnum(String key, Class typeClass) { 34 | return (E) ENUM_OPTIONS.get(key); 35 | } 36 | 37 | public static Enum getEnumTypeless(String key, Class> typeClass) { 38 | return ENUM_OPTIONS.get(key); 39 | } 40 | 41 | public static > void setEnum(String key, E value) { 42 | ENUM_OPTIONS.put(key, value); 43 | } 44 | 45 | public static void setEnumTypeless(String key, Enum value) { 46 | ENUM_OPTIONS.put(key, value); 47 | } 48 | 49 | public static > E cycleEnum(String key, Class typeClass) { 50 | return cycleEnum(key, typeClass, 1); 51 | } 52 | 53 | public static > E cycleEnum(String key, Class typeClass, int amount) { 54 | E[] values = typeClass.getEnumConstants(); 55 | E currentValue = getEnum(key, typeClass); 56 | E newValue = values[(currentValue.ordinal() + amount) % values.length]; 57 | setEnum(key, newValue); 58 | return newValue; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/option/EnumConfigOption.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config.option; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.terraformersmc.modmenu.util.TranslationUtil; 5 | import net.minecraft.client.option.SimpleOption; 6 | import net.minecraft.screen.ScreenTexts; 7 | import net.minecraft.text.Text; 8 | 9 | import java.util.Arrays; 10 | import java.util.Locale; 11 | 12 | public class EnumConfigOption> implements OptionConvertible { 13 | private final String key, translationKey; 14 | private final Class enumClass; 15 | private final E defaultValue; 16 | 17 | public EnumConfigOption(String key, E defaultValue) { 18 | ConfigOptionStorage.setEnum(key, defaultValue); 19 | this.key = key; 20 | this.translationKey = TranslationUtil.translationKeyOf("option", key); 21 | this.enumClass = defaultValue.getDeclaringClass(); 22 | this.defaultValue = defaultValue; 23 | } 24 | 25 | public String getKey() { 26 | return key; 27 | } 28 | 29 | public E getValue() { 30 | return ConfigOptionStorage.getEnum(key, enumClass); 31 | } 32 | 33 | public void setValue(E value) { 34 | ConfigOptionStorage.setEnum(key, value); 35 | } 36 | 37 | public void cycleValue() { 38 | ConfigOptionStorage.cycleEnum(key, enumClass); 39 | } 40 | 41 | public void cycleValue(int amount) { 42 | ConfigOptionStorage.cycleEnum(key, enumClass, amount); 43 | } 44 | 45 | public E getDefaultValue() { 46 | return defaultValue; 47 | } 48 | 49 | private static > Text getValueText(EnumConfigOption option, E value) { 50 | return Text.translatable(option.translationKey + "." + value.name().toLowerCase(Locale.ROOT)); 51 | } 52 | 53 | public Text getButtonText() { 54 | return ScreenTexts.composeGenericOptionText(Text.translatable(translationKey), getValueText(this, getValue())); 55 | } 56 | 57 | @Override 58 | public SimpleOption asOption() { 59 | return new SimpleOption<>(translationKey, 60 | SimpleOption.emptyTooltip(), 61 | (text, value) -> getValueText(this, value), 62 | new SimpleOption.PotentialValuesBasedCallbacks<>(Arrays.asList(enumClass.getEnumConstants()), 63 | Codec.STRING.xmap(string -> Arrays.stream(enumClass.getEnumConstants()) 64 | .filter(e -> e.name().toLowerCase().equals(string)) 65 | .findAny() 66 | .orElse(null), newValue -> newValue.name().toLowerCase()) 67 | ), 68 | getValue(), 69 | value -> ConfigOptionStorage.setEnum(key, value) 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/option/OptionConvertible.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config.option; 2 | 3 | import net.minecraft.client.option.SimpleOption; 4 | 5 | public interface OptionConvertible { 6 | SimpleOption asOption(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/config/option/StringSetConfigOption.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.config.option; 2 | 3 | import com.terraformersmc.modmenu.util.TranslationUtil; 4 | import net.minecraft.text.Text; 5 | 6 | import java.util.Set; 7 | 8 | public class StringSetConfigOption { 9 | private final String key, translationKey; 10 | private final Set defaultValue; 11 | 12 | public StringSetConfigOption(String key, Set defaultValue) { 13 | super(); 14 | ConfigOptionStorage.setStringSet(key, defaultValue); 15 | this.key = key; 16 | this.translationKey = TranslationUtil.translationKeyOf("option", key); 17 | this.defaultValue = defaultValue; 18 | } 19 | 20 | public String getKey() { 21 | return key; 22 | } 23 | 24 | public Set getValue() { 25 | return ConfigOptionStorage.getStringSet(key); 26 | } 27 | 28 | public void setValue(Set value) { 29 | ConfigOptionStorage.setStringSet(key, value); 30 | } 31 | 32 | public Text getMessage() { 33 | return Text.translatable(translationKey); 34 | } 35 | 36 | public Set getDefaultValue() { 37 | return defaultValue; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/event/ModMenuEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.event; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.api.ModMenuApi; 5 | import com.terraformersmc.modmenu.config.ModMenuConfig; 6 | import com.terraformersmc.modmenu.gui.ModsScreen; 7 | import com.terraformersmc.modmenu.gui.widget.ModMenuButtonWidget; 8 | import com.terraformersmc.modmenu.gui.widget.UpdateCheckerTexturedButtonWidget; 9 | import com.terraformersmc.modmenu.util.UpdateCheckerUtil; 10 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; 11 | import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; 12 | import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; 13 | import net.fabricmc.fabric.api.client.screen.v1.Screens; 14 | import net.minecraft.client.MinecraftClient; 15 | import net.minecraft.client.gui.screen.Screen; 16 | import net.minecraft.client.gui.screen.TitleScreen; 17 | import net.minecraft.client.gui.widget.ButtonWidget; 18 | import net.minecraft.client.gui.widget.ClickableWidget; 19 | import net.minecraft.client.gui.widget.Widget; 20 | import net.minecraft.client.option.KeyBinding; 21 | import net.minecraft.client.util.InputUtil; 22 | import net.minecraft.text.Text; 23 | import net.minecraft.text.TextContent; 24 | import net.minecraft.text.TranslatableTextContent; 25 | import net.minecraft.util.Identifier; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | public class ModMenuEventHandler { 31 | public static final Identifier MODS_BUTTON_TEXTURE = Identifier.of(ModMenu.MOD_ID, "textures/gui/mods_button.png"); 32 | private static KeyBinding MENU_KEY_BIND; 33 | 34 | public static void register() { 35 | MENU_KEY_BIND = KeyBindingHelper.registerKeyBinding(new KeyBinding( 36 | "key.modmenu.open_menu", 37 | InputUtil.Type.KEYSYM, 38 | InputUtil.UNKNOWN_KEY.getCode(), 39 | "key.categories.misc" 40 | )); 41 | ClientTickEvents.END_CLIENT_TICK.register(ModMenuEventHandler::onClientEndTick); 42 | ScreenEvents.AFTER_INIT.register(ModMenuEventHandler::afterScreenInit); 43 | } 44 | 45 | public static void afterScreenInit(MinecraftClient client, Screen screen, int scaledWidth, int scaledHeight) { 46 | if (screen instanceof TitleScreen) { 47 | afterTitleScreenInit(screen); 48 | } 49 | } 50 | 51 | private static void afterTitleScreenInit(Screen screen) { 52 | final List buttons = Screens.getButtons(screen); 53 | if (ModMenuConfig.MODIFY_TITLE_SCREEN.getValue()) { 54 | int modsButtonIndex = -1; 55 | final int spacing = 24; 56 | int buttonsY = screen.height / 4 + 48; 57 | for (int i = 0; i < buttons.size(); i++) { 58 | ClickableWidget widget = buttons.get(i); 59 | if (widget instanceof ButtonWidget button) { 60 | if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.CLASSIC) { 61 | if (button.visible) { 62 | shiftButtons(button, modsButtonIndex == -1, spacing); 63 | if (modsButtonIndex == -1) { 64 | buttonsY = button.getY(); 65 | } 66 | } 67 | } 68 | 69 | if (buttonHasText(button, "menu.online")) { 70 | if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.REPLACE_REALMS) { 71 | buttons.set(i, new ModMenuButtonWidget( 72 | button.getX(), 73 | button.getY(), 74 | button.getWidth(), 75 | button.getHeight(), 76 | ModMenuApi.createModsButtonText(), 77 | screen 78 | )); 79 | } else { 80 | if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.SHRINK) { 81 | button.setWidth(98); 82 | } 83 | 84 | modsButtonIndex = i + 1; 85 | if (button.visible) { 86 | buttonsY = button.getY(); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | if (modsButtonIndex != -1) { 94 | if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.CLASSIC) { 95 | buttons.add(modsButtonIndex, new ModMenuButtonWidget( 96 | screen.width / 2 - 100, 97 | buttonsY + spacing, 98 | 200, 99 | 20, 100 | ModMenuApi.createModsButtonText(), 101 | screen 102 | )); 103 | } else if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.SHRINK) { 104 | buttons.add(modsButtonIndex, 105 | new ModMenuButtonWidget( 106 | screen.width / 2 + 2, 107 | buttonsY, 108 | 98, 109 | 20, 110 | ModMenuApi.createModsButtonText(), 111 | screen 112 | ) 113 | ); 114 | } else if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.ICON) { 115 | buttons.add(modsButtonIndex, new UpdateCheckerTexturedButtonWidget( 116 | screen.width / 2 + 104, 117 | buttonsY, 118 | 20, 119 | 20, 120 | 0, 121 | 0, 122 | 20, 123 | MODS_BUTTON_TEXTURE, 124 | 32, 125 | 64, 126 | button -> MinecraftClient.getInstance().setScreen(new ModsScreen(screen)), 127 | ModMenuApi.createModsButtonText() 128 | )); 129 | } 130 | } 131 | } 132 | UpdateCheckerUtil.triggerV2DeprecatedToast(); 133 | } 134 | 135 | private static void onClientEndTick(MinecraftClient client) { 136 | while (MENU_KEY_BIND.wasPressed()) { 137 | client.setScreen(new ModsScreen(client.currentScreen)); 138 | } 139 | } 140 | 141 | public static boolean buttonHasText(Widget widget, String... translationKeys) { 142 | if (widget instanceof ButtonWidget button) { 143 | Text text = button.getMessage(); 144 | TextContent textContent = text.getContent(); 145 | return textContent instanceof TranslatableTextContent && Arrays.stream(translationKeys).anyMatch(s -> ((TranslatableTextContent) textContent).getKey().equals(s)); 146 | } else { 147 | return false; 148 | } 149 | } 150 | 151 | public static void shiftButtons(Widget widget, boolean shiftUp, int spacing) { 152 | if (shiftUp) { 153 | widget.setY(widget.getY() - spacing / 2); 154 | } else if (!(widget instanceof ClickableWidget button && button.getMessage().equals(Text.translatable("title.credits")))) { 155 | widget.setY(widget.getY() + spacing / 2); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/ModMenuOptionsScreen.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.config.ModMenuConfig; 5 | import com.terraformersmc.modmenu.config.ModMenuConfigManager; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.client.gui.screen.Screen; 8 | import net.minecraft.client.gui.screen.option.GameOptionsScreen; 9 | import net.minecraft.text.Text; 10 | 11 | public class ModMenuOptionsScreen extends GameOptionsScreen { 12 | public ModMenuOptionsScreen(Screen previous) { 13 | super(previous, MinecraftClient.getInstance().options, Text.translatable("modmenu.options")); 14 | } 15 | 16 | @Override 17 | protected void addOptions() { 18 | if (this.body != null) { 19 | this.body.addAll(ModMenuConfig.asOptions()); 20 | } 21 | } 22 | 23 | @Override 24 | public void removed() { 25 | ModMenuConfigManager.save(); 26 | ModMenu.checkForUpdates(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/widget/LegacyTexturedButtonWidget.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui.widget; 2 | 3 | import net.minecraft.client.gui.DrawContext; 4 | import net.minecraft.client.gui.widget.ButtonWidget; 5 | import net.minecraft.client.gui.widget.TexturedButtonWidget; 6 | import net.minecraft.client.render.RenderLayer; 7 | import net.minecraft.text.Text; 8 | import net.minecraft.util.Identifier; 9 | 10 | public class LegacyTexturedButtonWidget extends TexturedButtonWidget { 11 | private final int u; 12 | private final int v; 13 | private final int hoveredVOffset; 14 | 15 | private final Identifier texture; 16 | 17 | private final int textureWidth; 18 | private final int textureHeight; 19 | 20 | public LegacyTexturedButtonWidget( 21 | int x, 22 | int y, 23 | int width, 24 | int height, 25 | int u, 26 | int v, 27 | int hoveredVOffset, 28 | Identifier texture, 29 | int textureWidth, 30 | int textureHeight, 31 | ButtonWidget.PressAction pressAction, 32 | Text message 33 | ) { 34 | super(x, y, width, height, null, pressAction, message); 35 | 36 | this.u = u; 37 | this.v = v; 38 | this.hoveredVOffset = hoveredVOffset; 39 | 40 | this.texture = texture; 41 | 42 | this.textureWidth = textureWidth; 43 | this.textureHeight = textureHeight; 44 | } 45 | 46 | @Override 47 | public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { 48 | int v = this.v; 49 | if (!this.isNarratable()) { 50 | v += this.hoveredVOffset * 2; 51 | } else if (this.isSelected()) { 52 | v += this.hoveredVOffset; 53 | } 54 | 55 | context.drawTexture( 56 | RenderLayer::getGuiTextured, 57 | this.texture, 58 | this.getX(), 59 | this.getY(), 60 | this.u, 61 | v, 62 | this.width, 63 | this.height, 64 | this.textureWidth, 65 | this.textureHeight 66 | ); 67 | } 68 | 69 | public static Builder legacyTexturedBuilder(Text message, ButtonWidget.PressAction onPress) { 70 | return new Builder(message, onPress); 71 | } 72 | 73 | public static class Builder { 74 | private final Text message; 75 | private final ButtonWidget.PressAction onPress; 76 | 77 | private int x; 78 | private int y; 79 | 80 | private int width; 81 | private int height; 82 | 83 | private int u; 84 | private int v; 85 | private int hoveredVOffset; 86 | 87 | private Identifier texture; 88 | 89 | private int textureWidth; 90 | private int textureHeight; 91 | 92 | public Builder(Text message, PressAction onPress) { 93 | this.message = message; 94 | this.onPress = onPress; 95 | } 96 | 97 | public Builder position(int x, int y) { 98 | this.x = x; 99 | this.y = y; 100 | return this; 101 | } 102 | 103 | public Builder size(int width, int height) { 104 | this.width = width; 105 | this.height = height; 106 | return this; 107 | } 108 | 109 | public Builder uv(int u, int v, int hoveredVOffset) { 110 | this.u = u; 111 | this.v = v; 112 | this.hoveredVOffset = hoveredVOffset; 113 | return this; 114 | } 115 | 116 | public Builder texture(Identifier texture, int textureWidth, int textureHeight) { 117 | this.texture = texture; 118 | this.textureWidth = textureWidth; 119 | this.textureHeight = textureHeight; 120 | return this; 121 | } 122 | 123 | public LegacyTexturedButtonWidget build() { 124 | return new LegacyTexturedButtonWidget( 125 | this.x, 126 | this.y, 127 | this.width, 128 | this.height, 129 | this.u, 130 | this.v, 131 | this.hoveredVOffset, 132 | this.texture, 133 | this.textureWidth, 134 | this.textureHeight, 135 | this.onPress, 136 | this.message 137 | ); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/widget/ModMenuButtonWidget.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui.widget; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.config.ModMenuConfig; 5 | import com.terraformersmc.modmenu.gui.ModsScreen; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.client.gui.DrawContext; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.gui.widget.ButtonWidget; 10 | import net.minecraft.text.Text; 11 | 12 | public class ModMenuButtonWidget extends ButtonWidget { 13 | public ModMenuButtonWidget(int x, int y, int width, int height, Text text, Screen screen) { 14 | super( 15 | x, 16 | y, 17 | width, 18 | height, 19 | text, 20 | button -> MinecraftClient.getInstance().setScreen(new ModsScreen(screen)), 21 | ButtonWidget.DEFAULT_NARRATION_SUPPLIER 22 | ); 23 | } 24 | 25 | @Override 26 | public void renderWidget(DrawContext drawContext, int mouseX, int mouseY, float delta) { 27 | super.renderWidget(drawContext, mouseX, mouseY, delta); 28 | if (ModMenuConfig.BUTTON_UPDATE_BADGE.getValue() && ModMenu.areModUpdatesAvailable()) { 29 | UpdateAvailableBadge.renderBadge( 30 | drawContext, 31 | this.width + this.getX() - 16, 32 | this.height / 2 + this.getY() - 4 33 | ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateAvailableBadge.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui.widget; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import net.minecraft.client.gui.DrawContext; 5 | import net.minecraft.client.render.RenderLayer; 6 | import net.minecraft.util.Identifier; 7 | 8 | public class UpdateAvailableBadge { 9 | private static final Identifier UPDATE_ICON = Identifier.ofVanilla("icon/trial_available"); 10 | 11 | public static void renderBadge(DrawContext drawContext, int x, int y) { 12 | RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); 13 | drawContext.drawGuiTexture(RenderLayer::getGuiTextured, UPDATE_ICON, x, y, 8, 8); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/widget/UpdateCheckerTexturedButtonWidget.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui.widget; 2 | 3 | 4 | import com.terraformersmc.modmenu.ModMenu; 5 | import com.terraformersmc.modmenu.config.ModMenuConfig; 6 | import net.minecraft.client.gui.DrawContext; 7 | import net.minecraft.client.gui.widget.ButtonWidget; 8 | import net.minecraft.text.Text; 9 | import net.minecraft.util.Identifier; 10 | 11 | public class UpdateCheckerTexturedButtonWidget extends LegacyTexturedButtonWidget { 12 | public UpdateCheckerTexturedButtonWidget( 13 | int x, 14 | int y, 15 | int width, 16 | int height, 17 | int u, 18 | int v, 19 | int hoveredVOffset, 20 | Identifier texture, 21 | int textureWidth, 22 | int textureHeight, 23 | ButtonWidget.PressAction pressAction, 24 | Text message 25 | ) { 26 | super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, message); 27 | } 28 | 29 | @Override 30 | public void renderWidget(DrawContext drawContext, int mouseX, int mouseY, float delta) { 31 | super.renderWidget(drawContext, mouseX, mouseY, delta); 32 | if (ModMenuConfig.BUTTON_UPDATE_BADGE.getValue() && ModMenu.areModUpdatesAvailable()) { 33 | UpdateAvailableBadge.renderBadge(drawContext, this.getX() + this.width - 5, this.getY() - 3); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ChildEntry.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui.widget.entries; 2 | 3 | import com.terraformersmc.modmenu.gui.widget.ModListWidget; 4 | import com.terraformersmc.modmenu.util.mod.Mod; 5 | import net.minecraft.client.gui.DrawContext; 6 | import org.lwjgl.glfw.GLFW; 7 | 8 | public class ChildEntry extends ModListEntry { 9 | private final boolean bottomChild; 10 | private final ParentEntry parent; 11 | 12 | public ChildEntry(Mod mod, ParentEntry parent, ModListWidget list, boolean bottomChild) { 13 | super(mod, list); 14 | this.bottomChild = bottomChild; 15 | this.parent = parent; 16 | } 17 | 18 | @Override 19 | public void render( 20 | DrawContext drawContext, 21 | int index, 22 | int y, 23 | int x, 24 | int rowWidth, 25 | int rowHeight, 26 | int mouseX, 27 | int mouseY, 28 | boolean isSelected, 29 | float delta 30 | ) { 31 | super.render(drawContext, index, y, x, rowWidth, rowHeight, mouseX, mouseY, isSelected, delta); 32 | x += 4; 33 | int color = 0xFFA0A0A0; 34 | drawContext.fill(x, y - 2, x + 1, y + (bottomChild ? rowHeight / 2 : rowHeight + 2), color); 35 | drawContext.fill(x, y + rowHeight / 2, x + 7, y + rowHeight / 2 + 1, color); 36 | } 37 | 38 | @Override 39 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) { 40 | if (keyCode == GLFW.GLFW_KEY_LEFT) { 41 | list.setSelected(parent); 42 | list.ensureVisible(parent); 43 | return true; 44 | } else { 45 | return false; 46 | } 47 | } 48 | 49 | @Override 50 | public int getXOffset() { 51 | return 13; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/widget/entries/IndependentEntry.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui.widget.entries; 2 | 3 | import com.terraformersmc.modmenu.gui.widget.ModListWidget; 4 | import com.terraformersmc.modmenu.util.mod.Mod; 5 | 6 | public class IndependentEntry extends ModListEntry { 7 | public IndependentEntry(Mod mod, ModListWidget list) { 8 | super(mod, list); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/gui/widget/entries/ParentEntry.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.gui.widget.entries; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import com.terraformersmc.modmenu.ModMenu; 5 | import com.terraformersmc.modmenu.config.ModMenuConfig; 6 | import com.terraformersmc.modmenu.gui.widget.ModListWidget; 7 | import com.terraformersmc.modmenu.util.mod.Mod; 8 | import com.terraformersmc.modmenu.util.mod.ModSearch; 9 | import net.minecraft.client.font.TextRenderer; 10 | import net.minecraft.client.gui.DrawContext; 11 | import net.minecraft.client.render.RenderLayer; 12 | import net.minecraft.text.Text; 13 | import net.minecraft.util.Identifier; 14 | import net.minecraft.util.Util; 15 | import org.lwjgl.glfw.GLFW; 16 | 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.Objects; 20 | 21 | public class ParentEntry extends ModListEntry { 22 | private static final Identifier PARENT_MOD_TEXTURE = Identifier.of(ModMenu.MOD_ID, "textures/gui/parent_mod.png"); 23 | protected List children; 24 | protected ModListWidget list; 25 | protected boolean hoveringIcon = false; 26 | 27 | public ParentEntry(Mod parent, List children, ModListWidget list) { 28 | super(parent, list); 29 | this.children = children; 30 | this.list = list; 31 | } 32 | 33 | @Override 34 | public void render( 35 | DrawContext drawContext, 36 | int index, 37 | int y, 38 | int x, 39 | int rowWidth, 40 | int rowHeight, 41 | int mouseX, 42 | int mouseY, 43 | boolean isSelected, 44 | float delta 45 | ) { 46 | super.render(drawContext, index, y, x, rowWidth, rowHeight, mouseX, mouseY, isSelected, delta); 47 | TextRenderer font = client.textRenderer; 48 | int childrenBadgeHeight = font.fontHeight; 49 | int childrenBadgeWidth = font.fontHeight; 50 | int shownChildren = ModSearch.search(list.getParent(), list.getParent().getSearchInput(), getChildren()).size(); 51 | Text str = shownChildren == children.size() ? 52 | Text.literal(String.valueOf(shownChildren)) : 53 | Text.literal(shownChildren + "/" + children.size()); 54 | int childrenWidth = font.getWidth(str) - 1; 55 | if (childrenBadgeWidth < childrenWidth + 4) { 56 | childrenBadgeWidth = childrenWidth + 4; 57 | } 58 | int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; 59 | int childrenBadgeX = x + iconSize - childrenBadgeWidth; 60 | int childrenBadgeY = y + iconSize - childrenBadgeHeight; 61 | int childrenOutlineColor = 0xff107454; 62 | int childrenFillColor = 0xff093929; 63 | drawContext.fill( 64 | childrenBadgeX + 1, 65 | childrenBadgeY, 66 | childrenBadgeX + childrenBadgeWidth - 1, 67 | childrenBadgeY + 1, 68 | childrenOutlineColor 69 | ); 70 | drawContext.fill( 71 | childrenBadgeX, 72 | childrenBadgeY + 1, 73 | childrenBadgeX + 1, 74 | childrenBadgeY + childrenBadgeHeight - 1, 75 | childrenOutlineColor 76 | ); 77 | drawContext.fill( 78 | childrenBadgeX + childrenBadgeWidth - 1, 79 | childrenBadgeY + 1, 80 | childrenBadgeX + childrenBadgeWidth, 81 | childrenBadgeY + childrenBadgeHeight - 1, 82 | childrenOutlineColor 83 | ); 84 | drawContext.fill( 85 | childrenBadgeX + 1, 86 | childrenBadgeY + 1, 87 | childrenBadgeX + childrenBadgeWidth - 1, 88 | childrenBadgeY + childrenBadgeHeight - 1, 89 | childrenFillColor 90 | ); 91 | drawContext.fill( 92 | childrenBadgeX + 1, 93 | childrenBadgeY + childrenBadgeHeight - 1, 94 | childrenBadgeX + childrenBadgeWidth - 1, 95 | childrenBadgeY + childrenBadgeHeight, 96 | childrenOutlineColor 97 | ); 98 | drawContext.drawText( 99 | font, 100 | str.asOrderedText(), 101 | (int) (childrenBadgeX + (float) childrenBadgeWidth / 2 - (float) childrenWidth / 2), 102 | childrenBadgeY + 1, 103 | 0xCACACA, 104 | false 105 | ); 106 | 107 | this.hoveringIcon = mouseX >= x - 1 && mouseX <= x - 1 + iconSize && mouseY >= y - 1 && mouseY <= y - 1 + iconSize; 108 | if (isMouseOver(mouseX, mouseY)) { 109 | drawContext.fill(x, y, x + iconSize, y + iconSize, 0xA0909090); 110 | int xOffset = list.getParent().showModChildren.contains(getMod().getId()) ? iconSize : 0; 111 | int yOffset = hoveringIcon ? iconSize : 0; 112 | RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); 113 | drawContext.drawTexture( 114 | RenderLayer::getGuiTextured, 115 | PARENT_MOD_TEXTURE, 116 | x, 117 | y, 118 | xOffset, 119 | yOffset, 120 | iconSize + xOffset, 121 | iconSize + yOffset, 122 | ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256, 123 | ModMenuConfig.COMPACT_LIST.getValue() ? (int) (256 / (FULL_ICON_SIZE / (double) COMPACT_ICON_SIZE)) : 256 124 | ); 125 | } 126 | } 127 | 128 | @Override 129 | public boolean mouseClicked(double mouseX, double mouseY, int i) { 130 | int iconSize = ModMenuConfig.COMPACT_LIST.getValue() ? COMPACT_ICON_SIZE : FULL_ICON_SIZE; 131 | boolean quickConfigure = ModMenuConfig.QUICK_CONFIGURE.getValue(); 132 | if (mouseX - list.getRowLeft() <= iconSize) { 133 | this.toggleChildren(); 134 | return true; 135 | } else if (!quickConfigure && Util.getMeasuringTimeMs() - this.sinceLastClick < 250) { 136 | this.toggleChildren(); 137 | return true; 138 | } else { 139 | return super.mouseClicked(mouseX, mouseY, i); 140 | } 141 | } 142 | 143 | private void toggleChildren() { 144 | String id = getMod().getId(); 145 | if (list.getParent().showModChildren.contains(id)) { 146 | list.getParent().showModChildren.remove(id); 147 | } else { 148 | list.getParent().showModChildren.add(id); 149 | } 150 | 151 | list.filter(list.getParent().getSearchInput(), false); 152 | } 153 | 154 | @Override 155 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) { 156 | String modId = getMod().getId(); 157 | if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_SPACE) { 158 | if (list.getParent().showModChildren.contains(modId)) { 159 | list.getParent().showModChildren.remove(modId); 160 | } else { 161 | list.getParent().showModChildren.add(modId); 162 | } 163 | 164 | list.filter(list.getParent().getSearchInput(), false); 165 | return true; 166 | } else if (keyCode == GLFW.GLFW_KEY_LEFT) { 167 | if (list.getParent().showModChildren.contains(modId)) { 168 | list.getParent().showModChildren.remove(modId); 169 | list.filter(list.getParent().getSearchInput(), false); 170 | } 171 | 172 | return true; 173 | } else if (keyCode == GLFW.GLFW_KEY_RIGHT) { 174 | if (!list.getParent().showModChildren.contains(modId)) { 175 | list.getParent().showModChildren.add(modId); 176 | list.filter(list.getParent().getSearchInput(), false); 177 | return true; 178 | } else { 179 | return list.keyPressed(GLFW.GLFW_KEY_DOWN, 0, 0); 180 | } 181 | } 182 | 183 | return super.keyPressed(keyCode, scanCode, modifiers); 184 | } 185 | 186 | public void setChildren(List children) { 187 | this.children = children; 188 | } 189 | 190 | public void addChildren(List children) { 191 | this.children.addAll(children); 192 | } 193 | 194 | public void addChildren(Mod... children) { 195 | this.children.addAll(Arrays.asList(children)); 196 | } 197 | 198 | public List getChildren() { 199 | return children; 200 | } 201 | 202 | @Override 203 | public boolean isMouseOver(double double_1, double double_2) { 204 | return Objects.equals(this.list.getEntryAtPos(double_1, double_2), this); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/mixin/AccessorGridWidget.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.mixin; 2 | 3 | import net.minecraft.client.gui.widget.GridWidget; 4 | import net.minecraft.client.gui.widget.Widget; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import java.util.List; 9 | 10 | @Mixin(GridWidget.class) 11 | public interface AccessorGridWidget { 12 | @Accessor 13 | List getChildren(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/mixin/MixinGameMenu.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.mixin; 2 | 3 | import com.terraformersmc.modmenu.api.ModMenuApi; 4 | import com.terraformersmc.modmenu.config.ModMenuConfig; 5 | import com.terraformersmc.modmenu.event.ModMenuEventHandler; 6 | import com.terraformersmc.modmenu.gui.ModsScreen; 7 | import com.terraformersmc.modmenu.gui.widget.ModMenuButtonWidget; 8 | import com.terraformersmc.modmenu.gui.widget.UpdateCheckerTexturedButtonWidget; 9 | import net.minecraft.client.MinecraftClient; 10 | import net.minecraft.client.gui.screen.GameMenuScreen; 11 | import net.minecraft.client.gui.screen.Screen; 12 | import net.minecraft.client.gui.widget.ClickableWidget; 13 | import net.minecraft.client.gui.widget.GridWidget; 14 | import net.minecraft.client.gui.widget.Widget; 15 | import net.minecraft.text.Text; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 21 | 22 | import java.util.List; 23 | 24 | @Mixin(GameMenuScreen.class) 25 | public abstract class MixinGameMenu extends Screen { 26 | protected MixinGameMenu(Text title) { 27 | super(title); 28 | } 29 | 30 | @Inject(method = "initWidgets", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/GridWidget;forEachChild(Ljava/util/function/Consumer;)V"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) 31 | private void onInitWidgets(CallbackInfo ci, GridWidget gridWidget) { 32 | if (gridWidget != null) { 33 | final List buttons = ((AccessorGridWidget) gridWidget).getChildren(); 34 | if (ModMenuConfig.MODIFY_GAME_MENU.getValue()) { 35 | int modsButtonIndex = -1; 36 | final int spacing = 24; 37 | int buttonsY = this.height / 4 + 8; 38 | ModMenuConfig.GameMenuButtonStyle style = ModMenuConfig.GAME_MENU_BUTTON_STYLE.getValue(); 39 | int vanillaButtonsY = this.height / 4 + 72 - 16 + 1; 40 | final int fullWidthButton = 204; 41 | for (int i = 0; i < buttons.size(); i++) { 42 | Widget widget = buttons.get(i); 43 | if (style == ModMenuConfig.GameMenuButtonStyle.INSERT) { 44 | if (!(widget instanceof ClickableWidget button) || button.visible) { 45 | ModMenuEventHandler.shiftButtons(widget, modsButtonIndex == -1 || ModMenuEventHandler.buttonHasText(widget, "menu.reportBugs", "menu.server_links"), spacing); 46 | if (modsButtonIndex == -1) { 47 | buttonsY = widget.getY(); 48 | } 49 | } 50 | } 51 | 52 | boolean isShortFeedback = ModMenuEventHandler.buttonHasText(widget, "menu.feedback"); 53 | boolean isLongFeedback = ModMenuEventHandler.buttonHasText(widget, "menu.sendFeedback"); 54 | if (isShortFeedback || isLongFeedback) { 55 | modsButtonIndex = i + 1; 56 | vanillaButtonsY = widget.getY(); 57 | if (style == ModMenuConfig.GameMenuButtonStyle.REPLACE) { 58 | buttons.set(i, new ModMenuButtonWidget( 59 | widget.getX(), 60 | widget.getY(), 61 | isShortFeedback ? widget.getWidth() : fullWidthButton, 62 | widget.getHeight(), 63 | ModMenuApi.createModsButtonText(), 64 | this 65 | )); 66 | buttons.stream() 67 | .filter(w -> ModMenuEventHandler.buttonHasText(w, "menu.reportBugs")) 68 | .forEach(w -> { 69 | if (w instanceof ClickableWidget cw) { 70 | cw.visible = false; 71 | cw.active = false; 72 | } 73 | }); 74 | } else { 75 | modsButtonIndex = i + 1; 76 | if (!(widget instanceof ClickableWidget button) || button.visible) { 77 | buttonsY = widget.getY(); 78 | } 79 | } 80 | } 81 | } 82 | 83 | if (modsButtonIndex != -1) { 84 | if (style == ModMenuConfig.GameMenuButtonStyle.INSERT) { 85 | buttons.add(modsButtonIndex, new ModMenuButtonWidget( 86 | this.width / 2 - 102, 87 | buttonsY + spacing, 88 | fullWidthButton, 89 | 20, 90 | ModMenuApi.createModsButtonText(), 91 | this 92 | )); 93 | } else if (style == ModMenuConfig.GameMenuButtonStyle.ICON) { 94 | buttons.add(modsButtonIndex, new UpdateCheckerTexturedButtonWidget( 95 | this.width / 2 + 4 + 100 + 2, 96 | vanillaButtonsY, 97 | 20, 98 | 20, 99 | 0, 100 | 0, 101 | 20, 102 | ModMenuEventHandler.MODS_BUTTON_TEXTURE, 103 | 32, 104 | 64, 105 | button -> MinecraftClient.getInstance().setScreen(new ModsScreen(this)), 106 | ModMenuApi.createModsButtonText() 107 | )); 108 | } 109 | } 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/mixin/MixinTitleScreen.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.mixin; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.config.ModMenuConfig; 5 | import net.minecraft.client.gui.screen.TitleScreen; 6 | import net.minecraft.client.resource.language.I18n; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.ModifyArg; 10 | 11 | @Mixin(TitleScreen.class) 12 | public class MixinTitleScreen { 13 | @ModifyArg(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/realms/gui/screen/RealmsNotificationsScreen;init(Lnet/minecraft/client/MinecraftClient;II)V"), method = "init", index = 2) 14 | private int adjustRealmsHeight(int height) { 15 | if (ModMenuConfig.MODIFY_TITLE_SCREEN.getValue() && ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.CLASSIC) { 16 | return height - 51; 17 | } else if (ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.REPLACE_REALMS || ModMenuConfig.MODS_BUTTON_STYLE.getValue() == ModMenuConfig.TitleMenuButtonStyle.SHRINK) { 18 | return -99999; 19 | } else { 20 | return height; 21 | } 22 | } 23 | 24 | @ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Ljava/lang/String;III)I", ordinal = 0)) 25 | private String onRender(String string) { 26 | if (ModMenuConfig.MODIFY_TITLE_SCREEN.getValue() && ModMenuConfig.MOD_COUNT_LOCATION.getValue().isOnTitleScreen()) { 27 | String count = ModMenu.getDisplayedModCount(); 28 | String specificKey = "modmenu.mods." + count; 29 | String replacementKey = I18n.hasTranslation(specificKey) ? specificKey : "modmenu.mods.n"; 30 | if (ModMenuConfig.EASTER_EGGS.getValue() && I18n.hasTranslation(specificKey + ".secret")) { 31 | replacementKey = specificKey + ".secret"; 32 | } 33 | 34 | return string.replace(I18n.translate(I18n.translate("menu.modded")), I18n.translate(replacementKey, count)); 35 | } else { 36 | return string; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/DrawingUtil.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import com.terraformersmc.modmenu.config.ModMenuConfig; 5 | import com.terraformersmc.modmenu.util.mod.Mod; 6 | import net.fabricmc.api.EnvType; 7 | import net.fabricmc.api.Environment; 8 | import net.minecraft.client.MinecraftClient; 9 | import net.minecraft.client.gui.DrawContext; 10 | import net.minecraft.text.OrderedText; 11 | import net.minecraft.text.StringVisitable; 12 | import net.minecraft.text.Style; 13 | import net.minecraft.text.Text; 14 | import net.minecraft.util.Language; 15 | import net.minecraft.util.math.MathHelper; 16 | 17 | import java.util.List; 18 | import java.util.Random; 19 | 20 | @Environment(EnvType.CLIENT) 21 | public class DrawingUtil { 22 | private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); 23 | 24 | public static void drawRandomVersionBackground( 25 | Mod mod, 26 | DrawContext drawContext, 27 | int x, 28 | int y, 29 | int width, 30 | int height 31 | ) { 32 | int seed = mod.getName().hashCode() + mod.getVersion().hashCode(); 33 | 34 | Random random = new Random(seed); 35 | int color = 0xFF000000 | MathHelper.hsvToRgb(random.nextFloat(1f), random.nextFloat(0.7f, 0.8f), 0.9f); 36 | if (!ModMenuConfig.RANDOM_JAVA_COLORS.getValue()) { 37 | color = 0xFFDD5656; 38 | } 39 | 40 | RenderSystem.setShaderColor(1f, 1f, 1f, 1f); 41 | drawContext.fill(x, y, x + width, y + height, color); 42 | } 43 | 44 | public static void drawWrappedString( 45 | DrawContext drawContext, 46 | String string, 47 | int x, 48 | int y, 49 | int wrapWidth, 50 | int lines, 51 | int color 52 | ) { 53 | while (string != null && string.endsWith("\n")) { 54 | string = string.substring(0, string.length() - 1); 55 | } 56 | 57 | List strings = CLIENT.textRenderer.getTextHandler().wrapLines(Text.literal(string), wrapWidth, Style.EMPTY); 58 | for (int i = 0; i < strings.size(); i++) { 59 | if (i >= lines) { 60 | break; 61 | } 62 | 63 | StringVisitable renderable = strings.get(i); 64 | if (i == lines - 1 && strings.size() > lines) { 65 | renderable = StringVisitable.concat(strings.get(i), StringVisitable.plain("...")); 66 | } 67 | 68 | OrderedText line = Language.getInstance().reorder(renderable); 69 | int x1 = x; 70 | if (CLIENT.textRenderer.isRightToLeft()) { 71 | x1 += wrapWidth - CLIENT.textRenderer.getWidth(line); 72 | } 73 | 74 | drawContext.drawText(CLIENT.textRenderer, line, x1, y + i * CLIENT.textRenderer.fontHeight, color, true); 75 | } 76 | } 77 | 78 | public static void drawBadge( 79 | DrawContext drawContext, 80 | int x, 81 | int y, 82 | int tagWidth, 83 | OrderedText text, 84 | int outlineColor, 85 | int fillColor, 86 | int textColor 87 | ) { 88 | drawContext.fill(x + 1, y - 1, x + tagWidth, y, outlineColor); 89 | drawContext.fill(x, y, x + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); 90 | drawContext.fill(x + 1, 91 | y + 1 + CLIENT.textRenderer.fontHeight - 1, 92 | x + tagWidth, 93 | y + CLIENT.textRenderer.fontHeight + 1, 94 | outlineColor 95 | ); 96 | drawContext.fill(x + tagWidth, y, x + tagWidth + 1, y + CLIENT.textRenderer.fontHeight, outlineColor); 97 | drawContext.fill(x + 1, y, x + tagWidth, y + CLIENT.textRenderer.fontHeight, fillColor); 98 | drawContext.drawText(CLIENT.textRenderer, 99 | text, 100 | (int) (x + 1 + (tagWidth - CLIENT.textRenderer.getWidth(text)) / (float) 2), 101 | y + 1, 102 | textColor, 103 | false 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/EnumToLowerCaseJsonConverter.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import com.google.gson.*; 4 | 5 | import java.lang.reflect.Type; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public final class EnumToLowerCaseJsonConverter implements JsonSerializer>, JsonDeserializer> { 10 | private static final Map>> TYPE_CACHE = new HashMap<>(); 11 | 12 | @Override 13 | public JsonElement serialize(final Enum src, final Type typeOfSrc, final JsonSerializationContext context) { 14 | if (src == null) { 15 | return JsonNull.INSTANCE; 16 | } 17 | 18 | return new JsonPrimitive(src.name().toLowerCase()); 19 | } 20 | 21 | @SuppressWarnings("unchecked") 22 | @Override 23 | public Enum deserialize( 24 | final JsonElement json, 25 | final Type type, 26 | final JsonDeserializationContext context 27 | ) throws JsonParseException { 28 | if (json == null || json.isJsonNull()) { 29 | return null; 30 | } 31 | 32 | if (!json.isJsonPrimitive() || !json.getAsJsonPrimitive().isString()) { 33 | throw new JsonParseException("Expecting a String JsonPrimitive, getting " + json); 34 | } 35 | 36 | try { 37 | final String enumClassName = type.getTypeName(); 38 | Class> enumClass = TYPE_CACHE.get(enumClassName); 39 | if (enumClass == null) { 40 | enumClass = (Class>) Class.forName(enumClassName); 41 | TYPE_CACHE.put(enumClassName, enumClass); 42 | } 43 | 44 | return Enum.valueOf((Class) enumClass, json.getAsString().toUpperCase()); 45 | } catch (final ClassNotFoundException e) { 46 | throw new JsonParseException(e); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import net.fabricmc.loader.api.FabricLoader; 5 | import net.minecraft.SharedConstants; 6 | 7 | import java.io.IOException; 8 | import java.net.http.HttpClient; 9 | import java.net.http.HttpRequest; 10 | import java.net.http.HttpResponse; 11 | 12 | public class HttpUtil { 13 | private static final String USER_AGENT = buildUserAgent(); 14 | private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient(); 15 | 16 | private HttpUtil() { 17 | } 18 | 19 | public static HttpResponse request( 20 | HttpRequest.Builder builder, 21 | HttpResponse.BodyHandler handler 22 | ) throws IOException, InterruptedException { 23 | builder.setHeader("User-Agent", USER_AGENT); 24 | return HTTP_CLIENT.send(builder.build(), handler); 25 | } 26 | 27 | private static String buildUserAgent() { 28 | String env = ModMenu.DEV_ENVIRONMENT ? "/development" : ""; 29 | String loader = ModMenu.RUNNING_QUILT ? "quilt" : "fabric"; 30 | 31 | var modMenuVersion = getModMenuVersion(); 32 | var minecraftVersion = SharedConstants.getGameVersion().getName(); 33 | 34 | // -> TerraformersMC/ModMenu/9.1.0 (1.20.3/quilt/development) 35 | return "%s/%s (%s/%s%s)".formatted(ModMenu.GITHUB_REF, modMenuVersion, minecraftVersion, loader, env); 36 | } 37 | 38 | private static String getModMenuVersion() { 39 | var container = FabricLoader.getInstance().getModContainer(ModMenu.MOD_ID); 40 | if (container.isEmpty()) { 41 | throw new RuntimeException("Unable to find Modmenu's own mod container!"); 42 | } 43 | 44 | return VersionUtil.removeBuildMetadata(container.get().getMetadata().getVersion().getFriendlyString()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonPrimitive; 5 | 6 | import java.util.Optional; 7 | 8 | public class JsonUtil { 9 | private JsonUtil() { 10 | } 11 | 12 | public static Optional getString(JsonObject parent, String field) { 13 | if (!parent.has(field)) { 14 | return Optional.empty(); 15 | } 16 | 17 | var value = parent.get(field); 18 | if (!value.isJsonPrimitive() || !((JsonPrimitive) value).isString()) { 19 | return Optional.empty(); 20 | } 21 | 22 | return Optional.of(value.getAsString()); 23 | } 24 | 25 | public static Optional getBoolean(JsonObject parent, String field) { 26 | if (!parent.has(field)) { 27 | return Optional.empty(); 28 | } 29 | 30 | var value = parent.get(field); 31 | if (!value.isJsonPrimitive() || !((JsonPrimitive) value).isBoolean()) { 32 | return Optional.empty(); 33 | } 34 | 35 | return Optional.of(value.getAsBoolean()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/ModMenuScreenTexts.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import net.minecraft.screen.ScreenTexts; 4 | import net.minecraft.text.Text; 5 | import net.minecraft.util.Formatting; 6 | 7 | public final class ModMenuScreenTexts { 8 | public static final Text CONFIGURE = Text.translatable("modmenu.configure"); 9 | public static final Text DROP_CONFIRM = Text.translatable("modmenu.dropConfirm"); 10 | public static final Text DROP_INFO_LINE_1 = Text.translatable("modmenu.dropInfo.line1"); 11 | public static final Text DROP_INFO_LINE_2 = Text.translatable("modmenu.dropInfo.line2"); 12 | public static final Text DROP_SUCCESSFUL_LINE_1 = Text.translatable("modmenu.dropSuccessful.line1"); 13 | public static final Text DROP_SUCCESSFUL_LINE_2 = Text.translatable("modmenu.dropSuccessful.line2"); 14 | public static final Text ISSUES = Text.translatable("modmenu.issues"); 15 | public static final Text MODS_FOLDER = Text.translatable("modmenu.modsFolder"); 16 | public static final Text SEARCH = Text.translatable("modmenu.search"); 17 | public static final Text TITLE = Text.translatable("modmenu.title"); 18 | public static final Text TOGGLE_FILTER_OPTIONS = Text.translatable("modmenu.toggleFilterOptions"); 19 | public static final Text WEBSITE = Text.translatable("modmenu.website"); 20 | 21 | private ModMenuScreenTexts() { 22 | } 23 | 24 | public static Text modIdTooltip(String modId) { 25 | return Text.translatable("modmenu.modIdToolTip", modId); 26 | } 27 | 28 | public static Text configureError(String modId, Throwable e) { 29 | return Text.translatable("modmenu.configure.error", modId, modId) 30 | .append(ScreenTexts.LINE_BREAK) 31 | .append(ScreenTexts.LINE_BREAK) 32 | .append(e.toString()) 33 | .formatted(Formatting.RED); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/OptionalUtil.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import java.util.Optional; 4 | 5 | public class OptionalUtil { 6 | public static boolean isPresentAndTrue(Optional optional) { 7 | return optional.isPresent() && optional.get(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/TranslationUtil.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import net.minecraft.client.resource.language.I18n; 5 | import net.minecraft.text.Text; 6 | 7 | import java.text.NumberFormat; 8 | import java.util.Arrays; 9 | 10 | public class TranslationUtil { 11 | public static Text translateNumeric(String key, int[]... args) { 12 | Object[] realArgs = new Object[args.length]; 13 | for (int i = 0; i < args.length; i++) { 14 | NumberFormat nf = NumberFormat.getInstance(); 15 | if (args[i].length == 1) { 16 | realArgs[i] = nf.format(args[i][0]); 17 | } else { 18 | assert args[i].length == 2; 19 | realArgs[i] = nf.format(args[i][0]) + "/" + nf.format(args[i][1]); 20 | } 21 | } 22 | 23 | int[] override = new int[args.length]; 24 | Arrays.fill(override, -1); 25 | for (int i = 0; i < args.length; i++) { 26 | int[] arg = args[i]; 27 | if (arg == null) { 28 | throw new NullPointerException("args[" + i + "]"); 29 | } 30 | if (arg.length == 1) { 31 | override[i] = arg[0]; 32 | } 33 | } 34 | 35 | String lastKey = key; 36 | for (int flags = (1 << args.length) - 1; flags >= 0; flags--) { 37 | StringBuilder fullKey = new StringBuilder(key); 38 | for (int i = 0; i < args.length; i++) { 39 | fullKey.append('.'); 40 | if (((flags & (1 << i)) != 0) && override[i] != -1) { 41 | fullKey.append(override[i]); 42 | } else { 43 | fullKey.append('n'); 44 | } 45 | } 46 | lastKey = fullKey.toString(); 47 | if (I18n.hasTranslation(lastKey)) { 48 | return Text.translatable(lastKey, realArgs); 49 | } 50 | } 51 | return Text.translatable(lastKey, realArgs); 52 | } 53 | 54 | public static String translationKeyOf(String type, String id) { 55 | return type + "." + ModMenu.MOD_ID + "." + id; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/UpdateCheckerThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.concurrent.ThreadFactory; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | public class UpdateCheckerThreadFactory implements ThreadFactory { 9 | static final AtomicInteger COUNT = new AtomicInteger(-1); 10 | 11 | @Override 12 | public Thread newThread(@NotNull Runnable r) { 13 | return Thread.ofVirtual().name("ModMenu/Update Checker/%s".formatted(COUNT.incrementAndGet())).unstarted(r); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/VersionUtil.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util; 2 | 3 | import java.util.List; 4 | 5 | public final class VersionUtil { 6 | private static final List PREFIXES = List.of("version", "ver", "v"); 7 | 8 | private VersionUtil() { } 9 | 10 | public static String stripPrefix(String version) { 11 | version = version.trim(); 12 | 13 | for (String prefix : PREFIXES) { 14 | if (version.startsWith(prefix)) { 15 | return version.substring(prefix.length()); 16 | } 17 | } 18 | 19 | return version; 20 | } 21 | 22 | public static String getPrefixedVersion(String version) { 23 | return "v" + stripPrefix(version); 24 | } 25 | 26 | public static String removeBuildMetadata(String version) { 27 | return version.split("\\+")[0]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/Mod.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.TextPlaceholderApiCompat; 5 | import com.terraformersmc.modmenu.api.UpdateChecker; 6 | import com.terraformersmc.modmenu.api.UpdateInfo; 7 | import com.terraformersmc.modmenu.config.ModMenuConfig; 8 | import com.terraformersmc.modmenu.util.mod.fabric.FabricIconHandler; 9 | import eu.pb4.placeholders.api.ParserContext; 10 | import net.minecraft.client.resource.language.I18n; 11 | import net.minecraft.client.texture.NativeImageBackedTexture; 12 | import net.minecraft.text.Text; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | import java.io.IOException; 17 | import java.util.*; 18 | import java.util.stream.Collectors; 19 | 20 | public interface Mod { 21 | @NotNull String getId(); 22 | 23 | @NotNull String getName(); 24 | 25 | @NotNull 26 | default String getTranslatedName() { 27 | String translationKey = "modmenu.nameTranslation." + getId(); 28 | if ((getId().equals("minecraft") || getId().equals("java") || ModMenuConfig.TRANSLATE_NAMES.getValue()) && I18n.hasTranslation( 29 | translationKey)) { 30 | return I18n.translate(translationKey); 31 | } else { 32 | return getName(); 33 | } 34 | } 35 | 36 | @NotNull NativeImageBackedTexture getIcon(FabricIconHandler iconHandler, int i); 37 | 38 | @NotNull 39 | default String getSummary() { 40 | String string = getTranslatedSummary(); 41 | return ModMenu.TEXT_PLACEHOLDER_COMPAT ? 42 | TextPlaceholderApiCompat.PARSER.parseText(string, ParserContext.of()).getString() : 43 | string; 44 | } 45 | 46 | @NotNull 47 | default String getTranslatedSummary() { 48 | String translationKey = "modmenu.summaryTranslation." + getId(); 49 | if ((getId().equals("minecraft") || getId().equals("java") || ModMenuConfig.TRANSLATE_DESCRIPTIONS.getValue()) && I18n.hasTranslation( 50 | translationKey)) { 51 | return I18n.translate(translationKey); 52 | } else { 53 | return getTranslatedDescription(); 54 | } 55 | } 56 | 57 | @NotNull String getDescription(); 58 | 59 | @NotNull 60 | default String getTranslatedDescription() { 61 | String translatableDescriptionKey = "modmenu.descriptionTranslation." + getId(); 62 | if ((getId().equals("minecraft") || getId().equals("java") || ModMenuConfig.TRANSLATE_DESCRIPTIONS.getValue()) && I18n.hasTranslation( 63 | translatableDescriptionKey)) { 64 | return I18n.translate(translatableDescriptionKey); 65 | } else { 66 | return getDescription(); 67 | } 68 | } 69 | 70 | default Text getFormattedDescription() { 71 | String string = getTranslatedDescription(); 72 | return ModMenu.TEXT_PLACEHOLDER_COMPAT ? 73 | TextPlaceholderApiCompat.PARSER.parseText(string, ParserContext.of()) : 74 | Text.literal(string); 75 | } 76 | 77 | @NotNull String getVersion(); 78 | 79 | @NotNull String getPrefixedVersion(); 80 | 81 | @NotNull List getAuthors(); 82 | 83 | /** 84 | * @return a mapping of contributors to their roles. 85 | */ 86 | @NotNull Map> getContributors(); 87 | 88 | /** 89 | * @return a mapping of roles to each contributor with that role. 90 | */ 91 | @NotNull SortedMap> getCredits(); 92 | 93 | @NotNull Set getBadges(); 94 | 95 | @Nullable String getWebsite(); 96 | 97 | @Nullable String getIssueTracker(); 98 | 99 | @Nullable String getSource(); 100 | 101 | @Nullable String getParent(); 102 | 103 | @NotNull Set getLicense(); 104 | 105 | @NotNull Map getLinks(); 106 | 107 | boolean isReal(); 108 | 109 | boolean allowsUpdateChecks(); 110 | 111 | @Nullable UpdateChecker getUpdateChecker(); 112 | 113 | void setUpdateChecker(@Nullable UpdateChecker updateChecker); 114 | 115 | @Nullable UpdateInfo getUpdateInfo(); 116 | 117 | void setUpdateInfo(@Nullable UpdateInfo updateInfo); 118 | 119 | default boolean hasUpdate() { 120 | UpdateInfo updateInfo = this.getUpdateInfo(); 121 | if (updateInfo == null) { 122 | return false; 123 | } else { 124 | return updateInfo.isUpdateAvailable() && updateInfo.getUpdateChannel().compareTo(ModMenuConfig.UPDATE_CHANNEL.getValue()) >= 0; 125 | } 126 | } 127 | 128 | default @Nullable String getSha512Hash() throws IOException { 129 | return null; 130 | } 131 | 132 | void setChildHasUpdate(); 133 | 134 | boolean getChildHasUpdate(); 135 | 136 | boolean isHidden(); 137 | 138 | enum Badge { 139 | LIBRARY( 140 | "modmenu.badge.library", 141 | 0xff107454, 142 | 0xff093929, 143 | "library" 144 | ), 145 | CLIENT( 146 | "modmenu.badge.clientsideOnly", 147 | 0xff2b4b7c, 148 | 0xff0e2a55, 149 | null 150 | ), 151 | DEPRECATED( 152 | "modmenu.badge.deprecated", 153 | 0xff841426, 154 | 0xff530C17, 155 | "deprecated" 156 | ), 157 | PATCHWORK_FORGE( 158 | "modmenu.badge.forge", 159 | 0xff1f2d42, 160 | 0xff101721, 161 | null 162 | ), 163 | MODPACK( 164 | "modmenu.badge.modpack", 165 | 0xff7a2b7c, 166 | 0xff510d54, 167 | null 168 | ), 169 | MINECRAFT( 170 | "modmenu.badge.minecraft", 171 | 0xff6f6c6a, 172 | 0xff31302f, 173 | null 174 | ); 175 | 176 | private final Text text; 177 | private final int outlineColor, fillColor; 178 | private final String key; 179 | private static final Map KEY_MAP = new HashMap<>(); 180 | 181 | Badge(String translationKey, int outlineColor, int fillColor, String key) { 182 | this.text = Text.translatable(translationKey); 183 | this.outlineColor = outlineColor; 184 | this.fillColor = fillColor; 185 | this.key = key; 186 | } 187 | 188 | public Text getText() { 189 | return this.text; 190 | } 191 | 192 | public int getOutlineColor() { 193 | return this.outlineColor; 194 | } 195 | 196 | public int getFillColor() { 197 | return this.fillColor; 198 | } 199 | 200 | public static Set convert(Set badgeKeys, String modId) { 201 | return badgeKeys.stream().map(key -> { 202 | if (!KEY_MAP.containsKey(key)) { 203 | ModMenu.LOGGER.warn("Skipping unknown badge key '{}' specified by mod '{}'", key, modId); 204 | } 205 | 206 | return KEY_MAP.get(key); 207 | }).filter(Objects::nonNull).collect(Collectors.toSet()); 208 | } 209 | 210 | static { 211 | Arrays.stream(values()).forEach(badge -> KEY_MAP.put(badge.key, badge)); 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/ModBadgeRenderer.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod; 2 | 3 | import com.terraformersmc.modmenu.gui.ModsScreen; 4 | import com.terraformersmc.modmenu.util.DrawingUtil; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.gui.DrawContext; 7 | import net.minecraft.text.OrderedText; 8 | 9 | import java.util.Set; 10 | 11 | public class ModBadgeRenderer { 12 | protected int startX, startY, badgeX, badgeY, badgeMax; 13 | protected Mod mod; 14 | protected MinecraftClient client; 15 | protected final ModsScreen screen; 16 | 17 | public ModBadgeRenderer(int startX, int startY, int endX, Mod mod, ModsScreen screen) { 18 | this.startX = startX; 19 | this.startY = startY; 20 | this.badgeMax = endX; 21 | this.mod = mod; 22 | this.screen = screen; 23 | this.client = MinecraftClient.getInstance(); 24 | } 25 | 26 | public void draw(DrawContext drawContext, int mouseX, int mouseY) { 27 | this.badgeX = startX; 28 | this.badgeY = startY; 29 | Set badges = mod.getBadges(); 30 | badges.forEach(badge -> drawBadge(drawContext, badge, mouseX, mouseY)); 31 | } 32 | 33 | public void drawBadge(DrawContext drawContext, Mod.Badge badge, int mouseX, int mouseY) { 34 | this.drawBadge( 35 | drawContext, 36 | badge.getText().asOrderedText(), 37 | badge.getOutlineColor(), 38 | badge.getFillColor() 39 | ); 40 | } 41 | 42 | public void drawBadge( 43 | DrawContext drawContext, 44 | OrderedText text, 45 | int outlineColor, 46 | int fillColor 47 | ) { 48 | int width = client.textRenderer.getWidth(text) + 6; 49 | if (badgeX + width < badgeMax) { 50 | DrawingUtil.drawBadge(drawContext, badgeX, badgeY, width, text, outlineColor, fillColor, 0xCACACA); 51 | badgeX += width + 3; 52 | } 53 | } 54 | 55 | public Mod getMod() { 56 | return mod; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/ModSearch.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.config.ModMenuConfig; 5 | import com.terraformersmc.modmenu.gui.ModsScreen; 6 | import net.minecraft.client.resource.language.I18n; 7 | import net.minecraft.util.Pair; 8 | 9 | import java.util.List; 10 | import java.util.Locale; 11 | import java.util.stream.Collectors; 12 | 13 | public class ModSearch { 14 | public static boolean validSearchQuery(String query) { 15 | return query != null && !query.isEmpty(); 16 | } 17 | 18 | public static List search(ModsScreen screen, String query, List candidates) { 19 | if (!validSearchQuery(query)) { 20 | return candidates; 21 | } else { 22 | return candidates.stream() 23 | .map(modContainer -> new Pair<>(modContainer, 24 | passesFilters(screen, modContainer, query.toLowerCase(Locale.ROOT)) 25 | )) 26 | .filter(pair -> pair.getRight() > 0) 27 | .sorted((a, b) -> b.getRight() - a.getRight()) 28 | .map(Pair::getLeft) 29 | .collect(Collectors.toList()); 30 | } 31 | } 32 | 33 | private static int passesFilters(ModsScreen screen, Mod mod, String query) { 34 | String modId = mod.getId(); 35 | String modName = mod.getName(); 36 | String modTranslatedName = mod.getTranslatedName(); 37 | String modDescription = mod.getDescription(); 38 | String modSummary = mod.getSummary(); 39 | 40 | String library = I18n.translate("modmenu.searchTerms.library"); 41 | String patchwork = I18n.translate("modmenu.searchTerms.patchwork"); 42 | String modpack = I18n.translate("modmenu.searchTerms.modpack"); 43 | String deprecated = I18n.translate("modmenu.searchTerms.deprecated"); 44 | String clientside = I18n.translate("modmenu.searchTerms.clientside"); 45 | String configurable = I18n.translate("modmenu.searchTerms.configurable"); 46 | String hasUpdate = I18n.translate("modmenu.searchTerms.hasUpdate"); 47 | 48 | // Libraries are currently hidden, ignore them entirely 49 | if (mod.isHidden() || !ModMenuConfig.SHOW_LIBRARIES.getValue() && mod.getBadges().contains(Mod.Badge.LIBRARY)) { 50 | return 0; 51 | } 52 | 53 | // Some basic search, could do with something more advanced but this will do for now 54 | if (modName.toLowerCase(Locale.ROOT).contains(query) // Search default mod name 55 | || modTranslatedName.toLowerCase(Locale.ROOT).contains(query) // Search localized mod name 56 | || modId.toLowerCase(Locale.ROOT).contains(query) // Search mod ID 57 | ) { 58 | return query.length() >= 3 ? 2 : 1; 59 | } 60 | 61 | if (modDescription.toLowerCase(Locale.ROOT).contains(query) // Search default mod description 62 | || modSummary.toLowerCase(Locale.ROOT).contains(query) // Search mod summary 63 | || authorMatches(mod, query) // Search via author 64 | || library.contains(query) && mod.getBadges().contains(Mod.Badge.LIBRARY) // Search for lib mods 65 | || patchwork.contains(query) && mod.getBadges() 66 | .contains(Mod.Badge.PATCHWORK_FORGE) // Search for patchwork mods 67 | || modpack.contains(query) && mod.getBadges().contains(Mod.Badge.MODPACK) // Search for modpack mods 68 | || deprecated.contains(query) && mod.getBadges() 69 | .contains(Mod.Badge.DEPRECATED) // Search for deprecated mods 70 | || clientside.contains(query) && mod.getBadges().contains(Mod.Badge.CLIENT) // Search for clientside mods 71 | || configurable.contains(query) && screen.getModHasConfigScreen(modId) // Search for mods that can be configured 72 | || hasUpdate.contains(query) && mod.hasUpdate() // Search for mods that have updates 73 | ) { 74 | return 1; 75 | } 76 | 77 | // Allow parent to pass filter if a child passes 78 | if (ModMenu.PARENT_MAP.keySet().contains(mod)) { 79 | for (Mod child : ModMenu.PARENT_MAP.get(mod)) { 80 | int result = passesFilters(screen, child, query); 81 | if (result > 0) { 82 | return result; 83 | } 84 | } 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | private static boolean authorMatches(Mod mod, String query) { 91 | return mod.getAuthors() 92 | .stream() 93 | .map(s -> s.toLowerCase(Locale.ROOT)) 94 | .anyMatch(s -> s.contains(query.toLowerCase(Locale.ROOT))); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/ModrinthUpdateInfo.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod; 2 | 3 | import com.terraformersmc.modmenu.api.UpdateChannel; 4 | import com.terraformersmc.modmenu.api.UpdateInfo; 5 | import com.terraformersmc.modmenu.util.VersionUtil; 6 | import net.minecraft.text.Text; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public record ModrinthUpdateInfo(String projectId, String versionId, String versionNumber, 10 | UpdateChannel getUpdateChannel) implements UpdateInfo { 11 | private static final Text MODRINTH_TEXT = Text.translatable("modmenu.modrinth"); 12 | 13 | @Override 14 | public boolean isUpdateAvailable() { 15 | return true; 16 | } 17 | 18 | @Override 19 | public @NotNull Text getUpdateMessage() { 20 | return Text.translatable("modmenu.updateText", VersionUtil.stripPrefix(this.versionNumber), MODRINTH_TEXT); 21 | } 22 | 23 | @Override 24 | public String getDownloadLink() { 25 | return "https://modrinth.com/project/%s/version/%s".formatted(projectId, versionId); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/fabric/CustomValueUtil.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod.fabric; 2 | 3 | import net.fabricmc.loader.api.metadata.CustomValue; 4 | import net.fabricmc.loader.api.metadata.ModMetadata; 5 | 6 | import java.util.*; 7 | 8 | public class CustomValueUtil { 9 | public static Optional getBoolean(String key, ModMetadata metadata) { 10 | if (metadata.containsCustomValue(key)) { 11 | return Optional.of(metadata.getCustomValue(key).getAsBoolean()); 12 | } 13 | return Optional.empty(); 14 | } 15 | 16 | public static Optional getString(String key, ModMetadata metadata) { 17 | if (metadata.containsCustomValue(key)) { 18 | return Optional.of(metadata.getCustomValue(key).getAsString()); 19 | } 20 | return Optional.empty(); 21 | } 22 | 23 | public static Optional> getStringSet(String key, ModMetadata metadata) { 24 | if (metadata.containsCustomValue(key)) { 25 | return getStringSet(key, metadata.getCustomValue(key).getAsObject()); 26 | } 27 | return Optional.empty(); 28 | } 29 | 30 | public static Optional getBoolean(String key, CustomValue.CvObject object) { 31 | if (object.containsKey(key)) { 32 | return Optional.of(object.get(key).getAsBoolean()); 33 | } 34 | return Optional.empty(); 35 | } 36 | 37 | public static Optional getString(String key, CustomValue.CvObject object) { 38 | if (object.containsKey(key)) { 39 | return Optional.of(object.get(key).getAsString()); 40 | } 41 | return Optional.empty(); 42 | } 43 | 44 | public static Optional getStringArray(String key, CustomValue.CvObject object) { 45 | if (object.containsKey(key)) { 46 | CustomValue.CvArray cvArray = object.get(key).getAsArray(); 47 | String[] strings = new String[cvArray.size()]; 48 | for (int i = 0; i < cvArray.size(); i++) { 49 | strings[i] = cvArray.get(i).getAsString(); 50 | } 51 | return Optional.of(strings); 52 | } 53 | return Optional.empty(); 54 | } 55 | 56 | public static Optional> getStringSet(String key, CustomValue.CvObject object) { 57 | if (object.containsKey(key)) { 58 | Set strings = new HashSet<>(); 59 | for (CustomValue value : object.get(key).getAsArray()) { 60 | strings.add(value.getAsString()); 61 | } 62 | return Optional.of(strings); 63 | } 64 | return Optional.empty(); 65 | } 66 | 67 | public static Optional> getStringMap(String key, CustomValue.CvObject object) { 68 | if (object.containsKey(key)) { 69 | Map strings = new HashMap<>(); 70 | for (Map.Entry entry : object.get(key).getAsObject()) { 71 | strings.put(entry.getKey(), entry.getValue().getAsString()); 72 | } 73 | return Optional.of(strings); 74 | } 75 | return Optional.empty(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/fabric/FabricDummyParentMod.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod.fabric; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import com.terraformersmc.modmenu.api.UpdateChecker; 5 | import com.terraformersmc.modmenu.api.UpdateInfo; 6 | import com.terraformersmc.modmenu.config.ModMenuConfig; 7 | import com.terraformersmc.modmenu.util.mod.Mod; 8 | import net.fabricmc.loader.api.FabricLoader; 9 | import net.fabricmc.loader.api.ModContainer; 10 | import net.minecraft.client.texture.NativeImageBackedTexture; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.*; 15 | 16 | public class FabricDummyParentMod implements Mod { 17 | private final String id; 18 | private final FabricMod host; 19 | private boolean childHasUpdate; 20 | 21 | public FabricDummyParentMod(FabricMod host, String id) { 22 | this.host = host; 23 | this.id = id; 24 | } 25 | 26 | @Override 27 | public @NotNull String getId() { 28 | return id; 29 | } 30 | 31 | @Override 32 | public @NotNull String getName() { 33 | FabricMod.ModMenuData.DummyParentData parentData = host.getModMenuData().getDummyParentData(); 34 | if (parentData != null) { 35 | return parentData.getName().orElse(""); 36 | } 37 | if (id.equals("fabric-api")) { 38 | return "Fabric API"; 39 | } 40 | return id; 41 | } 42 | 43 | @Override 44 | public @NotNull NativeImageBackedTexture getIcon(FabricIconHandler iconHandler, int i) { 45 | String iconSourceId = host.getId(); 46 | FabricMod.ModMenuData.DummyParentData parentData = host.getModMenuData().getDummyParentData(); 47 | String iconPath = null; 48 | if (parentData != null) { 49 | iconPath = parentData.getIcon().orElse(null); 50 | } 51 | if ("inherit".equals(iconPath)) { 52 | return host.getIcon(iconHandler, i); 53 | } 54 | if (iconPath == null) { 55 | iconSourceId = ModMenu.MOD_ID; 56 | if (id.equals("fabric-api")) { 57 | iconPath = "assets/" + ModMenu.MOD_ID + "/fabric.png"; 58 | } else { 59 | iconPath = "assets/" + ModMenu.MOD_ID + "/unknown_parent.png"; 60 | } 61 | } 62 | final String finalIconSourceId = iconSourceId; 63 | ModContainer iconSource = FabricLoader.getInstance() 64 | .getModContainer(iconSourceId) 65 | .orElseThrow(() -> new RuntimeException("Cannot get ModContainer for Fabric mod with id " + finalIconSourceId)); 66 | return Objects.requireNonNull( 67 | iconHandler.createIcon(iconSource, iconPath), 68 | "Mod icon for " + getId() + " is null somehow (should be filled with default in this case)" 69 | ); 70 | } 71 | 72 | @Override 73 | public @NotNull String getDescription() { 74 | FabricMod.ModMenuData.DummyParentData parentData = host.getModMenuData().getDummyParentData(); 75 | if (parentData != null) { 76 | return parentData.getDescription().orElse(""); 77 | } 78 | return ""; 79 | } 80 | 81 | @Override 82 | public @NotNull String getVersion() { 83 | return ""; 84 | } 85 | 86 | @Override 87 | public @NotNull String getPrefixedVersion() { 88 | return ""; 89 | } 90 | 91 | @Override 92 | public @NotNull List getAuthors() { 93 | return new ArrayList<>(); 94 | } 95 | 96 | @Override 97 | public @NotNull Map> getContributors() { 98 | return Map.of(); 99 | } 100 | 101 | @Override 102 | public @NotNull SortedMap> getCredits() { 103 | return new TreeMap<>(); 104 | } 105 | 106 | @Override 107 | public @NotNull Set getBadges() { 108 | FabricMod.ModMenuData.DummyParentData parentData = host.getModMenuData().getDummyParentData(); 109 | if (parentData != null) { 110 | return parentData.getBadges(); 111 | } 112 | var badges = new HashSet(); 113 | if (id.equals("fabric-api")) { 114 | badges.add(Badge.LIBRARY); 115 | } 116 | 117 | boolean modpackChildren = true; 118 | for (Mod mod : ModMenu.PARENT_MAP.get(this)) { 119 | if (!mod.getBadges().contains(Badge.MODPACK)) { 120 | modpackChildren = false; 121 | } 122 | } 123 | if (modpackChildren) { 124 | badges.add(Badge.MODPACK); 125 | } 126 | 127 | return badges; 128 | } 129 | 130 | @Override 131 | public @Nullable String getWebsite() { 132 | return null; 133 | } 134 | 135 | @Override 136 | public @Nullable String getIssueTracker() { 137 | return null; 138 | } 139 | 140 | @Override 141 | public @Nullable String getSource() { 142 | return null; 143 | } 144 | 145 | @Override 146 | public @Nullable String getParent() { 147 | return null; 148 | } 149 | 150 | @Override 151 | public @NotNull Set getLicense() { 152 | return new HashSet<>(); 153 | } 154 | 155 | @Override 156 | public @NotNull Map getLinks() { 157 | return new HashMap<>(); 158 | } 159 | 160 | @Override 161 | public boolean isReal() { 162 | return false; 163 | } 164 | 165 | @Override 166 | public boolean allowsUpdateChecks() { 167 | return false; 168 | } 169 | 170 | @Override 171 | public @Nullable UpdateChecker getUpdateChecker() { 172 | return null; 173 | } 174 | 175 | @Override 176 | public void setUpdateChecker(@Nullable UpdateChecker updateChecker) { 177 | 178 | } 179 | 180 | @Override 181 | public @Nullable UpdateInfo getUpdateInfo() { 182 | return null; 183 | } 184 | 185 | @Override 186 | public void setUpdateInfo(@Nullable UpdateInfo updateInfo) { 187 | 188 | } 189 | 190 | @Override 191 | public boolean getChildHasUpdate() { 192 | return childHasUpdate; 193 | } 194 | 195 | @Override 196 | public void setChildHasUpdate() { 197 | this.childHasUpdate = true; 198 | } 199 | 200 | @Override 201 | public boolean isHidden() { 202 | return ModMenuConfig.HIDDEN_MODS.getValue().contains(this.getId()); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/fabric/FabricIconHandler.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod.fabric; 2 | 3 | import com.terraformersmc.modmenu.ModMenu; 4 | import net.fabricmc.loader.api.ModContainer; 5 | import net.minecraft.client.texture.NativeImage; 6 | import net.minecraft.client.texture.NativeImageBackedTexture; 7 | import net.minecraft.util.Identifier; 8 | import org.apache.commons.lang3.Validate; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.io.Closeable; 13 | import java.io.InputStream; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import java.util.Objects; 19 | 20 | public class FabricIconHandler implements Closeable { 21 | private static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu | FabricIconHandler"); 22 | 23 | private final Map modIconCache = new HashMap<>(); 24 | 25 | public NativeImageBackedTexture createIcon(ModContainer iconSource, String iconPath) { 26 | try { 27 | Path path = iconSource.getPath(iconPath); 28 | NativeImageBackedTexture cachedIcon = getCachedModIcon(path); 29 | if (cachedIcon != null) { 30 | return cachedIcon; 31 | } 32 | 33 | cachedIcon = getCachedModIcon(path); 34 | if (cachedIcon != null) { 35 | return cachedIcon; 36 | } 37 | 38 | try (InputStream inputStream = Files.newInputStream(path)) { 39 | NativeImage image = NativeImage.read(Objects.requireNonNull(inputStream)); 40 | Validate.validState(image.getHeight() == image.getWidth(), "Must be square icon"); 41 | NativeImageBackedTexture tex = new NativeImageBackedTexture(() -> Identifier.of(ModMenu.MOD_ID, path.toString()).toString(), image); 42 | cacheModIcon(path, tex); 43 | return tex; 44 | } 45 | } catch (IllegalStateException e) { 46 | if (e.getMessage().equals("Must be square icon")) { 47 | LOGGER.error("Mod icon must be a square for icon source {}: {}", 48 | iconSource.getMetadata().getId(), 49 | iconPath 50 | ); 51 | } 52 | 53 | return null; 54 | } catch (Throwable t) { 55 | if (!iconPath.equals("assets/" + iconSource.getMetadata().getId() + "/icon.png")) { 56 | LOGGER.error("Invalid mod icon for icon source {}: {}", iconSource.getMetadata().getId(), iconPath); 57 | } 58 | return null; 59 | } 60 | } 61 | 62 | @Override 63 | public void close() { 64 | for (NativeImageBackedTexture tex : modIconCache.values()) { 65 | tex.close(); 66 | } 67 | } 68 | 69 | NativeImageBackedTexture getCachedModIcon(Path path) { 70 | return modIconCache.get(path); 71 | } 72 | 73 | void cacheModIcon(Path path, NativeImageBackedTexture tex) { 74 | modIconCache.put(path, tex); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/fabric/FabricLoaderUpdateChecker.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod.fabric; 2 | 3 | import com.google.gson.JsonParser; 4 | import com.terraformersmc.modmenu.api.UpdateChannel; 5 | import com.terraformersmc.modmenu.api.UpdateChecker; 6 | import com.terraformersmc.modmenu.api.UpdateInfo; 7 | import com.terraformersmc.modmenu.util.HttpUtil; 8 | import com.terraformersmc.modmenu.util.JsonUtil; 9 | import com.terraformersmc.modmenu.util.OptionalUtil; 10 | import net.fabricmc.loader.api.FabricLoader; 11 | import net.fabricmc.loader.api.SemanticVersion; 12 | import net.fabricmc.loader.api.Version; 13 | import net.fabricmc.loader.api.VersionParsingException; 14 | import net.minecraft.text.Text; 15 | import org.jetbrains.annotations.Nullable; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import java.io.IOException; 20 | import java.net.URI; 21 | import java.net.http.HttpRequest; 22 | import java.net.http.HttpResponse; 23 | 24 | public class FabricLoaderUpdateChecker implements UpdateChecker { 25 | public static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu/Fabric Update Checker"); 26 | private static final URI LOADER_VERSIONS = URI.create("https://meta.fabricmc.net/v2/versions/loader"); 27 | 28 | @Override 29 | public UpdateInfo checkForUpdates() { 30 | UpdateInfo result = null; 31 | 32 | try { 33 | result = checkForUpdates0(); 34 | } catch (InterruptedException e) { 35 | Thread.currentThread().interrupt(); 36 | } catch (IOException e) { 37 | LOGGER.error("Failed Fabric Loader update check!", e); 38 | } 39 | 40 | return result; 41 | } 42 | 43 | private static UpdateInfo checkForUpdates0() throws IOException, InterruptedException { 44 | var preferredChannel = UpdateChannel.getUserPreference(); 45 | 46 | var request = HttpRequest.newBuilder().GET().uri(LOADER_VERSIONS); 47 | var response = HttpUtil.request(request, HttpResponse.BodyHandlers.ofString()); 48 | 49 | var status = response.statusCode(); 50 | 51 | if (status != 200) { 52 | LOGGER.warn("Fabric Meta responded with a non-200 status: {}!", status); 53 | return null; 54 | } 55 | 56 | var contentType = response.headers().firstValue("Content-Type"); 57 | 58 | if (contentType.isEmpty() || !contentType.get().contains("application/json")) { 59 | LOGGER.warn("Fabric Meta responded with a non-json content type, aborting loader update check!"); 60 | return null; 61 | } 62 | 63 | var data = JsonParser.parseString(response.body()); 64 | 65 | if (!data.isJsonArray()) { 66 | LOGGER.warn("Received invalid data from Fabric Meta, aborting loader update check!"); 67 | return null; 68 | } 69 | 70 | SemanticVersion match = null; 71 | boolean stableVersion = true; 72 | 73 | for (var child : data.getAsJsonArray()) { 74 | if (!child.isJsonObject()) { 75 | continue; 76 | } 77 | 78 | var object = child.getAsJsonObject(); 79 | var version = JsonUtil.getString(object, "version"); 80 | 81 | if (version.isEmpty()) { 82 | continue; 83 | } 84 | 85 | SemanticVersion parsed; 86 | 87 | try { 88 | parsed = SemanticVersion.parse(version.get()); 89 | } catch (VersionParsingException e) { 90 | continue; 91 | } 92 | 93 | // Why aren't betas just marked as beta in the version string ... 94 | var stable = OptionalUtil.isPresentAndTrue(JsonUtil.getBoolean(object, "stable")); 95 | 96 | if (preferredChannel == UpdateChannel.RELEASE && !stable) { 97 | continue; 98 | } 99 | 100 | if (match == null || isNewer(parsed, match)) { 101 | match = parsed; 102 | stableVersion = stable; 103 | } 104 | } 105 | 106 | Version current = getCurrentVersion(); 107 | 108 | if (match == null || !isNewer(match, current)) { 109 | LOGGER.debug("Fabric Loader is up to date."); 110 | return null; 111 | } 112 | 113 | LOGGER.debug("Fabric Loader has a matching update available!"); 114 | return new FabricLoaderUpdateInfo(match.getFriendlyString(), stableVersion); 115 | } 116 | 117 | private static boolean isNewer(Version self, Version other) { 118 | return self.compareTo(other) > 0; 119 | } 120 | 121 | private static Version getCurrentVersion() { 122 | return FabricLoader.getInstance().getModContainer("fabricloader").get().getMetadata().getVersion(); 123 | } 124 | 125 | private static class FabricLoaderUpdateInfo implements UpdateInfo { 126 | private final String version; 127 | private final boolean isStable; 128 | 129 | private FabricLoaderUpdateInfo(String version, boolean isStable) { 130 | this.version = version; 131 | this.isStable = isStable; 132 | } 133 | 134 | @Override 135 | public boolean isUpdateAvailable() { 136 | return true; 137 | } 138 | 139 | @Override 140 | public @Nullable Text getUpdateMessage() { 141 | return Text.translatable("modmenu.install_version", this.version); 142 | } 143 | 144 | @Override 145 | public String getDownloadLink() { 146 | return "https://fabricmc.net/use/installer"; 147 | } 148 | 149 | @Override 150 | public UpdateChannel getUpdateChannel() { 151 | return this.isStable ? UpdateChannel.RELEASE : UpdateChannel.BETA; 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/quilt/QuiltLoaderUpdateChecker.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod.quilt; 2 | 3 | import com.google.gson.JsonParser; 4 | import com.terraformersmc.modmenu.api.UpdateChannel; 5 | import com.terraformersmc.modmenu.api.UpdateChecker; 6 | import com.terraformersmc.modmenu.api.UpdateInfo; 7 | import com.terraformersmc.modmenu.util.HttpUtil; 8 | import com.terraformersmc.modmenu.util.JsonUtil; 9 | import net.minecraft.text.Text; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.quiltmc.loader.api.QuiltLoader; 12 | import org.quiltmc.loader.api.Version; 13 | import org.quiltmc.loader.api.VersionFormatException; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.IOException; 18 | import java.net.URI; 19 | import java.net.http.HttpRequest; 20 | import java.net.http.HttpResponse; 21 | 22 | public class QuiltLoaderUpdateChecker implements UpdateChecker { 23 | public static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu/Quilt Update Checker"); 24 | private static final URI LOADER_VERSIONS = URI.create("https://meta.quiltmc.org/v3/versions/loader"); 25 | 26 | @Override 27 | public UpdateInfo checkForUpdates() { 28 | UpdateInfo result = null; 29 | try { 30 | result = checkForUpdates0(); 31 | } catch (InterruptedException e) { 32 | Thread.currentThread().interrupt(); 33 | } catch (IOException e) { 34 | LOGGER.error("Failed Quilt Loader update check!", e); 35 | } 36 | 37 | return result; 38 | } 39 | 40 | private static UpdateInfo checkForUpdates0() throws IOException, InterruptedException { 41 | var preferredChannel = UpdateChannel.getUserPreference(); 42 | 43 | var request = HttpRequest.newBuilder().GET().uri(LOADER_VERSIONS); 44 | var response = HttpUtil.request(request, HttpResponse.BodyHandlers.ofString()); 45 | 46 | var status = response.statusCode(); 47 | if (status != 200) { 48 | LOGGER.warn("Quilt Meta responded with a non-200 status: {}!", status); 49 | return null; 50 | } 51 | 52 | var contentType = response.headers().firstValue("Content-Type"); 53 | if (contentType.isEmpty() || !contentType.get().contains("application/json")) { 54 | LOGGER.warn("Quilt Meta responded with a non-json content type, aborting loader update check!"); 55 | return null; 56 | } 57 | 58 | var data = JsonParser.parseString(response.body()); 59 | if (!data.isJsonArray()) { 60 | LOGGER.warn("Received invalid data from Quilt Meta, aborting loader update check!"); 61 | return null; 62 | } 63 | 64 | Version.Semantic match = null; 65 | for (var child : data.getAsJsonArray()) { 66 | if (!child.isJsonObject()) { 67 | continue; 68 | } 69 | 70 | var object = child.getAsJsonObject(); 71 | var version = JsonUtil.getString(object, "version"); 72 | if (version.isEmpty()) { 73 | continue; 74 | } 75 | 76 | Version.Semantic parsed; 77 | try { 78 | parsed = Version.Semantic.of(version.get()); 79 | } catch (VersionFormatException e) { 80 | continue; 81 | } 82 | 83 | if (preferredChannel == UpdateChannel.RELEASE && !parsed.preRelease().isEmpty()) { 84 | continue; 85 | } else if (preferredChannel == UpdateChannel.BETA && !isStableOrBeta(parsed.preRelease())) { 86 | continue; 87 | } 88 | 89 | if (match == null || isNewer(parsed, match)) { 90 | match = parsed; 91 | } 92 | } 93 | 94 | Version.Semantic current = getCurrentVersion(); 95 | if (match == null || !isNewer(match, current)) { 96 | LOGGER.debug("Quilt Loader is up to date."); 97 | return null; 98 | } 99 | 100 | LOGGER.debug("Quilt Loader has a matching update available!"); 101 | return new QuiltLoaderUpdateInfo(match); 102 | } 103 | 104 | private static boolean isNewer(Version.Semantic self, Version.Semantic other) { 105 | return self.compareTo(other) > 0; 106 | } 107 | 108 | private static Version.Semantic getCurrentVersion() { 109 | return QuiltLoader.getModContainer("quilt_loader").get().metadata().version().semantic(); 110 | } 111 | 112 | private static boolean isStableOrBeta(String preRelease) { 113 | return preRelease.isEmpty() || preRelease.startsWith("beta") || preRelease.startsWith("pre") || 114 | preRelease.startsWith("rc"); 115 | } 116 | 117 | private record QuiltLoaderUpdateInfo(Version.Semantic version) implements UpdateInfo { 118 | @Override 119 | public boolean isUpdateAvailable() { 120 | return true; 121 | } 122 | 123 | @Override 124 | public @NotNull Text getUpdateMessage() { 125 | return Text.translatable("modmenu.install_version", this.version.raw()); 126 | } 127 | 128 | @Override 129 | public String getDownloadLink() { 130 | return "https://quiltmc.org/en/install/client"; 131 | } 132 | 133 | @Override 134 | public UpdateChannel getUpdateChannel() { 135 | var preRelease = this.version.preRelease(); 136 | if (preRelease.isEmpty()) { 137 | return UpdateChannel.RELEASE; 138 | } else if (isStableOrBeta(preRelease)) { 139 | return UpdateChannel.BETA; 140 | } else { 141 | return UpdateChannel.ALPHA; 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/terraformersmc/modmenu/util/mod/quilt/QuiltMod.java: -------------------------------------------------------------------------------- 1 | package com.terraformersmc.modmenu.util.mod.quilt; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.common.hash.Hashing; 5 | import com.terraformersmc.modmenu.util.UpdateCheckerUtil; 6 | import com.terraformersmc.modmenu.util.mod.fabric.FabricMod; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.quiltmc.loader.api.ModContainer; 10 | import org.quiltmc.loader.api.ModContributor; 11 | import org.quiltmc.loader.api.ModMetadata; 12 | import org.quiltmc.loader.api.QuiltLoader; 13 | 14 | import java.io.IOException; 15 | import java.nio.file.FileSystems; 16 | import java.nio.file.Files; 17 | import java.nio.file.Path; 18 | import java.util.*; 19 | import java.util.stream.Collectors; 20 | 21 | public class QuiltMod extends FabricMod { 22 | protected final ModContainer container; 23 | protected final ModMetadata metadata; 24 | 25 | public QuiltMod(net.fabricmc.loader.api.ModContainer fabricModContainer, Set modpackMods) { 26 | super(fabricModContainer, modpackMods); 27 | this.container = QuiltLoader.getModContainer(fabricModContainer.getMetadata().getId()).get(); 28 | this.metadata = container.metadata(); 29 | if ("quilt_loader".equals(metadata.id())) { 30 | badges.add(Badge.LIBRARY); 31 | } 32 | } 33 | 34 | @Override 35 | public @NotNull List getAuthors() { 36 | List authors = metadata.contributors() 37 | .stream() 38 | .filter(contributor -> contributor.role().equals("Author") || contributor.role().equals("Owner")) 39 | .map(ModContributor::name) 40 | .collect(Collectors.toList()); 41 | if (authors.isEmpty()) { 42 | metadata.contributors() 43 | .stream() 44 | .findFirst() 45 | .ifPresent(modContributor -> authors.add(modContributor.name())); 46 | } 47 | 48 | if (authors.isEmpty()) { 49 | if ("minecraft".equals(getId())) { 50 | return Lists.newArrayList("Mojang Studios"); 51 | } else if ("java".equals(getId())) { 52 | return Lists.newArrayList(System.getProperty("java.vendor")); 53 | } 54 | } 55 | 56 | return authors; 57 | } 58 | 59 | @Override 60 | public @NotNull Map> getContributors() { 61 | Map> contributors = new LinkedHashMap<>(); 62 | 63 | for (var contributor : this.metadata.contributors()) { 64 | contributors.put(contributor.name(), contributor.roles()); 65 | } 66 | 67 | return contributors; 68 | } 69 | 70 | @Override 71 | public @NotNull SortedMap> getCredits() { 72 | SortedMap> credits = new TreeMap<>(); 73 | 74 | var contributors = this.getContributors(); 75 | 76 | for (var contributor : contributors.entrySet()) { 77 | for (var role : contributor.getValue()) { 78 | credits.computeIfAbsent(role, key -> new LinkedHashSet<>()); 79 | credits.get(role).add(contributor.getKey()); 80 | } 81 | } 82 | 83 | return credits; 84 | } 85 | 86 | 87 | public @Nullable String getSha512Hash() throws IOException { 88 | var fabricResult = super.getSha512Hash(); 89 | if (fabricResult == null) { 90 | UpdateCheckerUtil.LOGGER.debug("Checking {}", getId()); 91 | if (container.getSourceType().equals(ModContainer.BasicSourceType.NORMAL_QUILT) || container.getSourceType() 92 | .equals(ModContainer.BasicSourceType.NORMAL_FABRIC)) { 93 | for (var paths : container.getSourcePaths()) { 94 | List jars = paths.stream() 95 | .filter(p -> p.toString().toLowerCase(Locale.ROOT).endsWith(".jar")) 96 | .toList(); 97 | 98 | if (jars.size() == 1 && jars.get(0).getFileSystem() == FileSystems.getDefault()) { 99 | var path = jars.get(0); 100 | 101 | if (Files.exists(path)) { 102 | UpdateCheckerUtil.LOGGER.debug("Found {} hash", getId()); 103 | return com.google.common.io.Files.asByteSource(path.toFile()) 104 | .hash(Hashing.sha512()) 105 | .toString(); 106 | } 107 | } 108 | } 109 | } 110 | } 111 | return fabricResult; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/fabric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/fabric.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/java_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/java_icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/af_za.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/an-ES.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/az_az.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/ba_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/bak.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/bn-BD.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/br_fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/brb.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java Color", 133 | "option.modmenu.random_java_colors.true": "Vendor", 134 | "option.modmenu.random_java_colors.false": "Always Red", 135 | "option.modmenu.translate_names": "Names", 136 | "option.modmenu.translate_names.true": "Localized", 137 | "option.modmenu.translate_names.false": "Not Localized", 138 | "option.modmenu.translate_descriptions": "Descriptions", 139 | "option.modmenu.translate_descriptions.true": "Localized", 140 | "option.modmenu.translate_descriptions.false": "Not Localized", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/lzh.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "改囊之列", 3 | "key.modmenu.open_menu": "啟改囊之列", 4 | "modmenu.title": "改囊", 5 | "modmenu.nameTranslation.modmenu": "改囊之列", 6 | "modmenu.descriptionTranslation.modmenu": "入囊之列,窺其已載。", 7 | "modmenu.loaded": "(旣載%s囊)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s既載……善哉)", 10 | "modmenu.loaded.420.secret": "(%s既載……燃也)", 11 | "modmenu.mods.n": " (%s既載)", 12 | "modmenu.mods.1": " (%s既載)", 13 | "modmenu.mods.69.secret": " (%s既載……善哉)", 14 | "modmenu.mods.420.secret": " (%s囊……燃也)", 15 | "modmenu.search": "索", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "察", 23 | "modmenu.showingMods.n": "旣示%s囊", 24 | "modmenu.showingMods.1": "旣示%s囊", 25 | "modmenu.showingLibraries.n": "旣示%s庫", 26 | "modmenu.showingLibraries.1": "旣示%s庫", 27 | "modmenu.showingModsLibraries.n.n": "旣示%s囊及%s庫", 28 | "modmenu.showingModsLibraries.n.1": "旣示%s囊及%s庫", 29 | "modmenu.showingModsLibraries.1.n": "旣示%s囊及%s庫", 30 | "modmenu.showingModsLibraries.1.1": "旣示%s囊及%s庫", 31 | "modmenu.badge.library": "函式庫", 32 | "modmenu.badge.clientsideOnly": "客端", 33 | "modmenu.badge.deprecated": "旣摒", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "整合包", 36 | "modmenu.badge.minecraft": "礦藝", 37 | "modmenu.dropInfo.line1": "曳囊至此戶", 38 | "modmenu.dropInfo.line2": "以增益一囊", 39 | "modmenu.dropConfirm": "君實欲複製諸改囊至改囊夾乎?", 40 | "modmenu.dropSuccessful.line1": "畢", 41 | "modmenu.dropSuccessful.line2": "復啓以重載入改囊", 42 | "modmenu.modIdToolTip": "改囊編號: %s", 43 | "modmenu.authorPrefix": "著者: %s", 44 | "modmenu.config": "置設", 45 | "modmenu.configure": "置設...", 46 | "modmenu.configure.error": "無法加載「%s」的配置屏幕\n請報告給「%s」,而不是 Mod Menu", 47 | "modmenu.website": "覽其網葉", 48 | "modmenu.issues": "議題", 49 | "modmenu.credits": "囊貢獻者:", 50 | "modmenu.viewCredits": "囊貢獻者", 51 | "modmenu.license": "證:", 52 | "modmenu.links": "連結:", 53 | "modmenu.source": "源碼", 54 | "modmenu.hasUpdate": "有更新可用:", 55 | "modmenu.childHasUpdate": "此模組的一個子模組有更新。", 56 | "modmenu.updateText": "%2$s 中的 v%1$s", 57 | "modmenu.buymeacoffee": "予吾一茶", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "予", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub發佈頁面", 65 | "modmenu.github_sponsors": "GitHub贊助", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "推特", 76 | "modmenu.wiki": "維基", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "啟改囊之資料夾", 79 | "modmenu.configsFolder": "啟置設之資料夾", 80 | "modmenu.nameTranslation.minecraft": "礦藝", 81 | "modmenu.descriptionTranslation.minecraft": "基礎遊戲。", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "Java運行環境。", 84 | "modmenu.javaDistributionName": "運行:%s", 85 | "modmenu.options": "調囊之列", 86 | "option.modmenu.sorting": "序", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "前置", 90 | "option.modmenu.show_libraries.true": "旣示", 91 | "option.modmenu.show_libraries.false": "旣隱", 92 | "option.modmenu.hide_config_buttons": "調製鍵", 93 | "option.modmenu.hide_config_buttons.true": "旣隱", 94 | "option.modmenu.hide_config_buttons.false": "旣示", 95 | "option.modmenu.hide_badges": "囊標籤", 96 | "option.modmenu.hide_badges.true": "旣隱", 97 | "option.modmenu.hide_badges.false": "旣示", 98 | "option.modmenu.compact_list": "列", 99 | "option.modmenu.compact_list.true": "精", 100 | "option.modmenu.compact_list.false": "常", 101 | "option.modmenu.hide_mod_links": "連結", 102 | "option.modmenu.hide_mod_links.true": "旣隱", 103 | "option.modmenu.hide_mod_links.false": "旣示", 104 | "option.modmenu.hide_mod_credits": "囊貢獻者之列", 105 | "option.modmenu.hide_mod_credits.true": "旣隱", 106 | "option.modmenu.hide_mod_credits.false": "旣示", 107 | "option.modmenu.hide_mod_license": "改囊之證", 108 | "option.modmenu.hide_mod_license.true": "旣隱", 109 | "option.modmenu.hide_mod_license.false": "旣示", 110 | "option.modmenu.count_children": "改囊之附", 111 | "option.modmenu.count_children.true": "計", 112 | "option.modmenu.count_children.false": "略", 113 | "option.modmenu.count_libraries": "前置", 114 | "option.modmenu.count_libraries.true": "計", 115 | "option.modmenu.count_libraries.false": "略", 116 | "option.modmenu.count_hidden_mods": "既隱改囊", 117 | "option.modmenu.count_hidden_mods.true": "計", 118 | "option.modmenu.count_hidden_mods.false": "略", 119 | "option.modmenu.mod_count_location": "呈現總數", 120 | "option.modmenu.mod_count_location.title_screen": "鄰題", 121 | "option.modmenu.mod_count_location.mods_button": "顯鈕之處", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "咸顯", 123 | "option.modmenu.mod_count_location.none": "咸隱", 124 | "option.modmenu.easter_eggs": "復活節彩蛋", 125 | "option.modmenu.easter_eggs.true": "啟", 126 | "option.modmenu.easter_eggs.false": "未啟", 127 | "option.modmenu.mods_button_style": "顯鈕之處", 128 | "option.modmenu.mods_button_style.classic": "領域之次行", 129 | "option.modmenu.mods_button_style.replace_realms": "替領域也", 130 | "option.modmenu.mods_button_style.shrink": "同行于領域", 131 | "option.modmenu.mods_button_style.icon": "鄰領域鈕也", 132 | "option.modmenu.random_java_colors": "Java色", 133 | "option.modmenu.random_java_colors.true": "供應商", 134 | "option.modmenu.random_java_colors.false": "始赤", 135 | "option.modmenu.translate_names": "名", 136 | "option.modmenu.translate_names.true": "本土化", 137 | "option.modmenu.translate_names.false": "非本土化", 138 | "option.modmenu.translate_descriptions": "述", 139 | "option.modmenu.translate_descriptions.true": "本土化", 140 | "option.modmenu.translate_descriptions.false": "非本土化", 141 | "option.modmenu.update_checker": "更新檢測器", 142 | "option.modmenu.update_checker.true": "啟", 143 | "option.modmenu.update_checker.false": "未啟", 144 | "option.modmenu.button_update_badge": "更新指示器", 145 | "option.modmenu.button_update_badge.true": "旣示", 146 | "option.modmenu.button_update_badge.false": "旣隱" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/zh_hk.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "Mod Menu", 3 | "key.modmenu.open_menu": "Open Mod Menu", 4 | "modmenu.title": "Mods", 5 | "modmenu.nameTranslation.modmenu": "Mod Menu", 6 | "modmenu.descriptionTranslation.modmenu": "Adds a mod menu to view the list of mods you have installed.", 7 | "modmenu.loaded": "(%s Loaded)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(%s Loaded...nice)", 10 | "modmenu.loaded.420.secret": "(%s Loaded...blaze it)", 11 | "modmenu.mods.n": " (%s Mods)", 12 | "modmenu.mods.1": " (%s Mod)", 13 | "modmenu.mods.69.secret": " (%s Mods...nice)", 14 | "modmenu.mods.420.secret": " (%s Mods...blaze it)", 15 | "modmenu.search": "Search for mods", 16 | "modmenu.searchTerms.library": "api library", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "configurations configs configures configurable options", 22 | "modmenu.toggleFilterOptions": "Toggle Filter Options", 23 | "modmenu.showingMods.n": "Showing %s mods", 24 | "modmenu.showingMods.1": "Showing %s mod", 25 | "modmenu.showingLibraries.n": "Showing %s libraries", 26 | "modmenu.showingLibraries.1": "Showing %s library", 27 | "modmenu.showingModsLibraries.n.n": "Showing %s mods and %s libraries", 28 | "modmenu.showingModsLibraries.n.1": "Showing %s mods and %s library", 29 | "modmenu.showingModsLibraries.1.n": "Showing %s mod and %s libraries", 30 | "modmenu.showingModsLibraries.1.1": "Showing %s mod and %s library", 31 | "modmenu.badge.library": "Library", 32 | "modmenu.badge.clientsideOnly": "Client", 33 | "modmenu.badge.deprecated": "Deprecated", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "Modpack", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "Drag and drop files into", 38 | "modmenu.dropInfo.line2": "this window to add mods", 39 | "modmenu.dropConfirm": "Do you want to copy the following mods into the mods directory?", 40 | "modmenu.dropSuccessful.line1": "Successfully copied mods", 41 | "modmenu.dropSuccessful.line2": "Restart game to load mods", 42 | "modmenu.modIdToolTip": "Mod ID: %s", 43 | "modmenu.authorPrefix": "By %s", 44 | "modmenu.config": "Edit Config", 45 | "modmenu.configure": "Configure...", 46 | "modmenu.configure.error": "Failed to load config screen for '%s'\nReport to '%s', not Mod Menu", 47 | "modmenu.website": "Website", 48 | "modmenu.issues": "Issues", 49 | "modmenu.credits": "Credits:", 50 | "modmenu.viewCredits": "View Credits", 51 | "modmenu.license": "License:", 52 | "modmenu.links": "Links:", 53 | "modmenu.source": "Source", 54 | "modmenu.hasUpdate": "An update is available:", 55 | "modmenu.childHasUpdate": "A child of this mod has an update available.", 56 | "modmenu.updateText": "v%s on %s", 57 | "modmenu.buymeacoffee": "Buy Me a Coffee", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "Donate", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub Releases", 65 | "modmenu.github_sponsors": "GitHub Sponsors", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "Open Mods Folder", 79 | "modmenu.configsFolder": "Open Configs Folder", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "The base game.", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "The Java runtime environment.", 84 | "modmenu.javaDistributionName": "Running: %s", 85 | "modmenu.options": "Mod Menu Options", 86 | "option.modmenu.sorting": "Sort", 87 | "option.modmenu.sorting.ascending": "A-Z", 88 | "option.modmenu.sorting.descending": "Z-A", 89 | "option.modmenu.show_libraries": "Libraries", 90 | "option.modmenu.show_libraries.true": "Shown", 91 | "option.modmenu.show_libraries.false": "Hidden", 92 | "option.modmenu.hide_config_buttons": "Config Buttons", 93 | "option.modmenu.hide_config_buttons.true": "Hidden", 94 | "option.modmenu.hide_config_buttons.false": "Shown", 95 | "option.modmenu.hide_badges": "Mod Badges", 96 | "option.modmenu.hide_badges.true": "Hidden", 97 | "option.modmenu.hide_badges.false": "Shown", 98 | "option.modmenu.compact_list": "List", 99 | "option.modmenu.compact_list.true": "Compact", 100 | "option.modmenu.compact_list.false": "Standard", 101 | "option.modmenu.hide_mod_links": "Mod Links", 102 | "option.modmenu.hide_mod_links.true": "Hidden", 103 | "option.modmenu.hide_mod_links.false": "Shown", 104 | "option.modmenu.hide_mod_credits": "Mod Credits", 105 | "option.modmenu.hide_mod_credits.true": "Hidden", 106 | "option.modmenu.hide_mod_credits.false": "Shown", 107 | "option.modmenu.hide_mod_license": "Mod License", 108 | "option.modmenu.hide_mod_license.true": "Hidden", 109 | "option.modmenu.hide_mod_license.false": "Shown", 110 | "option.modmenu.count_children": "Children", 111 | "option.modmenu.count_children.true": "Counted", 112 | "option.modmenu.count_children.false": "Not Counted", 113 | "option.modmenu.count_libraries": "Libraries", 114 | "option.modmenu.count_libraries.true": "Counted", 115 | "option.modmenu.count_libraries.false": "Not Counted", 116 | "option.modmenu.count_hidden_mods": "Hidden Mods", 117 | "option.modmenu.count_hidden_mods.true": "Counted", 118 | "option.modmenu.count_hidden_mods.false": "Not Counted", 119 | "option.modmenu.mod_count_location": "Mod Count", 120 | "option.modmenu.mod_count_location.title_screen": "Title Corner", 121 | "option.modmenu.mod_count_location.mods_button": "Mods Button", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "Both", 123 | "option.modmenu.mod_count_location.none": "Neither", 124 | "option.modmenu.easter_eggs": "Easter Eggs", 125 | "option.modmenu.easter_eggs.true": "Enabled", 126 | "option.modmenu.easter_eggs.false": "Disabled", 127 | "option.modmenu.mods_button_style": "Mods Button", 128 | "option.modmenu.mods_button_style.classic": "Below Realms", 129 | "option.modmenu.mods_button_style.replace_realms": "Replace Realms", 130 | "option.modmenu.mods_button_style.shrink": "Adjacent", 131 | "option.modmenu.mods_button_style.icon": "Icon", 132 | "option.modmenu.random_java_colors": "Java 顏色", 133 | "option.modmenu.random_java_colors.true": "供應商", 134 | "option.modmenu.random_java_colors.false": "總是紅色", 135 | "option.modmenu.translate_names": "名稱", 136 | "option.modmenu.translate_names.true": "本地化", 137 | "option.modmenu.translate_names.false": "未本地化", 138 | "option.modmenu.translate_descriptions": "描述", 139 | "option.modmenu.translate_descriptions.true": "本地化", 140 | "option.modmenu.translate_descriptions.false": "未本地化", 141 | "option.modmenu.update_checker": "Update Checker", 142 | "option.modmenu.update_checker.true": "Enabled", 143 | "option.modmenu.update_checker.false": "Disabled", 144 | "option.modmenu.button_update_badge": "Update Indicator", 145 | "option.modmenu.button_update_badge.true": "Shown", 146 | "option.modmenu.button_update_badge.false": "Hidden" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "category.modmenu.name": "模組選單", 3 | "key.modmenu.open_menu": "開啟模組選單", 4 | "modmenu.title": "模組", 5 | "modmenu.nameTranslation.modmenu": "模組選單", 6 | "modmenu.descriptionTranslation.modmenu": "新增「模組選單」供您檢視已安裝的模組。", 7 | "modmenu.loaded": "(已載入 %s 個)", 8 | "modmenu.loaded.short": "(%s)", 9 | "modmenu.loaded.69.secret": "(已載入 %s 個……舒服)", 10 | "modmenu.loaded.420.secret": "(已載入 %s 個……燃燒吧)", 11 | "modmenu.mods.n": " (已載入 %s 個模組)", 12 | "modmenu.mods.1": " (已載入 %s 個模組)", 13 | "modmenu.mods.69.secret": " (%s 個模組……愉悅)", 14 | "modmenu.mods.420.secret": " (%s 個模組……燃燒吧)", 15 | "modmenu.search": "搜尋模組", 16 | "modmenu.searchTerms.library": "API 程式庫", 17 | "modmenu.searchTerms.patchwork": "patchwork forge fml", 18 | "modmenu.searchTerms.modpack": "modpack pack", 19 | "modmenu.searchTerms.deprecated": "deprecated outdated old", 20 | "modmenu.searchTerms.clientside": "clientside gameside", 21 | "modmenu.searchTerms.configurable": "配置 configs 配置可配置的選項", 22 | "modmenu.toggleFilterOptions": "開關篩選選項", 23 | "modmenu.showingMods.n": "已顯示 %s 個模組", 24 | "modmenu.showingMods.1": "已顯示 %s 個模組", 25 | "modmenu.showingLibraries.n": "已顯示 %s 個程式庫", 26 | "modmenu.showingLibraries.1": "已顯示 %s 個程式庫", 27 | "modmenu.showingModsLibraries.n.n": "已顯示 %s 個模組和 %s 個程式庫", 28 | "modmenu.showingModsLibraries.n.1": "已顯示 %s 個模組和 %s 個程式庫", 29 | "modmenu.showingModsLibraries.1.n": "已顯示 %s 個模組和 %s 個程式庫", 30 | "modmenu.showingModsLibraries.1.1": "已顯示 %s 個模組和 %s 個程式庫", 31 | "modmenu.badge.library": "程式庫", 32 | "modmenu.badge.clientsideOnly": "用戶端", 33 | "modmenu.badge.deprecated": "已過時", 34 | "modmenu.badge.forge": "Forge", 35 | "modmenu.badge.modpack": "模組包", 36 | "modmenu.badge.minecraft": "Minecraft", 37 | "modmenu.dropInfo.line1": "提示:", 38 | "modmenu.dropInfo.line2": "請將檔案拖曳至此視窗以新增模組。", 39 | "modmenu.dropConfirm": "確定要將下列模組複製到模組資料夾嗎?", 40 | "modmenu.dropSuccessful.line1": "成功複製模組", 41 | "modmenu.dropSuccessful.line2": "請重新啟動遊戲以載入模組", 42 | "modmenu.modIdToolTip": "模組 ID:%s", 43 | "modmenu.authorPrefix": "作者:%s", 44 | "modmenu.config": "編輯設定", 45 | "modmenu.configure": "設定…", 46 | "modmenu.configure.error": "無法加載「%s」的配置屏幕\n請報告給「%s」,而不是 Mod Menu", 47 | "modmenu.website": "瀏覽網站", 48 | "modmenu.issues": "議題", 49 | "modmenu.credits": "銘謝:", 50 | "modmenu.viewCredits": "檢視銘謝名單", 51 | "modmenu.license": "授權條款:", 52 | "modmenu.links": "連結:", 53 | "modmenu.source": "原始碼", 54 | "modmenu.hasUpdate": "有更新可用:", 55 | "modmenu.childHasUpdate": "此模組的一個子模組有更新。", 56 | "modmenu.updateText": "%2$s 中的 v%1$s", 57 | "modmenu.buymeacoffee": "請我喝杯咖啡", 58 | "modmenu.coindrop": "Coindrop", 59 | "modmenu.crowdin": "Crowdin", 60 | "modmenu.curseforge": "CurseForge", 61 | "modmenu.discord": "Discord", 62 | "modmenu.donate": "贊助", 63 | "modmenu.flattr": "Flattr", 64 | "modmenu.github_releases": "GitHub 發布頁面", 65 | "modmenu.github_sponsors": "GitHub原始碼", 66 | "modmenu.kofi": "Ko-fi", 67 | "modmenu.liberapay": "Liberapay", 68 | "modmenu.mastodon": "Mastodon", 69 | "modmenu.modrinth": "Modrinth", 70 | "modmenu.opencollective": "Open Collective", 71 | "modmenu.patreon": "Patreon", 72 | "modmenu.paypal": "PayPal", 73 | "modmenu.reddit": "Reddit", 74 | "modmenu.twitch": "Twitch", 75 | "modmenu.twitter": "Twitter", 76 | "modmenu.wiki": "Wiki", 77 | "modmenu.youtube": "YouTube", 78 | "modmenu.modsFolder": "開啟模組資料夾", 79 | "modmenu.configsFolder": "開啟設定檔資料夾", 80 | "modmenu.nameTranslation.minecraft": "Minecraft", 81 | "modmenu.descriptionTranslation.minecraft": "基礎遊戲。", 82 | "modmenu.nameTranslation.java": "Java", 83 | "modmenu.descriptionTranslation.java": "Java運行環境。", 84 | "modmenu.javaDistributionName": "正在運行:%s", 85 | "modmenu.options": "模組選單選項", 86 | "option.modmenu.sorting": "排序", 87 | "option.modmenu.sorting.ascending": "A 至 Z", 88 | "option.modmenu.sorting.descending": "Z 至 A", 89 | "option.modmenu.show_libraries": "程式庫", 90 | "option.modmenu.show_libraries.true": "顯示", 91 | "option.modmenu.show_libraries.false": "隱藏", 92 | "option.modmenu.hide_config_buttons": "設定按鈕", 93 | "option.modmenu.hide_config_buttons.true": "隱藏", 94 | "option.modmenu.hide_config_buttons.false": "顯示", 95 | "option.modmenu.hide_badges": "模組類型", 96 | "option.modmenu.hide_badges.true": "隱藏", 97 | "option.modmenu.hide_badges.false": "顯示", 98 | "option.modmenu.compact_list": "清單", 99 | "option.modmenu.compact_list.true": "密集", 100 | "option.modmenu.compact_list.false": "標準", 101 | "option.modmenu.hide_mod_links": "模組連結", 102 | "option.modmenu.hide_mod_links.true": "隱藏", 103 | "option.modmenu.hide_mod_links.false": "顯示", 104 | "option.modmenu.hide_mod_credits": "模組銘謝名單", 105 | "option.modmenu.hide_mod_credits.true": "隱藏", 106 | "option.modmenu.hide_mod_credits.false": "顯示", 107 | "option.modmenu.hide_mod_license": "模組授權條款", 108 | "option.modmenu.hide_mod_license.true": "隱藏", 109 | "option.modmenu.hide_mod_license.false": "顯示", 110 | "option.modmenu.count_children": "子模組", 111 | "option.modmenu.count_children.true": "計入", 112 | "option.modmenu.count_children.false": "不計入", 113 | "option.modmenu.count_libraries": "程式庫", 114 | "option.modmenu.count_libraries.true": "計入", 115 | "option.modmenu.count_libraries.false": "不計入", 116 | "option.modmenu.count_hidden_mods": "隱藏模組", 117 | "option.modmenu.count_hidden_mods.true": "計入", 118 | "option.modmenu.count_hidden_mods.false": "不計入", 119 | "option.modmenu.mod_count_location": "模組數量", 120 | "option.modmenu.mod_count_location.title_screen": "標題角落", 121 | "option.modmenu.mod_count_location.mods_button": "模組按鈕", 122 | "option.modmenu.mod_count_location.title_screen_and_mods_button": "全部顯示", 123 | "option.modmenu.mod_count_location.none": "隱藏", 124 | "option.modmenu.easter_eggs": "彩蛋", 125 | "option.modmenu.easter_eggs.true": "已啟用", 126 | "option.modmenu.easter_eggs.false": "已停用", 127 | "option.modmenu.mods_button_style": "模組按鈕", 128 | "option.modmenu.mods_button_style.classic": "Realms 下方", 129 | "option.modmenu.mods_button_style.replace_realms": "取代 Realms", 130 | "option.modmenu.mods_button_style.shrink": "相鄰 Realms", 131 | "option.modmenu.mods_button_style.icon": "圖示", 132 | "option.modmenu.random_java_colors": "Java顏色", 133 | "option.modmenu.random_java_colors.true": "供應商", 134 | "option.modmenu.random_java_colors.false": "總是紅色", 135 | "option.modmenu.translate_names": "名稱", 136 | "option.modmenu.translate_names.true": "本地化", 137 | "option.modmenu.translate_names.false": "未本地化", 138 | "option.modmenu.translate_descriptions": "描述", 139 | "option.modmenu.translate_descriptions.true": "本地化", 140 | "option.modmenu.translate_descriptions.false": "未本地化", 141 | "option.modmenu.update_checker": "更新檢測器", 142 | "option.modmenu.update_checker.true": "已啟用", 143 | "option.modmenu.update_checker.false": "已停用", 144 | "option.modmenu.button_update_badge": "更新指示器", 145 | "option.modmenu.button_update_badge.true": "顯示", 146 | "option.modmenu.button_update_badge.false": "隱藏" 147 | } 148 | -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/minecraft_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/minecraft_icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/textures/gui/configure_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/textures/gui/configure_button.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/textures/gui/filters_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/textures/gui/filters_button.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/textures/gui/mod_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/textures/gui/mod_configuration.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/textures/gui/mods_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/textures/gui/mods_button.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/textures/gui/mods_button_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/textures/gui/mods_button_alt.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/textures/gui/mods_button_alt3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/textures/gui/mods_button_alt3.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/textures/gui/parent_mod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/textures/gui/parent_mod.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/unknown_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/unknown_icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/modmenu/unknown_parent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/assets/modmenu/unknown_parent.png -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "modmenu", 4 | "name": "Mod Menu", 5 | "version": "$version", 6 | "environment": "client", 7 | "license": "MIT", 8 | "icon": "assets/modmenu/icon.png", 9 | "entrypoints": { 10 | "client": [ 11 | "com.terraformersmc.modmenu.ModMenu" 12 | ], 13 | "modmenu": [ 14 | "com.terraformersmc.modmenu.ModMenuModMenuCompat" 15 | ] 16 | }, 17 | "contact": { 18 | "homepage": "https://modrinth.com/mod/modmenu", 19 | "sources": "https://github.com/TerraformersMC/ModMenu", 20 | "issues": "https://github.com/TerraformersMC/ModMenu/issues" 21 | }, 22 | "depends": { 23 | "fabric-resource-loader-v0": "*", 24 | "fabric-screen-api-v1": "*", 25 | "fabric-key-binding-api-v1": "*", 26 | "fabric-lifecycle-events-v1": "*", 27 | "fabricloader": ">=0.16.10", 28 | "minecraft": ">=1.21.5-rc.1" 29 | }, 30 | "authors": [ 31 | "Prospector", 32 | "haykam821", 33 | "TerraformersMC" 34 | ], 35 | "contributors": [ 36 | "shedaniel", 37 | "Draylar", 38 | "Mordna", 39 | "Madis0", 40 | "Yanis48", 41 | "LemmaEOF", 42 | "fewizz", 43 | "XuyuEre", 44 | "geniiii", 45 | "thiagokenis", 46 | "Juuxel", 47 | "Chloe Dawn", 48 | "modmuss50", 49 | "Patbox", 50 | "Lykrast", 51 | "Pyrofab", 52 | "UpcraftLP", 53 | "swordglowsblue", 54 | "hinataaki", 55 | "LeonXu98", 56 | "magneticflux", 57 | "vanja-san", 58 | "AlexIIL", 59 | "masoncook16", 60 | "el97", 61 | "Hephaestus-Dev", 62 | "zabi94", 63 | "dexman545", 64 | "dhouck", 65 | "Hambaka", 66 | "po-stulate", 67 | "FlashyReese", 68 | "egeesin", 69 | "TheGlitch76", 70 | "ludg1e", 71 | "williambl", 72 | "PepperCode1", 73 | "ThatTrollzer", 74 | "ToffeeMax", 75 | "kalucky0", 76 | "Maaster", 77 | "MartrixX", 78 | "NotSteven", 79 | "Dolphin 2.1", 80 | "ENDERZOMBI102", 81 | "anatom3000", 82 | "OroArmor", 83 | "jackassmc", 84 | "Vaerian", 85 | "RDKRACZ", 86 | "Hulenkius", 87 | "XfedeX", 88 | "spnda", 89 | "Jab125", 90 | "SolidBlock", 91 | "Tkain", 92 | "nfitzen", 93 | "DenaryDev", 94 | "legenden", 95 | "NaiNonTH", 96 | "MagnusHJensen", 97 | "Benjamin-Norton", 98 | "triphora", 99 | "Pyrrha" 100 | ], 101 | "description": "Adds a mod menu to view the list of mods you have installed.", 102 | "mixins": [ 103 | "mixins.modmenu.json" 104 | ], 105 | "custom": { 106 | "modmenu": { 107 | "links": { 108 | "modmenu.discord": "https://discord.gg/jEGF5fb" 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/modmenu/textures/gui/configure_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/high_contrast/assets/modmenu/textures/gui/configure_button.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/modmenu/textures/gui/filters_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/high_contrast/assets/modmenu/textures/gui/filters_button.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/modmenu/textures/gui/mods_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/high_contrast/assets/modmenu/textures/gui/mods_button.png -------------------------------------------------------------------------------- /src/main/resources/mixins.modmenu.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.terraformersmc.modmenu.mixin", 5 | "compatibilityLevel": "JAVA_16", 6 | "client": [ 7 | "AccessorGridWidget", 8 | "MixinGameMenu", 9 | "MixinTitleScreen" 10 | ], 11 | "injectors": { 12 | "defaultRequire": 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/programmer_art/assets/modmenu/textures/gui/configure_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/programmer_art/assets/modmenu/textures/gui/configure_button.png -------------------------------------------------------------------------------- /src/main/resources/programmer_art/assets/modmenu/textures/gui/filters_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/programmer_art/assets/modmenu/textures/gui/filters_button.png -------------------------------------------------------------------------------- /src/main/resources/programmer_art/assets/modmenu/textures/gui/mods_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/programmer_art/assets/modmenu/textures/gui/mods_button.png -------------------------------------------------------------------------------- /src/main/resources/programmer_art/assets/modmenu/textures/gui/parent_mod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sakura-ryoko/ModMenu/d1376913939577ccf1bfc94c7151ba42754bfeef/src/main/resources/programmer_art/assets/modmenu/textures/gui/parent_mod.png --------------------------------------------------------------------------------