From dda63728e6164b9c782f8acaab841604e01b274e Mon Sep 17 00:00:00 2001 From: AndreAugustoDev <25272842+AndreAugustoDev@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:07:37 -0300 Subject: [PATCH 1/5] Multiloader port (fabric/neo) Migrate build scripts to Kotlin DSL (.gradle -> .gradle.kts) Migrate yarn mappings to mojmaps Manage dependencies with Version Catalog --- .editorconfig | 681 ++++++++++++++++++ .gitattributes | 131 ++++ .gitignore | 167 +++-- build-logic/build.gradle.kts | 8 + build-logic/settings.gradle.kts | 12 + .../src/main/kotlin/build-extensions.kt | 114 +++ .../main/kotlin/multiloader-common.gradle.kts | 124 ++++ .../main/kotlin/multiloader-loader.gradle.kts | 46 ++ build.gradle | 49 -- build.gradle.kts | 4 + common/build.gradle.kts | 50 ++ .../languagereload/LanguageReload.java | 54 +- .../access/IAdvancementsScreen.java | 4 - .../access/IAdvancementsTab.java | 4 - .../access/IClientLanguage.java | 9 + .../languagereload/access/ILanguage.java | 9 + .../access/ILanguageSelectScreen.java | 5 +- .../languagereload/config/Config.java | 21 +- .../languagereload/config/ConfigScreen.java | 51 ++ .../languagereload/gui/LanguageEntry.java | 110 ++- .../gui/LanguageListWidget.java | 111 +++ .../helper/ClientLanguageHelper.java | 20 + .../helper/SessionSearchTreesHelper.java | 43 ++ .../mixin/AdvancementTabMixin.java | 14 +- .../mixin/AdvancementWidgetAccessor.java | 16 +- .../mixin/AdvancementsScreenMixin.java | 18 +- .../mixin/BookViewScreenAccessor.java | 11 + .../mixin/ClientChunkCacheAccessor.java | 11 + .../ClientChunkCacheStorageAccessor.java | 14 + .../mixin/KeyboardHandlerMixin.java | 96 +++ .../mixin/LanguageManagerMixin.java | 48 +- .../languagereload/mixin/LanguageMixin.java | 41 ++ .../mixin/LanguageSelectScreenMixin.java | 173 +++++ .../LanguageSelectionListWidgetAccessor.java | 14 + .../languagereload/mixin/OptionsMixin.java | 63 +- .../mixin/RecipeBookComponentMixin.java | 14 +- .../mixin/SignTextAccessor.java | 12 + .../mixin/TextDisplayAccessor.java | 11 + .../mixin/TranslatableContentsMixin.java | 67 ++ .../languagereload/platform/Services.java | 46 ++ .../platform/services/IPlatformHelper.java | 43 ++ .../resources/META-INF/accesstransformer.cfg | 4 + .../resources/assets/languagereload/icon.png | Bin .../assets/languagereload/lang/be_by.json | 0 .../assets/languagereload/lang/en_us.json | 0 .../assets/languagereload/lang/es_ar.json | 0 .../assets/languagereload/lang/es_es.json | 0 .../assets/languagereload/lang/et_ee.json | 0 .../assets/languagereload/lang/fr_fr.json | 0 .../assets/languagereload/lang/it_it.json | 0 .../assets/languagereload/lang/ja_jp.json | 0 .../assets/languagereload/lang/ko_kr.json | 0 .../assets/languagereload/lang/ms_my.json | 0 .../assets/languagereload/lang/pl_pl.json | 0 .../assets/languagereload/lang/pt_br.json | 0 .../assets/languagereload/lang/ru_ru.json | 0 .../assets/languagereload/lang/tt_ru.json | 0 .../assets/languagereload/lang/uk_ua.json | 0 .../assets/languagereload/lang/vi_vn.json | 0 .../assets/languagereload/lang/zh_cn.json | 0 .../assets/languagereload/lang/zh_tw.json | 40 +- .../assets/languagereload/lang/zlm_arab.json | 0 .../gui/sprites/language_selection/add.png | Bin .../language_selection/add_highlighted.png | Bin .../sprites/language_selection/move_down.png | Bin .../move_down_highlighted.png | Bin .../sprites/language_selection/move_up.png | Bin .../move_up_highlighted.png | Bin .../gui/sprites/language_selection/remove.png | Bin .../language_selection/remove_highlighted.png | Bin .../resources/languagereload.accesswidener | 6 + .../main/resources/languagereload.mixins.json | 22 +- fabric/build.gradle.kts | 45 ++ .../config/ModMenuEntrypoint.java | 0 .../fabric/platform/FabricPlatformHelper.java | 28 + .../mixin/ClientLanguageMixin.java | 83 +++ .../mixin/SessionSearchTreesMixin.java | 23 + ...gereload.platform.services.IPlatformHelper | 1 + .../src}/main/resources/fabric.mod.json | 35 +- gradle.properties | 93 ++- gradle/libs.versions.toml | 77 ++ gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 43504 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- neoforge/build.gradle.kts | 58 ++ .../languagereload/LanguageReloadNeo.java | 17 + .../mixin/ClientLanguageMixin.java | 82 +++ .../mixin/SessionSearchTreesMixin.java | 24 + .../platform/NeoForgePlatformHelper.java | 30 + .../resources/META-INF/neoforge.mods.toml | 134 ++++ ...gereload.platform.services.IPlatformHelper | 1 + settings.gradle | 9 - settings.gradle.kts | 25 + .../languagereload/access/ILanguage.java | 12 - .../access/ITranslationStorage.java | 14 - .../languagereload/config/ConfigScreen.java | 52 -- .../gui/LanguageListWidget.java | 111 --- .../mixin/BookScreenAccessor.java | 11 - .../mixin/ClientChunkManagerAccessor.java | 11 - .../mixin/ClientChunkMapAccessor.java | 14 - .../languagereload/mixin/KeyboardMixin.java | 96 --- .../languagereload/mixin/LanguageMixin.java | 42 -- .../mixin/LanguageOptionsScreenMixin.java | 174 ----- .../LanguageSelectionListWidgetAccessor.java | 14 - .../mixin/SearchManagerMixin.java | 51 -- .../mixin/SignTextAccessor.java | 12 - .../mixin/TextDisplayEntityAccessor.java | 11 - .../mixin/TranslatableTextContentMixin.java | 67 -- .../mixin/TranslationStorageMixin.java | 83 --- .../resources/languagereload.accesswidener | 6 - 110 files changed, 2989 insertions(+), 1172 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 build-logic/build.gradle.kts create mode 100644 build-logic/settings.gradle.kts create mode 100644 build-logic/src/main/kotlin/build-extensions.kt create mode 100644 build-logic/src/main/kotlin/multiloader-common.gradle.kts create mode 100644 build-logic/src/main/kotlin/multiloader-loader.gradle.kts delete mode 100644 build.gradle create mode 100644 build.gradle.kts create mode 100644 common/build.gradle.kts rename {src => common/src}/main/java/jerozgen/languagereload/LanguageReload.java (53%) rename {src => common/src}/main/java/jerozgen/languagereload/access/IAdvancementsScreen.java (55%) rename {src => common/src}/main/java/jerozgen/languagereload/access/IAdvancementsTab.java (54%) create mode 100644 common/src/main/java/jerozgen/languagereload/access/IClientLanguage.java create mode 100644 common/src/main/java/jerozgen/languagereload/access/ILanguage.java rename src/main/java/jerozgen/languagereload/access/ILanguageOptionsScreen.java => common/src/main/java/jerozgen/languagereload/access/ILanguageSelectScreen.java (65%) rename {src => common/src}/main/java/jerozgen/languagereload/config/Config.java (81%) create mode 100644 common/src/main/java/jerozgen/languagereload/config/ConfigScreen.java rename {src => common/src}/main/java/jerozgen/languagereload/gui/LanguageEntry.java (52%) create mode 100644 common/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java create mode 100644 common/src/main/java/jerozgen/languagereload/helper/ClientLanguageHelper.java create mode 100644 common/src/main/java/jerozgen/languagereload/helper/SessionSearchTreesHelper.java rename {src => common/src}/main/java/jerozgen/languagereload/mixin/AdvancementTabMixin.java (75%) rename {src => common/src}/main/java/jerozgen/languagereload/mixin/AdvancementWidgetAccessor.java (65%) rename {src => common/src}/main/java/jerozgen/languagereload/mixin/AdvancementsScreenMixin.java (54%) create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/BookViewScreenAccessor.java create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheAccessor.java create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheStorageAccessor.java create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/KeyboardHandlerMixin.java rename {src => common/src}/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java (61%) create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectScreenMixin.java create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java rename src/main/java/jerozgen/languagereload/mixin/GameOptionsMixin.java => common/src/main/java/jerozgen/languagereload/mixin/OptionsMixin.java (65%) rename src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java => common/src/main/java/jerozgen/languagereload/mixin/RecipeBookComponentMixin.java (54%) create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/TextDisplayAccessor.java create mode 100644 common/src/main/java/jerozgen/languagereload/mixin/TranslatableContentsMixin.java create mode 100644 common/src/main/java/jerozgen/languagereload/platform/Services.java create mode 100644 common/src/main/java/jerozgen/languagereload/platform/services/IPlatformHelper.java create mode 100644 common/src/main/resources/META-INF/accesstransformer.cfg rename {src => common/src}/main/resources/assets/languagereload/icon.png (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/be_by.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/en_us.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/es_ar.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/es_es.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/et_ee.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/fr_fr.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/it_it.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/ja_jp.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/ko_kr.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/ms_my.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/pl_pl.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/pt_br.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/ru_ru.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/tt_ru.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/uk_ua.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/vi_vn.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/zh_cn.json (100%) rename {src => common/src}/main/resources/assets/languagereload/lang/zh_tw.json (98%) rename {src => common/src}/main/resources/assets/languagereload/lang/zlm_arab.json (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add.png (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add_highlighted.png (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down.png (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down_highlighted.png (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up.png (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up_highlighted.png (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove.png (100%) rename {src => common/src}/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove_highlighted.png (100%) create mode 100644 common/src/main/resources/languagereload.accesswidener rename {src => common/src}/main/resources/languagereload.mixins.json (54%) create mode 100644 fabric/build.gradle.kts rename {src => fabric/src}/main/java/jerozgen/languagereload/config/ModMenuEntrypoint.java (100%) create mode 100644 fabric/src/main/java/jerozgen/languagereload/fabric/platform/FabricPlatformHelper.java create mode 100644 fabric/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java create mode 100644 fabric/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java create mode 100644 fabric/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper rename {src => fabric/src}/main/resources/fabric.mod.json (77%) create mode 100644 gradle/libs.versions.toml create mode 100644 neoforge/build.gradle.kts create mode 100644 neoforge/src/main/java/jerozgen/languagereload/LanguageReloadNeo.java create mode 100644 neoforge/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java create mode 100644 neoforge/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java create mode 100644 neoforge/src/main/java/jerozgen/languagereload/neoforge/platform/NeoForgePlatformHelper.java create mode 100644 neoforge/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 neoforge/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts delete mode 100644 src/main/java/jerozgen/languagereload/access/ILanguage.java delete mode 100644 src/main/java/jerozgen/languagereload/access/ITranslationStorage.java delete mode 100644 src/main/java/jerozgen/languagereload/config/ConfigScreen.java delete mode 100644 src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/BookScreenAccessor.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/ClientChunkManagerAccessor.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/ClientChunkMapAccessor.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/SearchManagerMixin.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/TextDisplayEntityAccessor.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/TranslatableTextContentMixin.java delete mode 100644 src/main/java/jerozgen/languagereload/mixin/TranslationStorageMixin.java delete mode 100644 src/main/resources/languagereload.accesswidener diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6fa72c4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,681 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 4 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_wrap_on_typing = false + +[*.java] +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = false +ij_java_align_multiline_array_initializer_expression = false +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = false +ij_java_align_multiline_deconstruction_list_components = true +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = true +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = true +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_records = true +ij_java_align_multiline_resources = true +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_text_blocks = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = false +ij_java_align_types_in_multi_catch = true +ij_java_annotation_parameter_wrap = off +ij_java_array_initializer_new_line_after_left_brace = false +ij_java_array_initializer_right_brace_on_new_line = false +ij_java_array_initializer_wrap = off +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = off +ij_java_assignment_wrap = off +ij_java_binary_operation_sign_on_next_line = false +ij_java_binary_operation_wrap = off +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_field_with_annotations = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_before_imports = 1 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 0 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_add_space = false +ij_java_block_comment_at_first_column = true +ij_java_builder_methods = +ij_java_call_parameters_new_line_after_left_paren = false +ij_java_call_parameters_right_paren_on_new_line = false +ij_java_call_parameters_wrap = off +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 5 +ij_java_class_names_in_javadoc = 1 +ij_java_deconstruction_list_wrap = normal +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_not_wrap_after_single_annotation_in_parameter = false +ij_java_do_while_brace_force = always +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = true +ij_java_doc_align_param_comments = true +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = false +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = false +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_enum_constants_wrap = off +ij_java_enum_field_annotation_wrap = off +ij_java_extends_keyword_wrap = off +ij_java_extends_list_wrap = off +ij_java_field_annotation_wrap = split_into_lines +ij_java_field_name_prefix = +ij_java_field_name_suffix = +ij_java_finally_on_new_line = false +ij_java_for_brace_force = always +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = false +ij_java_for_statement_wrap = off +ij_java_generate_final_locals = false +ij_java_generate_final_parameters = false +ij_java_generate_use_type_annotation_before_type = true +ij_java_if_brace_force = always +ij_java_imports_layout = *,|,javax.**,java.**,|,$* +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = false +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 2 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_builder_methods_indents = false +ij_java_keep_control_statement_in_one_line = true +ij_java_keep_first_column_comment = true +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = true +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = false +ij_java_keep_simple_lambdas_in_one_line = false +ij_java_keep_simple_methods_in_one_line = false +ij_java_label_indent_absolute = false +ij_java_label_indent_size = 0 +ij_java_lambda_brace_style = end_of_line +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = false +ij_java_line_comment_add_space_on_reformat = false +ij_java_line_comment_at_first_column = true +ij_java_local_variable_name_prefix = +ij_java_local_variable_name_suffix = +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = off +ij_java_method_parameters_new_line_after_left_paren = false +ij_java_method_parameters_right_paren_on_new_line = false +ij_java_method_parameters_wrap = off +ij_java_modifier_list_wrap = false +ij_java_multi_catch_types_wrap = normal +ij_java_names_count_to_use_import_on_demand = 3 +ij_java_new_line_after_lparen_in_annotation = false +ij_java_new_line_after_lparen_in_deconstruction_pattern = true +ij_java_new_line_after_lparen_in_record_header = false +ij_java_new_line_when_body_is_presented = false +ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.* +ij_java_parameter_annotation_wrap = off +ij_java_parameter_name_prefix = +ij_java_parameter_name_suffix = +ij_java_parentheses_expression_new_line_after_left_paren = false +ij_java_parentheses_expression_right_paren_on_new_line = false +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_record_components_wrap = normal +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = false +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = false +ij_java_resource_list_right_paren_on_new_line = false +ij_java_resource_list_wrap = off +ij_java_rparen_on_new_line_in_annotation = false +ij_java_rparen_on_new_line_in_deconstruction_pattern = true +ij_java_rparen_on_new_line_in_record_header = false +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = true +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_deconstruction_list = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = true +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_annotation_eq = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_inside_block_braces_when_body_is_present = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = true +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_deconstruction_list = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_record_header = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_static_field_name_prefix = +ij_java_static_field_name_suffix = +ij_java_subclass_name_prefix = +ij_java_subclass_name_suffix = Impl +ij_java_switch_expressions_wrap = normal +ij_java_ternary_operation_signs_on_next_line = false +ij_java_ternary_operation_wrap = off +ij_java_test_name_prefix = +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = off +ij_java_throws_list_wrap = off +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_relative_indents = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = off +ij_java_visibility = public +ij_java_while_brace_force = never +ij_java_while_on_new_line = false +ij_java_wrap_comments = false +ij_java_wrap_first_method_in_call_chain = false +ij_java_wrap_long_lines = false +ij_java_wrap_semicolon_after_call_chain = false + +[*.nbtt] +max_line_length = 150 +ij_continuation_indent_size = 4 +ij_nbtt_keep_indents_on_empty_lines = false +ij_nbtt_space_after_colon = true +ij_nbtt_space_after_comma = true +ij_nbtt_space_before_colon = true +ij_nbtt_space_before_comma = false +ij_nbtt_spaces_within_brackets = false +ij_nbtt_spaces_within_parentheses = false + +[*.properties] +ij_properties_align_group_field_declarations = false +ij_properties_keep_blank_lines = false +ij_properties_key_value_delimiter = equals +ij_properties_spaces_around_key_value_delimiter = false + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.bash,*.zsh,*.sh}] +indent_size = 2 +tab_width = 2 +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false +ij_shell_use_unix_line_separator = true + +[{*.gant,*.groovy,*.gradle,*.gdsl,*.gy}] +ij_groovy_align_group_field_declarations = false +ij_groovy_align_multiline_array_initializer_expression = false +ij_groovy_align_multiline_assignment = false +ij_groovy_align_multiline_binary_operation = false +ij_groovy_align_multiline_chained_methods = false +ij_groovy_align_multiline_extends_list = false +ij_groovy_align_multiline_for = true +ij_groovy_align_multiline_list_or_map = true +ij_groovy_align_multiline_method_parentheses = false +ij_groovy_align_multiline_parameters = true +ij_groovy_align_multiline_parameters_in_calls = false +ij_groovy_align_multiline_resources = true +ij_groovy_align_multiline_ternary_operation = false +ij_groovy_align_multiline_throws_list = false +ij_groovy_align_named_args_in_map = true +ij_groovy_align_throws_keyword = false +ij_groovy_array_initializer_new_line_after_left_brace = false +ij_groovy_array_initializer_right_brace_on_new_line = false +ij_groovy_array_initializer_wrap = off +ij_groovy_assert_statement_wrap = off +ij_groovy_assignment_wrap = off +ij_groovy_binary_operation_wrap = off +ij_groovy_blank_lines_after_class_header = 0 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_block_brace_style = end_of_line +ij_groovy_block_comment_add_space = false +ij_groovy_block_comment_at_first_column = true +ij_groovy_call_parameters_new_line_after_left_paren = false +ij_groovy_call_parameters_right_paren_on_new_line = false +ij_groovy_call_parameters_wrap = off +ij_groovy_catch_on_new_line = false +ij_groovy_class_annotation_wrap = split_into_lines +ij_groovy_class_brace_style = end_of_line +ij_groovy_class_count_to_use_import_on_demand = 5 +ij_groovy_do_while_brace_force = never +ij_groovy_else_on_new_line = false +ij_groovy_enable_groovydoc_formatting = true +ij_groovy_enum_constants_wrap = off +ij_groovy_extends_keyword_wrap = off +ij_groovy_extends_list_wrap = off +ij_groovy_field_annotation_wrap = split_into_lines +ij_groovy_finally_on_new_line = false +ij_groovy_for_brace_force = never +ij_groovy_for_statement_new_line_after_left_paren = false +ij_groovy_for_statement_right_paren_on_new_line = false +ij_groovy_for_statement_wrap = off +ij_groovy_ginq_general_clause_wrap_policy = 2 +ij_groovy_ginq_having_wrap_policy = 1 +ij_groovy_ginq_indent_having_clause = true +ij_groovy_ginq_indent_on_clause = true +ij_groovy_ginq_on_wrap_policy = 1 +ij_groovy_ginq_space_after_keyword = true +ij_groovy_if_brace_force = never +ij_groovy_import_annotation_wrap = 2 +ij_groovy_imports_layout = *, |, javax.**, java.**, |, $* +ij_groovy_indent_case_from_switch = true +ij_groovy_indent_label_blocks = true +ij_groovy_insert_inner_class_imports = false +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 2 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = true +ij_groovy_keep_first_column_comment = true +ij_groovy_keep_indents_on_empty_lines = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_label_indent_absolute = false +ij_groovy_label_indent_size = 0 +ij_groovy_lambda_brace_style = end_of_line +ij_groovy_layout_static_imports_separately = true +ij_groovy_line_comment_add_space = false +ij_groovy_line_comment_add_space_on_reformat = false +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_annotation_wrap = split_into_lines +ij_groovy_method_brace_style = end_of_line +ij_groovy_method_call_chain_wrap = off +ij_groovy_method_parameters_new_line_after_left_paren = false +ij_groovy_method_parameters_right_paren_on_new_line = false +ij_groovy_method_parameters_wrap = off +ij_groovy_modifier_list_wrap = false +ij_groovy_names_count_to_use_import_on_demand = 3 +ij_groovy_packages_to_use_import_on_demand = java.awt.*, javax.swing.* +ij_groovy_parameter_annotation_wrap = off +ij_groovy_parentheses_expression_new_line_after_left_paren = false +ij_groovy_parentheses_expression_right_paren_on_new_line = false +ij_groovy_prefer_parameters_wrap = false +ij_groovy_resource_list_new_line_after_left_paren = false +ij_groovy_resource_list_right_paren_on_new_line = false +ij_groovy_resource_list_wrap = off +ij_groovy_space_after_assert_separator = true +ij_groovy_space_after_colon = true +ij_groovy_space_after_comma = true +ij_groovy_space_after_comma_in_type_arguments = true +ij_groovy_space_after_for_semicolon = true +ij_groovy_space_after_quest = true +ij_groovy_space_after_type_cast = true +ij_groovy_space_before_annotation_parameter_list = false +ij_groovy_space_before_array_initializer_left_brace = false +ij_groovy_space_before_assert_separator = false +ij_groovy_space_before_catch_keyword = true +ij_groovy_space_before_catch_left_brace = true +ij_groovy_space_before_catch_parentheses = true +ij_groovy_space_before_class_left_brace = true +ij_groovy_space_before_closure_left_brace = true +ij_groovy_space_before_colon = true +ij_groovy_space_before_comma = false +ij_groovy_space_before_do_left_brace = true +ij_groovy_space_before_else_keyword = true +ij_groovy_space_before_else_left_brace = true +ij_groovy_space_before_finally_keyword = true +ij_groovy_space_before_finally_left_brace = true +ij_groovy_space_before_for_left_brace = true +ij_groovy_space_before_for_parentheses = true +ij_groovy_space_before_for_semicolon = false +ij_groovy_space_before_if_left_brace = true +ij_groovy_space_before_if_parentheses = true +ij_groovy_space_before_method_call_parentheses = false +ij_groovy_space_before_method_left_brace = true +ij_groovy_space_before_method_parentheses = false +ij_groovy_space_before_quest = true +ij_groovy_space_before_record_parentheses = false +ij_groovy_space_before_switch_left_brace = true +ij_groovy_space_before_switch_parentheses = true +ij_groovy_space_before_synchronized_left_brace = true +ij_groovy_space_before_synchronized_parentheses = true +ij_groovy_space_before_try_left_brace = true +ij_groovy_space_before_try_parentheses = true +ij_groovy_space_before_while_keyword = true +ij_groovy_space_before_while_left_brace = true +ij_groovy_space_before_while_parentheses = true +ij_groovy_space_in_named_argument = true +ij_groovy_space_in_named_argument_before_colon = false +ij_groovy_space_within_empty_array_initializer_braces = false +ij_groovy_space_within_empty_method_call_parentheses = false +ij_groovy_spaces_around_additive_operators = true +ij_groovy_spaces_around_assignment_operators = true +ij_groovy_spaces_around_bitwise_operators = true +ij_groovy_spaces_around_equality_operators = true +ij_groovy_spaces_around_lambda_arrow = true +ij_groovy_spaces_around_logical_operators = true +ij_groovy_spaces_around_multiplicative_operators = true +ij_groovy_spaces_around_regex_operators = true +ij_groovy_spaces_around_relational_operators = true +ij_groovy_spaces_around_shift_operators = true +ij_groovy_spaces_within_annotation_parentheses = false +ij_groovy_spaces_within_array_initializer_braces = false +ij_groovy_spaces_within_braces = true +ij_groovy_spaces_within_brackets = false +ij_groovy_spaces_within_cast_parentheses = false +ij_groovy_spaces_within_catch_parentheses = false +ij_groovy_spaces_within_for_parentheses = false +ij_groovy_spaces_within_gstring_injection_braces = false +ij_groovy_spaces_within_if_parentheses = false +ij_groovy_spaces_within_list_or_map = false +ij_groovy_spaces_within_method_call_parentheses = false +ij_groovy_spaces_within_method_parentheses = false +ij_groovy_spaces_within_parentheses = false +ij_groovy_spaces_within_switch_parentheses = false +ij_groovy_spaces_within_synchronized_parentheses = false +ij_groovy_spaces_within_try_parentheses = false +ij_groovy_spaces_within_tuple_expression = false +ij_groovy_spaces_within_while_parentheses = false +ij_groovy_special_else_if_treatment = true +ij_groovy_ternary_operation_wrap = off +ij_groovy_throws_keyword_wrap = off +ij_groovy_throws_list_wrap = off +ij_groovy_use_flying_geese_braces = false +ij_groovy_use_fq_class_names = false +ij_groovy_use_fq_class_names_in_javadoc = true +ij_groovy_use_relative_indents = false +ij_groovy_use_single_class_imports = true +ij_groovy_variable_annotation_wrap = off +ij_groovy_while_brace_force = never +ij_groovy_while_on_new_line = false +ij_groovy_wrap_chain_calls_after_dot = false +ij_groovy_wrap_long_lines = false + +[{*.htm,*.html,*.sht,*.shtm,*.shtml}] +ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_add_space = false +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p +ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span, pre, textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +ij_xml_block_comment_add_space = false +ij_xml_block_comment_at_first_column = true +ij_xml_keep_blank_lines = 2 +ij_xml_keep_indents_on_empty_lines = false +ij_xml_keep_line_breaks = true +ij_xml_keep_line_breaks_in_text = true +ij_xml_keep_whitespaces = false +ij_xml_keep_whitespaces_around_cdata = preserve +ij_xml_keep_whitespaces_inside_cdata = false +ij_xml_line_comment_at_first_column = true +ij_xml_space_after_tag_name = false +ij_xml_space_around_equals_in_attribute = false +ij_xml_space_inside_empty_tag = false +ij_xml_text_wrap = normal + +[{*.kt,*.kts}] +ij_kotlin_align_in_columns_case_branch = false +ij_kotlin_align_multiline_binary_operation = false +ij_kotlin_align_multiline_extends_list = false +ij_kotlin_align_multiline_method_parentheses = false +ij_kotlin_align_multiline_parameters = true +ij_kotlin_align_multiline_parameters_in_calls = false +ij_kotlin_allow_trailing_comma = false +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_assignment_wrap = off +ij_kotlin_blank_lines_after_class_header = 0 +ij_kotlin_blank_lines_around_block_when_branches = 0 +ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 +ij_kotlin_block_comment_add_space = false +ij_kotlin_block_comment_at_first_column = true +ij_kotlin_call_parameters_new_line_after_left_paren = false +ij_kotlin_call_parameters_right_paren_on_new_line = false +ij_kotlin_call_parameters_wrap = off +ij_kotlin_catch_on_new_line = false +ij_kotlin_class_annotation_wrap = split_into_lines +ij_kotlin_continuation_indent_for_chained_calls = true +ij_kotlin_continuation_indent_for_expression_bodies = true +ij_kotlin_continuation_indent_in_argument_lists = true +ij_kotlin_continuation_indent_in_elvis = true +ij_kotlin_continuation_indent_in_if_conditions = true +ij_kotlin_continuation_indent_in_parameter_lists = true +ij_kotlin_continuation_indent_in_supertype_lists = true +ij_kotlin_else_on_new_line = false +ij_kotlin_enum_constants_wrap = off +ij_kotlin_extends_list_wrap = off +ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_finally_on_new_line = false +ij_kotlin_if_rparen_on_new_line = false +ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ +ij_kotlin_indent_before_arrow_on_new_line = true +ij_kotlin_insert_whitespaces_in_simple_one_line_method = true +ij_kotlin_keep_blank_lines_before_right_brace = 2 +ij_kotlin_keep_blank_lines_in_code = 2 +ij_kotlin_keep_blank_lines_in_declarations = 2 +ij_kotlin_keep_first_column_comment = true +ij_kotlin_keep_indents_on_empty_lines = false +ij_kotlin_keep_line_breaks = true +ij_kotlin_lbrace_on_next_line = false +ij_kotlin_line_break_after_multiline_when_entry = true +ij_kotlin_line_comment_add_space = false +ij_kotlin_line_comment_add_space_on_reformat = false +ij_kotlin_line_comment_at_first_column = true +ij_kotlin_method_annotation_wrap = split_into_lines +ij_kotlin_method_call_chain_wrap = off +ij_kotlin_method_parameters_new_line_after_left_paren = false +ij_kotlin_method_parameters_right_paren_on_new_line = false +ij_kotlin_method_parameters_wrap = off +ij_kotlin_name_count_to_use_star_import = 5 +ij_kotlin_name_count_to_use_star_import_for_members = 3 +ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.** +ij_kotlin_parameter_annotation_wrap = off +ij_kotlin_space_after_comma = true +ij_kotlin_space_after_extend_colon = true +ij_kotlin_space_after_type_colon = true +ij_kotlin_space_before_catch_parentheses = true +ij_kotlin_space_before_comma = false +ij_kotlin_space_before_extend_colon = true +ij_kotlin_space_before_for_parentheses = true +ij_kotlin_space_before_if_parentheses = true +ij_kotlin_space_before_lambda_arrow = true +ij_kotlin_space_before_type_colon = false +ij_kotlin_space_before_when_parentheses = true +ij_kotlin_space_before_while_parentheses = true +ij_kotlin_spaces_around_additive_operators = true +ij_kotlin_spaces_around_assignment_operators = true +ij_kotlin_spaces_around_elvis = true +ij_kotlin_spaces_around_equality_operators = true +ij_kotlin_spaces_around_function_type_arrow = true +ij_kotlin_spaces_around_logical_operators = true +ij_kotlin_spaces_around_multiplicative_operators = true +ij_kotlin_spaces_around_range = false +ij_kotlin_spaces_around_relational_operators = true +ij_kotlin_spaces_around_unary_operator = false +ij_kotlin_spaces_around_when_arrow = true +ij_kotlin_variable_annotation_wrap = off +ij_kotlin_while_on_new_line = false +ij_kotlin_wrap_elvis_expressions = 1 +ij_kotlin_wrap_expression_body_functions = 0 +ij_kotlin_wrap_first_method_in_call_chain = false + +[{*.yml,*.yaml}] +indent_size = 2 +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true + +[{*.har,*.json,*.jsonc,*.png.mcmeta,mcmod.info,pack.mcmeta}] +indent_size = 2 +ij_json_array_wrapping = split_into_lines +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_keep_trailing_comma = false +ij_json_object_wrapping = split_into_lines +ij_json_property_alignment = do_not_align +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = true +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.markdown,*.md}] +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true +ij_markdown_format_tables = true +ij_markdown_insert_quote_arrows_on_wrap = true +ij_markdown_keep_indents_on_empty_lines = false +ij_markdown_keep_line_breaks_inside_text_blocks = true +ij_markdown_max_lines_around_block_elements = 1 +ij_markdown_max_lines_around_header = 1 +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_min_lines_around_header = 1 +ij_markdown_min_lines_between_paragraphs = 1 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock,uv.lock}] +ij_toml_keep_indents_on_empty_lines = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e5d7f00 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,131 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Text files where line endings should be preserved +*.patch -text + +# Enable syntax highlighting for files with `.gitattributes` extensions. +*.gitattributes linguist-language=gitattributes +*.gitattributes linguist-detectable=true +*.gitattributes linguist-documentation=false + +# Exclude files from exporting +.gitattributes export-ignore +.gitignore export-ignore +.gitkeep export-ignore + +# Documents +*.md text diff=markdown linguist-detectable +*.mdx text diff=markdown +*.markdown text diff=markdown +*.mdtxt text +*.mdtext text +*.txt text +*.adoc text +*.csv text eol=crlf +*.tab text +*.tsv text + +# Documentation +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +*COPYRIGHT* text +INSTALL text +LICENSE text +NEWS text +*README* text +TODO text + +# Graphics +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.svg binary +*.eps binary + +# Scripts +*.bash text eol=lf +*.fish text eol=lf +*.ksh text eol=lf +*.sh text eol=lf +*.zsh text eol=lf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Serialisation +*.json text +*.toml text +*.xml text +*.yaml text +*.yml text + +# Archives +*.7z binary +*.gz binary +*.rar binary +*.tar binary +*.xz binary +*.zip binary + +# Binary files +*.exe binary +*.dll binary +*.so binary +*.jar binary +*.class binary +*.ear binary +*.war binary +*.jks binary + +# Text files normalization (Convert CRLF => LF) +*.gitattributes text +.gitignore text +*.css text diff=css +*.scss text diff=css +*.htm text diff=html +*.html text diff=html +*.js text +*.properties text + +# Configs +*.cnf text +*.conf text +*.config text +*.cfg text eol=lf +.editorconfig text +.env text +.gitattributes text +.gitconfig text +*.lock text -diff +makefile text + +# Audio +*.ogg binary + +# Java sources +*.java text diff=java +*.kt text diff=kotlin +*.kts text diff=kotlin +*.groovy text diff=java +*.scala text diff=java + +# Gradle +*.gradle text diff=java +*.gradle.kts text diff=kotlin +gradlew text eol=lf + +# Minecraft modding +*.mixins.json text +*.accesswidener text +*.mcmeta text + +# Disable autocrlf on generated files, they always generate with LF +# Add any extra files or paths here to make git stop saying they +# are changed when only line endings change. +src/generated/**/.cache/cache text eol=lf +src/generated/**/*.json text eol=lf diff --git a/.gitignore b/.gitignore index 3c37caf..42fa64f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,109 @@ -# User-specific stuff -.idea/ - +# ============================ +# IDE specific files +# ============================ + +# Eclipse +bin/ +*.launch +.settings/ +.metadata/ +.classpath +.project +eclipse + +# IntelliJ IDEA +.idea +!.idea/scopes *.iml *.ipr *.iws - -# IntelliJ out/ # mpeltonen/sbt-idea plugin .idea_modules/ -# JIRA plugin -atlassian-ide-plugin.xml +# ============================ +# Build system & Gradle +# ============================ -# Compiled class file -*.class +# Gradle directories and files +.gradle/ +build/ +**/build/ +!src/**/build/ +local.properties +# Avoid ignore Gradle wrapper files +!gradle-wrapper.jar +!gradle-wrapper.properties +# Ignore Gradle GUI config +gradle-app.setting +# Cache of project +.gradletasknamecache + +# ============================ +# Minecraft mod development +# ============================ + +# Minecraft client/server directories generated by the modloader +run/ +runs/ -# Log file +# Minecraft crash reports and logs +crash-reports/ +logs/ *.log -# BlueJ files -*.ctxt +# ============================ +# System files +# ============================ + +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Windows +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# ============================ +# Additional common ignores +# ============================ + +# JVM crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Compiled Java classes +*.class -# Package Files # +# Package files *.jar *.war *.nar @@ -31,28 +112,20 @@ atlassian-ide-plugin.xml *.tar.gz *.rar -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +# JIRA plugin +atlassian-ide-plugin.xml + +# BlueJ files +*.ctxt *~ # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - # .nfs files are created when an open file is removed but is still being accessed .nfs* -# General -.DS_Store -.AppleDouble -.LSOverride - # Icon must end with two \r Icon @@ -74,45 +147,3 @@ Icon Network Trash Folder Temporary Items .apdisk - -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -.gradle -build/ - -# Ignore Gradle GUI config -gradle-app.setting - -# Cache of project -.gradletasknamecache - -**/build/ - -# Common working directory -run/ - -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 0000000..0a2aabd --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() + gradlePluginPortal() +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..61466c0 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,12 @@ +pluginManagement { + repositories { + gradlePluginPortal() + } +} + +plugins { + // https://github.com/radoslaw-panuszewski/typesafe-conventions-gradle-plugin + id("dev.panuszewski.typesafe-conventions") version "0.7.3" +} + +rootProject.name = "build-logic" \ No newline at end of file diff --git a/build-logic/src/main/kotlin/build-extensions.kt b/build-logic/src/main/kotlin/build-extensions.kt new file mode 100644 index 0000000..25f5f2b --- /dev/null +++ b/build-logic/src/main/kotlin/build-extensions.kt @@ -0,0 +1,114 @@ +import org.gradle.api.Project +import org.gradle.language.jvm.tasks.ProcessResources + +/** + * Retrieves the mod data from the project. + * + * @return the mod data + */ +val Project.mod: ModData get() = ModData(this) + +/** + * Mod links. + * + * @property modrinth the link to the mod on Modrinth + * @property curseforge the link to the mod on Curseforge + */ +data class ModLinks( + val modrinth: String, + val curseforge: String, +) + +/** + * Retrieves a property value from the project based on the provided key. + * + * @param key the property key to look up in the project's properties. + * @return the value of the property as a string, or null if the property does not exist. + */ +fun Project.prop(key: String): String? = findProperty(key)?.toString() + +/** + * Retrieves common project. + * + * @return the common project + */ +val Project.common get() = rootProject.project(":common") + +@JvmInline +value class ModData(private val project: Project) { + val id: String get() = modProp("id") + val name: String get() = modProp("name") + val version: String get() = modProp("version") + val group: String get() = modProp("group") + val archivesBaseName: String get() = modProp("archives_base_name") + val author: String get() = modProp("author") + val description: String get() = modProp("description") + val license: String get() = modProp("license") + val credits: String get() = modProp("credits") + val homepage: String get() = modProp("homepage") + val sources: String get() = modProp("sources") + val issues: String get() = modProp("issues") + val links: ModLinks + get() = ModLinks( + modrinth = linkProp("modrinth"), + curseforge = linkProp("curseforge"), + ) + + /** + * Retrieves a mod property value from the project based on the provided key. + * + * @param key the mod property key to look up in the project's properties. + * @return the value of the mod property as a string. + */ + private fun modPropOrNull(key: String) = project.prop("mod.$key") + private fun modProp(key: String) = requireNotNull(modPropOrNull(key)) { "Missing 'mod.$key'" } + + /** + * Retrieves a link property value from the project based on the provided key. + * + * @param key the link property key to look up in the project's properties. + * @return the value of the link property as a string. + */ + private fun linkPropOrNull(key: String) = modPropOrNull("links.$key") + private fun linkProp(key: String) = requireNotNull(linkPropOrNull(key)) { "Missing 'links.$key'" } + + // private fun depOrNull(key: String) = project.prop("deps.$key") + // private fun dep(key: String) = requireNotNull(depOrNull(key)) { "Missing 'deps.$key'" } +} + +/** + * Returns a properties map transformed based on the file type. + * Line breaks are escaped to '\\n' if the file is JSON. + * + * @param filePath The path of the file to be processed (e.g. "file.json"). + * @param props The original properties map with literal line breaks. + * @return The properties map appropriate for the file type. + */ +fun getTransformedProps(filePath: String, props: Map): Map { + val jsonExtensions = listOf("json", "mcmeta") + + return when { + jsonExtensions.any { filePath.endsWith(it) } -> { + props.mapValues { (_, value) -> + if (value is String) value.replace("\n", "\\\\n") else value + } + } + else -> props + } +} + +/** + * Applies a set of properties to a `ProcessResources` task, based on the project's configuration + * and additional resource files. + * + * @param properties a map of properties to be applied to the resource files. + * @param files an iterable collection of file paths to which the properties will be applied. + */ +fun ProcessResources.applyProperties(properties: Map, files: Iterable) { + filesMatching(files) { + val props = getTransformedProps(relativePath.pathString, properties) + expand(props) + } + + inputs.properties(properties) +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/multiloader-common.gradle.kts b/build-logic/src/main/kotlin/multiloader-common.gradle.kts new file mode 100644 index 0000000..11c8b3f --- /dev/null +++ b/build-logic/src/main/kotlin/multiloader-common.gradle.kts @@ -0,0 +1,124 @@ +plugins { + idea + `java-library` + `maven-publish` +} + +base { + archivesName.set(mod.archivesBaseName) +} + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get().toInt())) + withSourcesJar() + withJavadocJar() +} + +repositories { + // https://docs.gradle.org/current/userguide/declaring_repositories.html#declaring_content_exclusively_found_in_one_repository + exclusiveContent { + forRepositories( + maven("https://maven.parchmentmc.org") { name = "ParchmentMC" }, + maven("https://maven.neoforged.net/releases") { name = "NeoForge" } + ) + filter { includeGroup("org.parchmentmc.data") } + } + exclusiveContent { + forRepository { maven("https://maven.fabricmc.net") { name = "Fabric" } } + filter { includeGroup("net.fabricmc") } + } + exclusiveContent { + forRepository { maven("https://repo.spongepowered.org/repository/maven-public") { name = "Sponge" } } + filter { includeGroupAndSubgroups("org.spongepowered") } + } + exclusiveContent { + forRepository { maven("https://maven.terraformersmc.com") { name = "TerraformersMC" } } + filter { includeGroup("com.terraformersmc") } + } + maven("https://maven.blamejared.com") { name = "BlameJared" } + + mavenCentral() +} + +// Declare capabilities on the outgoing configurations. +// Read more about capabilities here: https://docs.gradle.org/current/userguide/component_capabilities.html#sec:declaring-additional-capabilities-for-a-local-component +listOf("apiElements", "runtimeElements", "sourcesElements", "javadocElements").forEach { variant -> + configurations[variant].outgoing { + capability("${mod.group}:${project.name}:${mod.version}") + capability("${mod.group}:${base.archivesName.get()}:${mod.version}") + capability("${mod.group}:${mod.id}-${project.name}-${libs.versions.minecraft.asProvider().get()}:${mod.version}") + capability("${mod.group}:${mod.id}:${mod.version}") + } + + publishing.publications.containerWithType(MavenPublication::class.java).configureEach { + suppressPomMetadataWarningsFor(variant) + } +} + +tasks { + named("sourcesJar") { + from(rootProject.file("LICENSE")) { + rename { "${it}_${mod.id}" } + } + } + + named("jar") { + from(rootProject.file("LICENSE")) { + rename { "${it}_${mod.id}" } + } + + manifest.attributes( + mapOf( + "Specification-Title" to mod.name, "Specification-Vendor" to mod.author, + "Specification-Version" to common.mod.version, // project.jar.archiveVersion, + "Implementation-Title" to project.name, "Implementation-Vendor" to mod.author, + "Implementation-Version" to common.mod.version, // project.jar.archiveVersion, + "Built-On-Minecraft" to libs.versions.minecraft.asProvider().get() + ) + ) + } + + named("processResources") { + val expandProps = mapOf( + "modGroup" to mod.group, + "modId" to mod.id, + "modName" to mod.name, + "modVersion" to mod.version, + "modAuthor" to mod.author, + "modDescription" to mod.description, + "modLicense" to mod.license, + "modCredits" to mod.credits, + "modHomepage" to mod.homepage, + "modSources" to mod.sources, + "modIssues" to mod.issues, + "modLinksModrinth" to mod.links.modrinth, + "modLinksCurseforge" to mod.links.curseforge, + "javaVersion" to libs.versions.java.get(), + "minecraftVersion" to libs.versions.minecraft.asProvider().get(), + "minecraftVersionRange" to libs.versions.minecraft.range.get(), + "fabricVersion" to libs.versions.fabric.api.get(), + "fabricLoaderVersion" to libs.versions.fabric.loader.get(), + "neoforgeVersion" to libs.versions.neoforge.asProvider().get(), + "neoforgeVersionRange" to libs.versions.neoforge.range.get(), + "neoforgeLoaderVersionRange" to libs.versions.neoforge.loader.range.get(), + ) + + val files = listOf("pack.mcmeta", "fabric.mod.json", "*.mixins.json", "META-INF/mods.toml", "META-INF/neoforge.mods.toml") + applyProperties(expandProps, files) + } +} + +publishing { + publications { + register("mavenJava") { + artifactId = base.archivesName.get() + from(components["java"]) + } + } + repositories { + val localMavenUrl = System.getenv("local_maven_url") + if (!localMavenUrl.isNullOrBlank()) { + maven { url = uri(localMavenUrl) } + } + } +} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/multiloader-loader.gradle.kts b/build-logic/src/main/kotlin/multiloader-loader.gradle.kts new file mode 100644 index 0000000..1855af0 --- /dev/null +++ b/build-logic/src/main/kotlin/multiloader-loader.gradle.kts @@ -0,0 +1,46 @@ +plugins { + idea + `java-library` + id("multiloader-common") +} + +val commonJava: Configuration by configurations.creating { + isCanBeResolved = true +} +val commonResources: Configuration by configurations.creating { + isCanBeResolved = true +} + +dependencies { + compileOnly(project(path = common.path)) { + capabilities { + requireCapability("${mod.group}:${mod.id}") + } + } + commonJava(project(path = common.path, configuration = commonJava.name)) + commonResources(project(path = common.path, configuration = commonResources.name)) +} + +tasks { + named("compileJava") { + dependsOn(commonJava) + source(commonJava) + } + + processResources { + dependsOn(commonResources) + from(commonResources) + } + + named("javadoc").configure { + dependsOn(commonJava) + source(commonJava) + } + + named("sourcesJar") { + dependsOn(commonJava) + from(commonJava) + dependsOn(commonResources) + from(commonResources) + } +} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 4178be7..0000000 --- a/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' - id 'maven-publish' -} - -loom { - accessWidenerPath = file("src/main/resources/languagereload.accesswidener") -} - -version = "${project.mod_version}+${project.minecraft_version}" -group = project.maven_group - -repositories { - maven { url "https://maven.terraformersmc.com" } -} - -dependencies { - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - modImplementation fabricApi.module("fabric-resource-loader-v0", project.fabric_version) - - modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}" - modRuntimeOnly fabricApi.module("fabric-screen-api-v1", project.fabric_version) - modRuntimeOnly fabricApi.module("fabric-key-binding-api-v1", project.fabric_version) - modRuntimeOnly fabricApi.module("fabric-lifecycle-events-v1", project.fabric_version) -} - -processResources { - inputs.property "version", project.version - filteringCharset "UTF-8" - - filesMatching("fabric.mod.json") { - expand "version": project.version - } -} - -tasks.withType(JavaCompile).configureEach { - it.options.encoding = "UTF-8" -} - -java { - archivesBaseName = project.archives_base_name - withSourcesJar() -} - -jar { - from("LICENSE") -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..aa51271 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + alias(libs.plugins.loom) apply false + alias(libs.plugins.moddev) apply false +} diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 0000000..bcc8f63 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,50 @@ +plugins { + id("multiloader-common") + alias(libs.plugins.moddev) +} + +neoForge { + neoFormVersion = libs.versions.neoform.get() + // Automatically enable AccessTransformers if the file exists + val accessTransformer = file("src/main/resources/META-INF/accesstransformer.cfg") + if (accessTransformer.exists()) { + accessTransformers.from(accessTransformer.absolutePath) + } + parchment { + minecraftVersion = libs.versions.parchmentmc.get() + mappingsVersion = libs.versions.parchment.get() + } + + validateAccessTransformers.set(true) +} + +dependencies { + compileOnly(libs.mixin) + // Fabric and NeoForge both bundle MixinExtras, so it is safe to use it in common + libs.mixinextras.common.get().let { + compileOnly(it) + annotationProcessor(it) + } +} + +val commonJava: Configuration by configurations.creating { + isCanBeResolved = false + isCanBeConsumed = true +} + +val commonResources: Configuration by configurations.creating { + isCanBeResolved = false + isCanBeConsumed = true +} + +artifacts { + afterEvaluate { + val mainSourceSet = sourceSets.main.get() + mainSourceSet.java.sourceDirectories.files.forEach { + add(commonJava.name, it) + } + mainSourceSet.resources.sourceDirectories.files.forEach { + add(commonResources.name, it) + } + } +} diff --git a/src/main/java/jerozgen/languagereload/LanguageReload.java b/common/src/main/java/jerozgen/languagereload/LanguageReload.java similarity index 53% rename from src/main/java/jerozgen/languagereload/LanguageReload.java rename to common/src/main/java/jerozgen/languagereload/LanguageReload.java index e0a3d6e..d43a3fa 100644 --- a/src/main/java/jerozgen/languagereload/LanguageReload.java +++ b/common/src/main/java/jerozgen/languagereload/LanguageReload.java @@ -3,21 +3,19 @@ import jerozgen.languagereload.access.IAdvancementsScreen; import jerozgen.languagereload.config.Config; import jerozgen.languagereload.mixin.*; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.block.entity.SignBlockEntity; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.advancement.AdvancementsScreen; -import net.minecraft.client.gui.screen.ingame.BookScreen; -import net.minecraft.entity.decoration.DisplayEntity; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.advancements.AdvancementsScreen; +import net.minecraft.client.gui.screens.inventory.BookViewScreen; +import net.minecraft.world.entity.Display; +import net.minecraft.world.level.block.entity.SignBlockEntity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.LinkedList; -@Environment(EnvType.CLIENT) public class LanguageReload { - public static final Logger LOGGER = LogManager.getLogger("Language Reload"); + public static final String MOD_NAME = "Language Reload"; + public static final Logger LOGGER = LogManager.getLogger(MOD_NAME); public static final String MOD_ID = "languagereload"; public static final String NO_LANGUAGE = "*"; @@ -25,26 +23,26 @@ public class LanguageReload { public static boolean shouldSetSystemLanguage = false; public static void reloadLanguages() { - var client = MinecraftClient.getInstance(); + var client = Minecraft.getInstance(); // Reload language manager - client.getLanguageManager().reload(client.getResourceManager()); + client.getLanguageManager().onResourceManagerReload(client.getResourceManager()); // Update window title and chat - client.updateWindowTitle(); - client.inGameHud.getChatHud().reset(); + client.updateTitle(); + client.gui.getChat().rescaleChat(); // Update book and advancements screens - if (client.currentScreen instanceof BookScreen bookScreen) { - ((BookScreenAccessor) bookScreen).languagereload_setCachedPageIndex(-1); - } else if (client.currentScreen instanceof AdvancementsScreen advancementsScreen) { + if (client.screen instanceof BookViewScreen bookScreen) { + ((BookViewScreenAccessor) bookScreen).languagereload_setCachedPage(-1); + } else if (client.screen instanceof AdvancementsScreen advancementsScreen) { ((IAdvancementsScreen) advancementsScreen).languagereload_recreateWidgets(); } - if (client.world != null) { + if (client.level != null) { // Update signs - var chunkManager = (ClientChunkManagerAccessor) client.world.getChunkManager(); - var chunks = ((ClientChunkMapAccessor) chunkManager.languagereload_getChunks()).languagereload_getChunks(); + var chunkManager = (ClientChunkCacheAccessor) client.level.getChunkSource(); + var chunks = ((ClientChunkCacheStorageAccessor)(Object) chunkManager.languagereload_getChunks()).languagereload_getChunks(); for (int i = 0; i < chunks.length(); i++) { var chunk = chunks.get(i); if (chunk == null) continue; @@ -56,32 +54,32 @@ public static void reloadLanguages() { } // Update text displays - for (var entity : client.world.getEntities()) { - if (entity instanceof DisplayEntity.TextDisplayEntity textDisplay) { - ((TextDisplayEntityAccessor) textDisplay).languagereload_setTextLines(null); + for (var entity : client.level.entitiesForRendering()) { + if (entity instanceof Display.TextDisplay textDisplay) { + ((TextDisplayAccessor) textDisplay).languagereload_setTextLines(null); } } } } public static void setLanguage(String language, LinkedList fallbacks) { - var client = MinecraftClient.getInstance(); + var client = Minecraft.getInstance(); var languageManager = client.getLanguageManager(); var config = Config.getInstance(); - var languageIsSame = languageManager.getLanguage().equals(language); + var languageIsSame = languageManager.getSelected().equals(language); var fallbacksAreSame = config.fallbacks.equals(fallbacks); if (languageIsSame && fallbacksAreSame) return; - config.previousLanguage = languageManager.getLanguage(); + config.previousLanguage = languageManager.getSelected(); config.previousFallbacks = config.fallbacks; config.language = language; config.fallbacks = fallbacks; Config.save(); - languageManager.setLanguage(language); - client.options.language = language; - client.options.write(); + languageManager.setSelected(language); + client.options.languageCode = language; + client.options.save(); reloadLanguages(); } diff --git a/src/main/java/jerozgen/languagereload/access/IAdvancementsScreen.java b/common/src/main/java/jerozgen/languagereload/access/IAdvancementsScreen.java similarity index 55% rename from src/main/java/jerozgen/languagereload/access/IAdvancementsScreen.java rename to common/src/main/java/jerozgen/languagereload/access/IAdvancementsScreen.java index 3b1959b..a34dc31 100644 --- a/src/main/java/jerozgen/languagereload/access/IAdvancementsScreen.java +++ b/common/src/main/java/jerozgen/languagereload/access/IAdvancementsScreen.java @@ -1,9 +1,5 @@ package jerozgen.languagereload.access; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; - -@Environment(EnvType.CLIENT) public interface IAdvancementsScreen { void languagereload_recreateWidgets(); } diff --git a/src/main/java/jerozgen/languagereload/access/IAdvancementsTab.java b/common/src/main/java/jerozgen/languagereload/access/IAdvancementsTab.java similarity index 54% rename from src/main/java/jerozgen/languagereload/access/IAdvancementsTab.java rename to common/src/main/java/jerozgen/languagereload/access/IAdvancementsTab.java index 0081e62..1487a70 100644 --- a/src/main/java/jerozgen/languagereload/access/IAdvancementsTab.java +++ b/common/src/main/java/jerozgen/languagereload/access/IAdvancementsTab.java @@ -1,9 +1,5 @@ package jerozgen.languagereload.access; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; - -@Environment(EnvType.CLIENT) public interface IAdvancementsTab { void languagereload_recreateWidgets(); } diff --git a/common/src/main/java/jerozgen/languagereload/access/IClientLanguage.java b/common/src/main/java/jerozgen/languagereload/access/IClientLanguage.java new file mode 100644 index 0000000..c701e99 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/access/IClientLanguage.java @@ -0,0 +1,9 @@ +package jerozgen.languagereload.access; + +public interface IClientLanguage { + String languagereload_get(String key); + + String languagereload_getTargetLanguage(); + + void languagereload_setTargetLanguage(String value); +} diff --git a/common/src/main/java/jerozgen/languagereload/access/ILanguage.java b/common/src/main/java/jerozgen/languagereload/access/ILanguage.java new file mode 100644 index 0000000..5ad43fc --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/access/ILanguage.java @@ -0,0 +1,9 @@ +package jerozgen.languagereload.access; + +import net.minecraft.client.resources.language.ClientLanguage; + +public interface ILanguage { + void languagereload_setClientLanguage(ClientLanguage clientLanguage); + + ClientLanguage languagereload_getClientLanguage(); +} diff --git a/src/main/java/jerozgen/languagereload/access/ILanguageOptionsScreen.java b/common/src/main/java/jerozgen/languagereload/access/ILanguageSelectScreen.java similarity index 65% rename from src/main/java/jerozgen/languagereload/access/ILanguageOptionsScreen.java rename to common/src/main/java/jerozgen/languagereload/access/ILanguageSelectScreen.java index 6bd62e8..5dc3d22 100644 --- a/src/main/java/jerozgen/languagereload/access/ILanguageOptionsScreen.java +++ b/common/src/main/java/jerozgen/languagereload/access/ILanguageSelectScreen.java @@ -2,11 +2,8 @@ import jerozgen.languagereload.gui.LanguageEntry; import jerozgen.languagereload.gui.LanguageListWidget; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -@Environment(EnvType.CLIENT) -public interface ILanguageOptionsScreen { +public interface ILanguageSelectScreen { void languagereload_focusList(LanguageListWidget list); void languagereload_focusEntry(LanguageEntry entry); diff --git a/src/main/java/jerozgen/languagereload/config/Config.java b/common/src/main/java/jerozgen/languagereload/config/Config.java similarity index 81% rename from src/main/java/jerozgen/languagereload/config/Config.java rename to common/src/main/java/jerozgen/languagereload/config/Config.java index 6141706..9d6430c 100644 --- a/src/main/java/jerozgen/languagereload/config/Config.java +++ b/common/src/main/java/jerozgen/languagereload/config/Config.java @@ -3,20 +3,17 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import jerozgen.languagereload.LanguageReload; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.util.Language; +import jerozgen.languagereload.platform.Services; +import net.minecraft.locale.Language; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.LinkedList; -@Environment(EnvType.CLIENT) public class Config { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - private static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve(LanguageReload.MOD_ID + ".json"); + private static final Path PATH = Services.PLATFORM.getConfigDir().resolve(LanguageReload.MOD_ID + ".json"); private static Config INSTANCE; @@ -71,14 +68,14 @@ private static void migrateToVersion1() { INSTANCE.version = 1; if (!INSTANCE.language.isEmpty() - && !INSTANCE.language.equals(Language.DEFAULT_LANGUAGE) - && !INSTANCE.fallbacks.contains(Language.DEFAULT_LANGUAGE)) - INSTANCE.fallbacks.add(Language.DEFAULT_LANGUAGE); + && !INSTANCE.language.equals(Language.DEFAULT) + && !INSTANCE.fallbacks.contains(Language.DEFAULT)) + INSTANCE.fallbacks.add(Language.DEFAULT); if (!INSTANCE.previousLanguage.isEmpty() - && !INSTANCE.previousLanguage.equals(Language.DEFAULT_LANGUAGE) - && !INSTANCE.previousFallbacks.contains(Language.DEFAULT_LANGUAGE)) - INSTANCE.previousFallbacks.add(Language.DEFAULT_LANGUAGE); + && !INSTANCE.previousLanguage.equals(Language.DEFAULT) + && !INSTANCE.previousFallbacks.contains(Language.DEFAULT)) + INSTANCE.previousFallbacks.add(Language.DEFAULT); save(); } diff --git a/common/src/main/java/jerozgen/languagereload/config/ConfigScreen.java b/common/src/main/java/jerozgen/languagereload/config/ConfigScreen.java new file mode 100644 index 0000000..ef2bcaa --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/config/ConfigScreen.java @@ -0,0 +1,51 @@ +package jerozgen.languagereload.config; + +import jerozgen.languagereload.LanguageReload; +import net.minecraft.client.Minecraft; +import net.minecraft.client.OptionInstance; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.options.OptionsSubScreen; +import net.minecraft.network.chat.Component; + +public class ConfigScreen extends OptionsSubScreen { + private static final OptionInstance MULTILINGUAL_SEARCH = OptionInstance.createBoolean( + "options.languagereload.multilingualItemSearch", + OptionInstance.cachedConstantTooltip( + Component.translatable("options.languagereload.multilingualItemSearch.tooltip")), + true, + value -> { + Config.getInstance().multilingualItemSearch = value; + Config.save(); + LanguageReload.reloadLanguages(); + }); + + private static final OptionInstance REMOVABLE_DEFAULT_LANGUAGE = OptionInstance.createBoolean( + "options.languagereload.removableDefaultLanguage", + (value) -> value + ? Tooltip.create( + Component.translatable("options.languagereload.removableDefaultLanguage.removable.tooltip")) + : Tooltip.create( + Component.translatable("options.languagereload.removableDefaultLanguage.fixed.tooltip")), + (__, value) -> value + ? Component.translatable("options.languagereload.removableDefaultLanguage.removable") + : Component.translatable("options.languagereload.removableDefaultLanguage.fixed"), + false, + value -> { + Config.getInstance().removableDefaultLanguage = value; + Config.save(); + }); + + public ConfigScreen(Screen parent) { + super(parent, Minecraft.getInstance().options, Component.translatable("options.languagereload.title")); + MULTILINGUAL_SEARCH.set(Config.getInstance().multilingualItemSearch); + REMOVABLE_DEFAULT_LANGUAGE.set(Config.getInstance().removableDefaultLanguage); + } + + @Override + protected void addOptions() { + if (list != null) { + list.addSmall(MULTILINGUAL_SEARCH, REMOVABLE_DEFAULT_LANGUAGE); + } + } +} diff --git a/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java b/common/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java similarity index 52% rename from src/main/java/jerozgen/languagereload/gui/LanguageEntry.java rename to common/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java index 5d077cc..304b4be 100644 --- a/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java +++ b/common/src/main/java/jerozgen/languagereload/gui/LanguageEntry.java @@ -1,74 +1,70 @@ package jerozgen.languagereload.gui; import jerozgen.languagereload.LanguageReload; -import jerozgen.languagereload.access.ILanguageOptionsScreen; +import jerozgen.languagereload.access.ILanguageSelectScreen; import jerozgen.languagereload.config.Config; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ButtonTextures; -import net.minecraft.client.gui.tooltip.TooltipPositioner; -import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.ClickableWidget; -import net.minecraft.client.gui.widget.TexturedButtonWidget; -import net.minecraft.client.resource.language.LanguageDefinition; -import net.minecraft.text.Text; -import net.minecraft.util.Colors; -import net.minecraft.util.Identifier; -import net.minecraft.util.Language; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.*; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner; +import net.minecraft.client.resources.language.LanguageInfo; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.CommonColors; +import org.jetbrains.annotations.NotNull; import org.joml.Vector2i; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -public class LanguageEntry extends AlwaysSelectedEntryListWidget.Entry { - private static final Text DEFAULT_LANGUAGE_TOOLTIP = Text.translatable("language.default.tooltip"); +public class LanguageEntry extends ObjectSelectionList.Entry { + private static final Component DEFAULT_LANGUAGE_TOOLTIP = Component.translatable("language.default.tooltip"); - private static final ButtonTextures ADD_TEXTURES = new ButtonTextures( - Identifier.of(LanguageReload.MOD_ID, "language_selection/add"), - Identifier.of(LanguageReload.MOD_ID, "language_selection/add_highlighted")); - private static final ButtonTextures REMOVE_TEXTURES = new ButtonTextures( - Identifier.of(LanguageReload.MOD_ID, "language_selection/remove"), - Identifier.of(LanguageReload.MOD_ID, "language_selection/remove_highlighted")); - private static final ButtonTextures MOVE_UP_TEXTURES = new ButtonTextures( - Identifier.of(LanguageReload.MOD_ID, "language_selection/move_up"), - Identifier.of(LanguageReload.MOD_ID, "language_selection/move_up_highlighted")); - private static final ButtonTextures MOVE_DOWN_TEXTURES = new ButtonTextures( - Identifier.of(LanguageReload.MOD_ID, "language_selection/move_down"), - Identifier.of(LanguageReload.MOD_ID, "language_selection/move_down_highlighted")); + private static final WidgetSprites ADD_TEXTURES = new WidgetSprites( + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/add"), + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/add_highlighted")); + private static final WidgetSprites REMOVE_TEXTURES = new WidgetSprites( + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/remove"), + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/remove_highlighted")); + private static final WidgetSprites MOVE_UP_TEXTURES = new WidgetSprites( + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/move_up"), + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/move_up_highlighted")); + private static final WidgetSprites MOVE_DOWN_TEXTURES = new WidgetSprites( + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/move_down"), + ResourceLocation.fromNamespaceAndPath(LanguageReload.MOD_ID, "language_selection/move_down_highlighted")); - private final MinecraftClient client = MinecraftClient.getInstance(); + private final Minecraft client = Minecraft.getInstance(); private final String code; - private final LanguageDefinition language; + private final LanguageInfo language; private final LinkedList selectedLanguages; private final Runnable refreshListsAction; - private final List buttons = new ArrayList<>(); - private final ButtonWidget addButton = addButton(15, 24, ADD_TEXTURES, __ -> add()); - private final ButtonWidget removeButton = addButton(15, 24, REMOVE_TEXTURES, __ -> remove()); - private final ButtonWidget moveUpButton = addButton(11, 11, MOVE_UP_TEXTURES, __ -> moveUp()); - private final ButtonWidget moveDownButton = addButton(11, 11, MOVE_DOWN_TEXTURES, __ -> moveDown()); - + private final List buttons = new ArrayList<>(); + private final Button moveUpButton = addButton(11, 11, MOVE_UP_TEXTURES, __ -> moveUp()); + private final Button moveDownButton = addButton(11, 11, MOVE_DOWN_TEXTURES, __ -> moveDown()); private LanguageListWidget parentList; + private final Button addButton = addButton(15, 24, ADD_TEXTURES, __ -> add()); + private final Button removeButton = addButton(15, 24, REMOVE_TEXTURES, __ -> remove()); - public LanguageEntry(Runnable refreshListsAction, String code, LanguageDefinition language, LinkedList selectedLanguages) { + public LanguageEntry(Runnable refreshListsAction, String code, LanguageInfo language, LinkedList selectedLanguages) { this.code = code; this.language = language; this.selectedLanguages = selectedLanguages; this.refreshListsAction = refreshListsAction; } - protected ButtonWidget addButton(int width, int height, ButtonTextures textures, ButtonWidget.PressAction action) { - var button = new TexturedButtonWidget(0, 0, width, height, textures, action); + protected Button addButton(int width, int height, WidgetSprites textures, Button.OnPress action) { + var button = new ImageButton(0, 0, width, height, textures, action); button.visible = false; buttons.add(button); return button; } private boolean isDefault() { - return code.equals(Language.DEFAULT_LANGUAGE); + return code.equals(Language.DEFAULT); } private boolean isSelected() { @@ -119,10 +115,10 @@ public void moveDown() { } @Override - public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + public void render(@NotNull GuiGraphics context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { x -= 2; y -= 2; - if (hovered || isFocused() || client.options.getTouchscreen().getValue()) { + if (hovered || isFocused() || client.options.touchscreen().get()) { var x1 = x + 1; var y1 = y + 1; var x2 = parentList.getHoveredSelectionRight() - 1; @@ -139,8 +135,8 @@ public void render(DrawContext context, int index, int y, int x, int entryWidth, renderDefaultLanguageTooltip(context, x, y); } } - context.drawTextWithShadow(client.textRenderer, language.name(), x + 29, y + 3, Colors.WHITE); - context.drawTextWithShadow(client.textRenderer, language.region(), x + 29, y + 14, Colors.GRAY); + context.drawString(client.font, language.name(), x + 29, y + 3, CommonColors.WHITE); + context.drawString(client.font, language.region(), x + 29, y + 14, CommonColors.GRAY); } private void renderButtons(ButtonRenderer renderer, int x, int y) { @@ -151,9 +147,9 @@ private void renderButtons(ButtonRenderer renderer, int x, int y) { } else renderer.render(addButton, x + 7, y); } - private void renderDefaultLanguageTooltip(DrawContext context, int x, int y) { - var tooltip = client.textRenderer.wrapLines(DEFAULT_LANGUAGE_TOOLTIP, parentList.getRowWidth() - 6); - TooltipPositioner positioner = (screenWidth, screenHeight, mouseX, mouseY, width, height) -> { + private void renderDefaultLanguageTooltip(GuiGraphics context, int x, int y) { + var tooltip = client.font.split(DEFAULT_LANGUAGE_TOOLTIP, parentList.getRowWidth() - 6); + ClientTooltipPositioner positioner = (screenWidth, screenHeight, mouseX, mouseY, width, height) -> { var pos = new Vector2i( x + 3 + (parentList.getRowWidth() - width - 6) / 2, y + parentList.getRowHeight() + 4); @@ -161,42 +157,42 @@ private void renderDefaultLanguageTooltip(DrawContext context, int x, int y) { pos.y = y - height - 6; return pos; }; - context.drawTooltip(client.textRenderer, tooltip, positioner, 0, 0, true); + context.setTooltipForNextFrame(client.font, tooltip, positioner, 0, 0, true); } @Override - public Text getNarration() { - return Text.translatable("narrator.select", language.getDisplayText()); + public @NotNull Component getNarration() { + return Component.translatable("narrator.select", language.toComponent()); } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { for (var widget : buttons) if (widget.mouseClicked(mouseX, mouseY, button)) { - ((ILanguageOptionsScreen) parentList.getScreen()).languagereload_focusList(parentList); + ((ILanguageSelectScreen) parentList.getScreen()).languagereload_focusList(parentList); return true; } return false; } - public void setParent(LanguageListWidget list) { - this.parentList = list; - } - public LanguageListWidget getParent() { return parentList; } + public void setParent(LanguageListWidget list) { + this.parentList = list; + } + public String getCode() { return code; } - public LanguageDefinition getLanguage() { + public LanguageInfo getLanguage() { return language; } @FunctionalInterface private interface ButtonRenderer { - void render(ButtonWidget button, int x, int y); + void render(Button button, int x, int y); } } diff --git a/common/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java b/common/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java new file mode 100644 index 0000000..8950674 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java @@ -0,0 +1,111 @@ +package jerozgen.languagereload.gui; + +import jerozgen.languagereload.access.ILanguageSelectScreen; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.options.LanguageSelectScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.util.CommonColors; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static org.lwjgl.glfw.GLFW.*; + +public class LanguageListWidget extends ObjectSelectionList { + private final Component title; + private final LanguageSelectScreen screen; + + public LanguageListWidget(Minecraft client, LanguageSelectScreen screen, int width, int height, Component title) { + super(client, width, height - 83 - 16, 32 + 16, 24, (int) (9f * 1.5f)); + this.title = title; + this.screen = screen; + centerListVertically = false; + } + + @Override + protected void renderHeader(GuiGraphics context, int x, int y) { + var headerText = title.copy().withStyle(ChatFormatting.UNDERLINE, ChatFormatting.BOLD); + int headerPosX = x + width / 2 - minecraft.font.width(headerText) / 2; + int headerPosY = Math.min(this.getY() + 3, y); + context.drawString(minecraft.font, headerText, headerPosX, headerPosY, CommonColors.WHITE); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + var selectedEntry = this.getSelected(); + if (selectedEntry == null) return super.keyPressed(keyCode, scanCode, modifiers); + + if (keyCode == GLFW_KEY_SPACE || keyCode == GLFW_KEY_ENTER) { + selectedEntry.toggle(); + this.setFocused(null); + ((ILanguageSelectScreen) screen).languagereload_focusEntry(selectedEntry); + return true; + } + + if (Screen.hasShiftDown()) { + if (keyCode == GLFW_KEY_DOWN) { + selectedEntry.moveDown(); + return true; + } + if (keyCode == GLFW_KEY_UP) { + selectedEntry.moveUp(); + return true; + } + } + + return super.keyPressed(keyCode, scanCode, modifiers); + } + + /** + * Remove hovering in scrollbar area + */ + @Override + @Nullable + public LanguageEntry getEntryAtPosition(double x, double y) { + var entry = super.getEntryAtPosition(x, y); + return entry != null && this.scrollbarVisible() && x >= this.scrollBarX() + ? null + : entry; + } + + @Override + protected void renderSelection(@NotNull GuiGraphics context, int y, int entryWidth, int entryHeight, int borderColor, int fillColor) { + if (this.scrollbarVisible()) { + var x1 = this.getRowLeft() - 2; + var x2 = this.scrollBarX(); + var y1 = y - 2; + var y2 = y + entryHeight + 2; + context.fill(x1, y1, x2, y2, borderColor); + context.fill(x1 + 1, y1 + 1, x2 - 1, y2 - 1, fillColor); + } else { + super.renderSelection(context, y, entryWidth, entryHeight, borderColor, fillColor); + } + } + + public int getHoveredSelectionRight() { + return this.scrollbarVisible() + ? this.scrollBarX() + : this.getRowRight() - 2; + } + + public LanguageSelectScreen getScreen() { + return screen; + } + + public int getRowHeight() { + return itemHeight; + } + + @Override + public int getRowWidth() { + return width; + } + + @Override + protected int scrollBarX() { + return this.getRight() - 6; + } +} diff --git a/common/src/main/java/jerozgen/languagereload/helper/ClientLanguageHelper.java b/common/src/main/java/jerozgen/languagereload/helper/ClientLanguageHelper.java new file mode 100644 index 0000000..811cb4c --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/helper/ClientLanguageHelper.java @@ -0,0 +1,20 @@ +package jerozgen.languagereload.helper; + +import com.google.common.collect.Maps; +import jerozgen.languagereload.config.Config; +import net.minecraft.locale.Language; + +import java.io.InputStream; +import java.util.Map; +import java.util.function.BiConsumer; + +public class ClientLanguageHelper { + public static void saveSeparately(InputStream stream, BiConsumer output, Map> separateTranslationsOnLoad, String languageName) { + if (Config.getInstance().multilingualItemSearch) { + Language.loadFromJson(stream, output.andThen((key, value) -> + separateTranslationsOnLoad.computeIfAbsent(languageName, k -> Maps.newHashMap()).put(key, value))); + } else { + Language.loadFromJson(stream, output); + } + } +} diff --git a/common/src/main/java/jerozgen/languagereload/helper/SessionSearchTreesHelper.java b/common/src/main/java/jerozgen/languagereload/helper/SessionSearchTreesHelper.java new file mode 100644 index 0000000..5adbbd8 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/helper/SessionSearchTreesHelper.java @@ -0,0 +1,43 @@ +package jerozgen.languagereload.helper; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import jerozgen.languagereload.access.IClientLanguage; +import jerozgen.languagereload.access.ILanguage; +import jerozgen.languagereload.config.Config; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.ArrayList; +import java.util.List; + +public class SessionSearchTreesHelper { + public static List addFallbackTranslationsToSearchTooltips(ItemStack items, Item.TooltipContext context, Player player, TooltipFlag type, Operation> operation) { + var original = operation.call(items, context, player, type); + + if (Config.getInstance() == null) return original; + if (!Config.getInstance().multilingualItemSearch) return original; + + var language = Language.getInstance(); + if (language == null) return original; + + var clientLanguage = ((ILanguage) language).languagereload_getClientLanguage(); + if (clientLanguage == null) return original; + + var result = new ArrayList<>(original); + for (var fallbackCode : Config.getInstance().fallbacks) { + ((IClientLanguage) clientLanguage).languagereload_setTargetLanguage(fallbackCode); + operation.call(items, context, player, type).stream() + .map(Component::getString) + .map(Component::literal) + .forEach(result::add); + } + + ((IClientLanguage) clientLanguage).languagereload_setTargetLanguage(null); + + return result; + } +} diff --git a/src/main/java/jerozgen/languagereload/mixin/AdvancementTabMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/AdvancementTabMixin.java similarity index 75% rename from src/main/java/jerozgen/languagereload/mixin/AdvancementTabMixin.java rename to common/src/main/java/jerozgen/languagereload/mixin/AdvancementTabMixin.java index 143e1d5..c346f85 100644 --- a/src/main/java/jerozgen/languagereload/mixin/AdvancementTabMixin.java +++ b/common/src/main/java/jerozgen/languagereload/mixin/AdvancementTabMixin.java @@ -1,10 +1,10 @@ package jerozgen.languagereload.mixin; import jerozgen.languagereload.access.IAdvancementsTab; -import net.minecraft.advancement.AdvancementEntry; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.advancement.AdvancementTab; -import net.minecraft.client.gui.screen.advancement.AdvancementWidget; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.advancements.AdvancementTab; +import net.minecraft.client.gui.screens.advancements.AdvancementWidget; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -13,15 +13,15 @@ @Mixin(AdvancementTab.class) public abstract class AdvancementTabMixin implements IAdvancementsTab { - @Shadow @Final private MinecraftClient client; - @Shadow @Final private Map widgets; + @Shadow @Final private Minecraft minecraft; + @Shadow @Final private Map widgets; @Override public void languagereload_recreateWidgets() { widgets.replaceAll((advancement, widget) -> { var newWidget = new AdvancementWidget( ((AdvancementWidgetAccessor) widget).languagereload_getTab(), - client, + minecraft, ((AdvancementWidgetAccessor) widget).languagereload_getAdvancement(), ((AdvancementWidgetAccessor) widget).languagereload_getDisplay() ); diff --git a/src/main/java/jerozgen/languagereload/mixin/AdvancementWidgetAccessor.java b/common/src/main/java/jerozgen/languagereload/mixin/AdvancementWidgetAccessor.java similarity index 65% rename from src/main/java/jerozgen/languagereload/mixin/AdvancementWidgetAccessor.java rename to common/src/main/java/jerozgen/languagereload/mixin/AdvancementWidgetAccessor.java index 470f721..b274b04 100644 --- a/src/main/java/jerozgen/languagereload/mixin/AdvancementWidgetAccessor.java +++ b/common/src/main/java/jerozgen/languagereload/mixin/AdvancementWidgetAccessor.java @@ -1,10 +1,10 @@ package jerozgen.languagereload.mixin; -import net.minecraft.advancement.AdvancementDisplay; -import net.minecraft.advancement.AdvancementProgress; -import net.minecraft.advancement.PlacedAdvancement; -import net.minecraft.client.gui.screen.advancement.AdvancementTab; -import net.minecraft.client.gui.screen.advancement.AdvancementWidget; +import net.minecraft.advancements.AdvancementNode; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.advancements.DisplayInfo; +import net.minecraft.client.gui.screens.advancements.AdvancementTab; +import net.minecraft.client.gui.screens.advancements.AdvancementWidget; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @@ -16,11 +16,11 @@ public interface AdvancementWidgetAccessor { @Accessor("tab") AdvancementTab languagereload_getTab(); - @Accessor("advancement") - PlacedAdvancement languagereload_getAdvancement(); + @Accessor("advancementNode") + AdvancementNode languagereload_getAdvancement(); @Accessor("display") - AdvancementDisplay languagereload_getDisplay(); + DisplayInfo languagereload_getDisplay(); @Accessor("progress") AdvancementProgress languagereload_getProgress(); diff --git a/src/main/java/jerozgen/languagereload/mixin/AdvancementsScreenMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/AdvancementsScreenMixin.java similarity index 54% rename from src/main/java/jerozgen/languagereload/mixin/AdvancementsScreenMixin.java rename to common/src/main/java/jerozgen/languagereload/mixin/AdvancementsScreenMixin.java index 78f018a..ebde91a 100644 --- a/src/main/java/jerozgen/languagereload/mixin/AdvancementsScreenMixin.java +++ b/common/src/main/java/jerozgen/languagereload/mixin/AdvancementsScreenMixin.java @@ -2,12 +2,12 @@ import jerozgen.languagereload.access.IAdvancementsScreen; import jerozgen.languagereload.access.IAdvancementsTab; -import net.minecraft.advancement.AdvancementEntry; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.advancement.AdvancementTab; -import net.minecraft.client.gui.screen.advancement.AdvancementsScreen; -import net.minecraft.client.network.ClientAdvancementManager; -import net.minecraft.text.Text; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.advancements.AdvancementTab; +import net.minecraft.client.gui.screens.advancements.AdvancementsScreen; +import net.minecraft.client.multiplayer.ClientAdvancements; +import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -15,10 +15,10 @@ import java.util.Map; @Mixin(AdvancementsScreen.class) -public abstract class AdvancementsScreenMixin extends Screen implements ClientAdvancementManager.Listener, IAdvancementsScreen { - @Shadow @Final private Map tabs; +public abstract class AdvancementsScreenMixin extends Screen implements ClientAdvancements.Listener, IAdvancementsScreen { + @Shadow @Final private Map tabs; - protected AdvancementsScreenMixin(Text title) { + protected AdvancementsScreenMixin(Component title) { super(title); } diff --git a/common/src/main/java/jerozgen/languagereload/mixin/BookViewScreenAccessor.java b/common/src/main/java/jerozgen/languagereload/mixin/BookViewScreenAccessor.java new file mode 100644 index 0000000..f8bc9c8 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/BookViewScreenAccessor.java @@ -0,0 +1,11 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.client.gui.screens.inventory.BookViewScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(BookViewScreen.class) +public interface BookViewScreenAccessor { + @Accessor("cachedPage") + void languagereload_setCachedPage(int cachedPage); +} diff --git a/common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheAccessor.java b/common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheAccessor.java new file mode 100644 index 0000000..393d17c --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheAccessor.java @@ -0,0 +1,11 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.client.multiplayer.ClientChunkCache; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ClientChunkCache.class) +public interface ClientChunkCacheAccessor { + @Accessor("storage") + ClientChunkCache.Storage languagereload_getChunks(); +} diff --git a/common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheStorageAccessor.java b/common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheStorageAccessor.java new file mode 100644 index 0000000..0f62dcf --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/ClientChunkCacheStorageAccessor.java @@ -0,0 +1,14 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.client.multiplayer.ClientChunkCache; +import net.minecraft.world.level.chunk.LevelChunk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.concurrent.atomic.AtomicReferenceArray; + +@Mixin(ClientChunkCache.Storage.class) +public interface ClientChunkCacheStorageAccessor { + @Accessor("chunks") + AtomicReferenceArray languagereload_getChunks(); +} diff --git a/common/src/main/java/jerozgen/languagereload/mixin/KeyboardHandlerMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/KeyboardHandlerMixin.java new file mode 100644 index 0000000..13de200 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/KeyboardHandlerMixin.java @@ -0,0 +1,96 @@ +package jerozgen.languagereload.mixin; + +import com.mojang.blaze3d.platform.InputConstants; +import jerozgen.languagereload.LanguageReload; +import jerozgen.languagereload.config.Config; +import net.minecraft.client.KeyboardHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.resources.language.LanguageInfo; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentUtils; +import org.lwjgl.glfw.GLFW; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.ArrayList; +import java.util.Objects; + +@Mixin(KeyboardHandler.class) +public abstract class KeyboardHandlerMixin { + @Shadow @Final private Minecraft minecraft; + + @Shadow protected abstract void showDebugChat(Component message); + @Shadow protected abstract void debugWarningComponent(Component message); + @Shadow protected abstract void debugFeedbackComponent(Component text); + @Shadow protected abstract void debugFeedbackTranslated(String key); + + @Unique + private void languagereload_processLanguageReloadKeys() { + if (Screen.hasShiftDown()) { + var config = Config.getInstance(); + var languageManager = minecraft.getLanguageManager(); + + var language = languageManager.getLanguage(config.previousLanguage); + var noLanguage = config.previousLanguage.equals(LanguageReload.NO_LANGUAGE); + if (language == null && !noLanguage) { + this.debugWarningComponent(Component.translatable("debug.reload_languages.switch.failure")); + } else { + LanguageReload.setLanguage(config.previousLanguage, config.previousFallbacks); + var languages = new ArrayList() {{ + if (noLanguage) + add(Component.literal("∅")); + if (language != null) + add(language.toComponent()); + addAll(config.fallbacks.stream() + .map(languageManager::getLanguage) + .filter(Objects::nonNull) + .map(LanguageInfo::toComponent) + .toList()); + }}; + this.debugFeedbackComponent(Component.translatable("debug.reload_languages.switch.success", ComponentUtils.formatList(languages, Component.literal(", ")))); + } + } else { + LanguageReload.reloadLanguages(); + this.debugFeedbackTranslated("debug.reload_languages.message"); + } + } + + @Inject(method = "handleDebugKeys", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/KeyboardHandler;showDebugChat(Lnet/minecraft/network/chat/Component;)V", + ordinal = 6, shift = At.Shift.AFTER)) + private void onProcessF3$addHelp(int key, CallbackInfoReturnable cir) { + this.showDebugChat(Component.translatable("debug.reload_languages.help")); + } + + @Inject(method = "handleDebugKeys", at = @At("RETURN"), cancellable = true) + private void onProcessF3(int key, CallbackInfoReturnable cir) { + if (key == GLFW.GLFW_KEY_J) { + languagereload_processLanguageReloadKeys(); + cir.setReturnValue(true); + } + } + + @Inject(method = "keyPress", at = @At(value = "FIELD", target = "Lnet/minecraft/client/KeyboardHandler;debugCrashKeyTime:J", ordinal = 0), cancellable = true) + private void onKeyPress(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { + if (minecraft.screen != null && InputConstants.isKeyDown(window, GLFW.GLFW_KEY_F3) && key == GLFW.GLFW_KEY_J) { + if (action != 0) { + languagereload_processLanguageReloadKeys(); + } + ci.cancel(); + } + } + + @Inject(method = "charTyped", at = @At("HEAD"), cancellable = true) + private void onCharTyped(long window, int codePoint, int modifiers, CallbackInfo ci) { + if (InputConstants.isKeyDown(window, GLFW.GLFW_KEY_F3) && InputConstants.isKeyDown(window, GLFW.GLFW_KEY_J)) { + ci.cancel(); + } + } +} diff --git a/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java similarity index 61% rename from src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java rename to common/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java index b8d218e..e3fcd6f 100644 --- a/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java +++ b/common/src/main/java/jerozgen/languagereload/mixin/LanguageManagerMixin.java @@ -4,10 +4,10 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import jerozgen.languagereload.LanguageReload; import jerozgen.languagereload.config.Config; -import net.minecraft.client.resource.language.LanguageDefinition; -import net.minecraft.client.resource.language.LanguageManager; -import net.minecraft.resource.ResourceManager; -import net.minecraft.util.Language; +import net.minecraft.client.resources.language.LanguageInfo; +import net.minecraft.client.resources.language.LanguageManager; +import net.minecraft.locale.Language; +import net.minecraft.server.packs.resources.ResourceManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -20,34 +20,43 @@ @Mixin(LanguageManager.class) abstract class LanguageManagerMixin { - @Shadow private Map languageDefs; + @Shadow private Map languages; - @Shadow public abstract LanguageDefinition getLanguage(String code); + @Unique + private static void languagereload_setSystemLanguage(String lang, Locale locale) { + LanguageReload.LOGGER.info("Set language to {} (mapped from {})", lang, locale.toLanguageTag()); + LanguageReload.setLanguage(lang, new LinkedList<>() {{ + if (!lang.equals(Language.DEFAULT)) + add(Language.DEFAULT); + }}); + } + + @Shadow public abstract LanguageInfo getLanguage(String code); - @Redirect(method = "reload", at = @At(value = "INVOKE", ordinal = 0, remap = false, + @Redirect(method = "onResourceManagerReload", at = @At(value = "INVOKE", ordinal = 0, remap = false, target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) - boolean onReload$addFallbacks(List list, Object enUsCode) { + boolean onResourceManagerReload$addFallbacks(List list, Object enUsCode) { Lists.reverse(Config.getInstance().fallbacks).stream() .filter(code -> Objects.nonNull(getLanguage(code))) .forEach(list::add); return true; } - @ModifyExpressionValue(method = "reload", at = @At(value = "INVOKE", remap = false, + @ModifyExpressionValue(method = "onResourceManagerReload", at = @At(value = "INVOKE", remap = false, target = "Ljava/lang/String;equals(Ljava/lang/Object;)Z")) - boolean onReload$ignoreNoLanguage(boolean original) { + boolean onResourceManagerReload$ignoreNoLanguage(boolean original) { return Config.getInstance().language.equals(LanguageReload.NO_LANGUAGE); } - @Inject(method = "reload", at = @At(value = "INVOKE", ordinal = 0, remap = false, + @Inject(method = "onResourceManagerReload", at = @At(value = "INVOKE", ordinal = 0, remap = false, target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) - void onReload$setSystemLanguage(ResourceManager manager, CallbackInfo ci) { + void onResourceManagerReload$setSystemLanguage(ResourceManager manager, CallbackInfo ci) { if (LanguageReload.shouldSetSystemLanguage) { LanguageReload.shouldSetSystemLanguage = false; LanguageReload.LOGGER.info("Language is not set. Setting it to system language"); var locale = Locale.getDefault(); - var matchingLanguages = languageDefs.keySet().stream() + var matchingLanguages = languages.keySet().stream() .filter(code -> code.split("_")[0].equalsIgnoreCase(locale.getLanguage())) .toList(); var count = matchingLanguages.size(); @@ -58,17 +67,8 @@ abstract class LanguageManagerMixin { return split[1].equalsIgnoreCase(locale.getCountry()); }) .findFirst() - .ifPresent(lang -> setSystemLanguage(lang, locale)); - else if (count == 1) setSystemLanguage(matchingLanguages.getFirst(), locale); + .ifPresent(lang -> languagereload_setSystemLanguage(lang, locale)); + else if (count == 1) languagereload_setSystemLanguage(matchingLanguages.getFirst(), locale); } } - - @Unique - private static void setSystemLanguage(String lang, Locale locale) { - LanguageReload.LOGGER.info("Set language to {} (mapped from {})", lang, locale.toLanguageTag()); - LanguageReload.setLanguage(lang, new LinkedList<>() {{ - if (!lang.equals(Language.DEFAULT_LANGUAGE)) - add(Language.DEFAULT_LANGUAGE); - }}); - } } diff --git a/common/src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java new file mode 100644 index 0000000..bee0d2c --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java @@ -0,0 +1,41 @@ +package jerozgen.languagereload.mixin; + +import jerozgen.languagereload.access.ILanguage; +import net.minecraft.client.resources.language.ClientLanguage; +import net.minecraft.locale.Language; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +// Fixes Server Translation API incompatibility (#56) +@Mixin(value = Language.class, priority = 990) +public class LanguageMixin implements ILanguage { + @Unique private static @Nullable ClientLanguage languagereload_clientLanguageOnSetInstance = null; + @Unique private @Nullable ClientLanguage languagereload_clientLanguage = null; + + @Inject(method = "inject", at = @At("HEAD")) + private static void onSetInstance(Language language, CallbackInfo ci) { + if (language instanceof ClientLanguage clientLanguage) { + languagereload_clientLanguageOnSetInstance = clientLanguage; + } + } + + @Inject(method = "inject", at = @At("TAIL")) + private static void afterSetInstance(Language language, CallbackInfo ci) { + ((ILanguage) language).languagereload_setClientLanguage(languagereload_clientLanguageOnSetInstance); + languagereload_clientLanguageOnSetInstance = null; + } + + @Override + public void languagereload_setClientLanguage(ClientLanguage translationStorage) { + this.languagereload_clientLanguage = translationStorage; + } + + @Override + public ClientLanguage languagereload_getClientLanguage() { + return languagereload_clientLanguage; + } +} diff --git a/common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectScreenMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectScreenMixin.java new file mode 100644 index 0000000..c49a6f9 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectScreenMixin.java @@ -0,0 +1,173 @@ +package jerozgen.languagereload.mixin; + +import jerozgen.languagereload.LanguageReload; +import jerozgen.languagereload.access.ILanguageSelectScreen; +import jerozgen.languagereload.config.Config; +import jerozgen.languagereload.gui.LanguageEntry; +import jerozgen.languagereload.gui.LanguageListWidget; +import net.minecraft.client.Options; +import net.minecraft.client.gui.ComponentPath; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.StringWidget; +import net.minecraft.client.gui.layouts.LinearLayout; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.options.LanguageSelectScreen; +import net.minecraft.client.gui.screens.options.OptionsSubScreen; +import net.minecraft.client.resources.language.LanguageManager; +import net.minecraft.network.chat.Component; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.*; +import java.util.stream.Stream; + +@Mixin(LanguageSelectScreen.class) +public abstract class LanguageSelectScreenMixin extends OptionsSubScreen implements ILanguageSelectScreen { + @Unique private final LinkedList languagereload_selectedLanguages = new LinkedList<>(); + @Unique private final Map languagereload_languageEntries = new LinkedHashMap<>(); + @Unique private LanguageListWidget languagereload_availableLanguageList; + @Unique private LanguageListWidget languagereload_selectedLanguageList; + @Unique private EditBox languagereload_searchBox; + @Shadow private LanguageSelectScreen.LanguageSelectionList languageSelectionList; + + LanguageSelectScreenMixin(Screen parent, Options options, Component title) { + super(parent, options, title); + } + + @Inject(method = "", at = @At("TAIL")) + void onConstructed(Screen parent, Options options, LanguageManager languageManager, CallbackInfo ci) { + var currentLangCode = languageManager.getSelected(); + if (!currentLangCode.equals(LanguageReload.NO_LANGUAGE)) + languagereload_selectedLanguages.add(currentLangCode); + languagereload_selectedLanguages.addAll(Config.getInstance().fallbacks); + languageManager.getLanguages().forEach((code, language) -> + languagereload_languageEntries.put(code, new LanguageEntry(this::languagereload_refresh, code, language, languagereload_selectedLanguages))); + + layout.setHeaderHeight(48); + layout.setFooterHeight(53); + } + + @Inject(method = "addContents", at = @At("HEAD"), cancellable = true) + void onAddContents(CallbackInfo ci) { + languageSelectionList = LanguageSelectionListWidgetAccessor.languagereload_init(languagereload_it(), minecraft); + + var listWidth = Math.min(width / 2 - 4, 200); + languagereload_availableLanguageList = new LanguageListWidget(minecraft, languagereload_it(), listWidth, height, Component.translatable("pack.available.title")); + languagereload_selectedLanguageList = new LanguageListWidget(minecraft, languagereload_it(), listWidth, height, Component.translatable("pack.selected.title")); + languagereload_availableLanguageList.setX(width / 2 - 4 - listWidth); + languagereload_selectedLanguageList.setX(width / 2 + 4); + layout.addToContents(languagereload_availableLanguageList); + layout.addToContents(languagereload_selectedLanguageList); + languagereload_refresh(); + + ci.cancel(); + } + + @Override + protected void addTitle() { + languagereload_searchBox = new EditBox(font, width / 2 - 100, 22, 200, 20, languagereload_searchBox, Component.empty()) { + @Override + public void setFocused(boolean focused) { + if (!isFocused() && focused) { + super.setFocused(true); + languagereload_focusSearch(); + } else super.setFocused(focused); + } + }; + languagereload_searchBox.setResponder(__ -> languagereload_refresh()); + + var header = layout.addToHeader(LinearLayout.vertical().spacing(5)); + header.defaultCellSetting().alignHorizontallyCenter(); + header.addChild(new StringWidget(title, font)); + header.addChild(languagereload_searchBox); + } + + @Inject(method = "repositionElements", at = @At("HEAD"), cancellable = true) + protected void onRepositionElements(CallbackInfo ci) { + super.repositionElements(); + + var listWidth = Math.min(width / 2 - 4, 200); + languagereload_availableLanguageList.updateSize(listWidth, layout); + languagereload_selectedLanguageList.updateSize(listWidth, layout); + languagereload_availableLanguageList.setX(width / 2 - 4 - listWidth); + languagereload_selectedLanguageList.setX(width / 2 + 4); + languagereload_availableLanguageList.refreshScrollAmount(); + languagereload_selectedLanguageList.refreshScrollAmount(); + + ci.cancel(); + } + + @Inject(method = "onDone", at = @At("HEAD"), cancellable = true) + private void onDone(CallbackInfo ci) { + if (minecraft == null) return; + minecraft.setScreen(lastScreen); + + var language = languagereload_selectedLanguages.peekFirst(); + if (language == null) { + LanguageReload.setLanguage(LanguageReload.NO_LANGUAGE, new LinkedList<>()); + } else { + var fallbacks = new LinkedList<>(languagereload_selectedLanguages); + fallbacks.removeFirst(); + LanguageReload.setLanguage(language, fallbacks); + } + + ci.cancel(); + } + + @Unique + private void languagereload_refresh() { + languagereload_refreshList(languagereload_selectedLanguageList, languagereload_selectedLanguages.stream().map(languagereload_languageEntries::get).filter(Objects::nonNull)); + languagereload_refreshList(languagereload_availableLanguageList, languagereload_languageEntries.values().stream() + .filter(entry -> { + if (languagereload_selectedLanguageList.children().contains(entry)) return false; + var query = languagereload_searchBox.getValue().toLowerCase(Locale.ROOT); + var langCode = entry.getCode().toLowerCase(Locale.ROOT); + var langName = entry.getLanguage().toComponent().getString().toLowerCase(Locale.ROOT); + return langCode.contains(query) || langName.contains(query); + })); + } + + @Unique + private void languagereload_refreshList(LanguageListWidget list, Stream entries) { + var selectedEntry = list.getSelected(); + list.setSelected(null); + list.children().clear(); + entries.forEach(entry -> { + list.children().add(entry); + entry.setParent(list); + if (entry == selectedEntry) { + list.setSelected(entry); + } + }); + list.refreshScrollAmount(); + } + + @Override + protected void setInitialFocus() { + languagereload_focusSearch(); + } + + @Unique + private void languagereload_focusSearch() { + changeFocus(ComponentPath.path(languagereload_searchBox, this)); + } + + @Override + public void languagereload_focusList(LanguageListWidget list) { + changeFocus(ComponentPath.path(list, this)); + } + + @Override + public void languagereload_focusEntry(LanguageEntry entry) { + changeFocus(ComponentPath.path(entry, entry.getParent(), this)); + } + + @Unique + LanguageSelectScreen languagereload_it() { + return (LanguageSelectScreen) (Object) this; + } +} diff --git a/common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java b/common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java new file mode 100644 index 0000000..bf5b3b4 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java @@ -0,0 +1,14 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.options.LanguageSelectScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(LanguageSelectScreen.LanguageSelectionList.class) +public interface LanguageSelectionListWidgetAccessor { + @Invoker("") + static LanguageSelectScreen.LanguageSelectionList languagereload_init(LanguageSelectScreen screen, Minecraft client) { + throw new AssertionError(); + } +} diff --git a/src/main/java/jerozgen/languagereload/mixin/GameOptionsMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/OptionsMixin.java similarity index 65% rename from src/main/java/jerozgen/languagereload/mixin/GameOptionsMixin.java rename to common/src/main/java/jerozgen/languagereload/mixin/OptionsMixin.java index 7b8278b..f0229ed 100644 --- a/src/main/java/jerozgen/languagereload/mixin/GameOptionsMixin.java +++ b/common/src/main/java/jerozgen/languagereload/mixin/OptionsMixin.java @@ -2,10 +2,10 @@ import jerozgen.languagereload.LanguageReload; import jerozgen.languagereload.config.Config; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.option.GameOptions; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.Language; +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.minecraft.locale.Language; +import net.minecraft.nbt.CompoundTag; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -17,15 +17,33 @@ import java.io.File; -@Mixin(GameOptions.class) -abstract class GameOptionsMixin { +@Mixin(Options.class) +abstract class OptionsMixin { + @Shadow public String languageCode; @Shadow @Final private File optionsFile; - @Shadow public String language; + + @Unique private static void languagereload_checkConfigLanguage(String language) { + var config = Config.getInstance(); + if (!config.language.equals(language)) { + LanguageReload.LOGGER.info( + "Game language ({}) and config language ({}) are different. Updating config", + language, + config.language + ); + config.previousLanguage = config.language; + config.previousFallbacks = config.fallbacks; + config.language = language; + config.fallbacks.clear(); + if (!language.equals(Language.DEFAULT)) + config.fallbacks.add(Language.DEFAULT); + Config.save(); + } + } @Inject(method = "", at = @At("TAIL")) - void onConstructed(MinecraftClient client, File optionsFile, CallbackInfo ci) { + void onConstructed(Minecraft client, File optionsFile, CallbackInfo ci) { if (!LanguageReload.shouldSetSystemLanguage) { - checkConfigLanguage(language); + languagereload_checkConfigLanguage(languageCode); } } @@ -36,30 +54,11 @@ void onLoad(CallbackInfo ci) { } } - @Inject(method = "update", at = @At("RETURN")) - void onUpdate(NbtCompound nbt, CallbackInfoReturnable cir) { - var lang = cir.getReturnValue().getString("lang", ""); + @Inject(method = "dataFix", at = @At("RETURN")) + void onDataFix(CompoundTag nbt, CallbackInfoReturnable cir) { + var lang = cir.getReturnValue().getStringOr("lang", ""); if (lang.isEmpty()) { LanguageReload.shouldSetSystemLanguage = true; - } else checkConfigLanguage(lang); - } - - @Unique - private static void checkConfigLanguage(String language) { - var config = Config.getInstance(); - if (!config.language.equals(language)) { - LanguageReload.LOGGER.info( - "Game language ({}) and config language ({}) are different. Updating config", - language, - config.language - ); - config.previousLanguage = config.language; - config.previousFallbacks = config.fallbacks; - config.language = language; - config.fallbacks.clear(); - if (!language.equals(Language.DEFAULT_LANGUAGE)) - config.fallbacks.add(Language.DEFAULT_LANGUAGE); - Config.save(); - } + } else languagereload_checkConfigLanguage(lang); } } diff --git a/src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/RecipeBookComponentMixin.java similarity index 54% rename from src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java rename to common/src/main/java/jerozgen/languagereload/mixin/RecipeBookComponentMixin.java index a8afb92..8242c06 100644 --- a/src/main/java/jerozgen/languagereload/mixin/RecipeBookWidgetMixin.java +++ b/common/src/main/java/jerozgen/languagereload/mixin/RecipeBookComponentMixin.java @@ -1,8 +1,8 @@ package jerozgen.languagereload.mixin; import jerozgen.languagereload.LanguageReload; -import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; -import net.minecraft.util.Language; +import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; +import net.minecraft.locale.Language; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -10,13 +10,13 @@ import java.util.LinkedList; -@Mixin(RecipeBookWidget.class) -public class RecipeBookWidgetMixin { - @Inject(method = "triggerPirateSpeakEasterEgg", cancellable = true, at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/resource/language/LanguageManager;setLanguage(Ljava/lang/String;)V")) +@Mixin(RecipeBookComponent.class) +public class RecipeBookComponentMixin { + @Inject(method = "pirateSpeechForThePeople", cancellable = true, at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/resources/language/LanguageManager;setSelected(Ljava/lang/String;)V")) void onLanguageSwitching$cancel(String search, CallbackInfo ci) { LanguageReload.setLanguage("en_pt", new LinkedList<>() {{ - add(Language.DEFAULT_LANGUAGE); + add(Language.DEFAULT); }}); ci.cancel(); } diff --git a/common/src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java b/common/src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java new file mode 100644 index 0000000..24d53ed --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java @@ -0,0 +1,12 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.world.level.block.entity.SignText; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SignText.class) +public interface SignTextAccessor { + @Accessor("renderMessages") + void languagereload_setOrderedMessages(FormattedCharSequence[] orderedMessages); +} diff --git a/common/src/main/java/jerozgen/languagereload/mixin/TextDisplayAccessor.java b/common/src/main/java/jerozgen/languagereload/mixin/TextDisplayAccessor.java new file mode 100644 index 0000000..99dd9f4 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/TextDisplayAccessor.java @@ -0,0 +1,11 @@ +package jerozgen.languagereload.mixin; + +import net.minecraft.world.entity.Display; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Display.TextDisplay.class) +public interface TextDisplayAccessor { + @Accessor("clientDisplayCache") + void languagereload_setTextLines(Display.TextDisplay.CachedInfo textLines); +} diff --git a/common/src/main/java/jerozgen/languagereload/mixin/TranslatableContentsMixin.java b/common/src/main/java/jerozgen/languagereload/mixin/TranslatableContentsMixin.java new file mode 100644 index 0000000..98e191d --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/mixin/TranslatableContentsMixin.java @@ -0,0 +1,67 @@ +package jerozgen.languagereload.mixin; + +import com.google.common.collect.ImmutableList; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import jerozgen.languagereload.access.IClientLanguage; +import jerozgen.languagereload.access.ILanguage; +import jerozgen.languagereload.config.Config; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.ComponentContents; +import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.contents.TranslatableContents; +import net.minecraft.network.chat.contents.TranslatableFormatException; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.List; +import java.util.function.Consumer; + +@Mixin(TranslatableContents.class) +abstract class TranslatableContentsMixin implements ComponentContents { + @Shadow @Final private String key; + + @WrapOperation(method = "visit(Lnet/minecraft/network/chat/FormattedText$ContentConsumer;)Ljava/util/Optional;", + at = @At(value = "FIELD", target = "Lnet/minecraft/network/chat/contents/TranslatableContents;decomposedParts:Ljava/util/List;")) + List onVisit(TranslatableContents instance, Operation> translationsGetter) { + var overriddenTranslations = languagereload_getOverriddenTranslations(); + if (overriddenTranslations != null) return overriddenTranslations; + return translationsGetter.call(instance); + } + + @WrapOperation(method = "visit(Lnet/minecraft/network/chat/FormattedText$StyledContentConsumer;Lnet/minecraft/network/chat/Style;)Ljava/util/Optional;", + at = @At(value = "FIELD", target = "Lnet/minecraft/network/chat/contents/TranslatableContents;decomposedParts:Ljava/util/List;")) + List onVisitStyled(TranslatableContents instance, Operation> translationsGetter) { + var overriddenTranslations = languagereload_getOverriddenTranslations(); + if (overriddenTranslations != null) return overriddenTranslations; + return translationsGetter.call(instance); + } + + @Unique + List languagereload_getOverriddenTranslations() { + if (!Config.getInstance().multilingualItemSearch) return null; + + var language = Language.getInstance(); + if (language == null) return null; + + var clientLanguage = ((ILanguage) language).languagereload_getClientLanguage(); + if (clientLanguage == null) return null; + + var targetLanguage = ((IClientLanguage) clientLanguage).languagereload_getTargetLanguage(); + if (targetLanguage == null) return null; + + var string = ((IClientLanguage) clientLanguage).languagereload_get(key); + try { + var builder = new ImmutableList.Builder(); + this.decomposeTemplate(string, builder::add); + return builder.build(); + } catch (TranslatableFormatException e) { + return ImmutableList.of(FormattedText.of(string)); + } + } + + @Shadow protected abstract void decomposeTemplate(String translation, Consumer partsConsumer); +} diff --git a/common/src/main/java/jerozgen/languagereload/platform/Services.java b/common/src/main/java/jerozgen/languagereload/platform/Services.java new file mode 100644 index 0000000..3aa157a --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/platform/Services.java @@ -0,0 +1,46 @@ +package jerozgen.languagereload.platform; + +import jerozgen.languagereload.LanguageReload; +import jerozgen.languagereload.platform.services.IPlatformHelper; + +import java.util.ServiceLoader; + +/** + * Service loaders are a built-in Java feature that enables dynamic discovery of interface implementations. + *

In the {@code MultiLoader} context, they are leveraged to access a mock API within the common codebase. + * This mock API acts as a placeholder during development, ensuring a consistent interface across shared code. + *

At runtime, the service loader mechanism seamlessly replaces the mock API with a platform-specific implementation. + * This dynamic substitution guarantees that the appropriate platform-dependent logic is executed while the common code + * continues to interact with a unified API. + */ +public final class Services { + + /** + * Singleton instance providing platform-specific functionality and information at runtime. + *

This helper enables: + *

    + *
  • Platform detection (Forge/Fabric/NeoForge)
  • + *
  • Dynamic mod dependency validation
  • + *
  • Safe access to platform-specific APIs and features
  • + *
  • Cross-platform compatibility checks
  • + *
+ *

The implementation is loaded dynamically via Java's ServiceLoader mechanism, + * ensuring the correct platform-specific logic is used at runtime. + */ + public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class); + + /** + * Loads a service implementation for the current platform. + *

Implementation is defined in META-INF/services + * + * @param clazz The service interface to load + * @return {@code loadedService} The platform-specific implementation + * @throws NullPointerException if no implementation is found + */ + public static T load(Class clazz) { + T loadedService = ServiceLoader.load(clazz).findFirst().orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName())); + + LanguageReload.LOGGER.debug("Loaded {} for service {}", loadedService, clazz); + return loadedService; + } +} \ No newline at end of file diff --git a/common/src/main/java/jerozgen/languagereload/platform/services/IPlatformHelper.java b/common/src/main/java/jerozgen/languagereload/platform/services/IPlatformHelper.java new file mode 100644 index 0000000..60b7305 --- /dev/null +++ b/common/src/main/java/jerozgen/languagereload/platform/services/IPlatformHelper.java @@ -0,0 +1,43 @@ +package jerozgen.languagereload.platform.services; + +import java.nio.file.Path; + +public interface IPlatformHelper { + /** + * Gets the name of the current platform + * + * @return The name of the current platform + */ + String getPlatformName(); + + /** + * Checks if a mod with the given id is loaded. + * + * @param modId The mod to check if it is loaded + * @return True if the mod is loaded, false otherwise + */ + boolean isModLoaded(String modId); + + /** + * Check if the game is currently in a development environment. + * + * @return True if in a development environment, false otherwise + */ + boolean isDevelopmentEnvironment(); + + /** + * Gets the config directory + * + * @return The config directory + */ + Path getConfigDir(); + + /** + * Gets the name of the environment type as a string. + * + * @return The name of the environment type + */ + default String getEnvironmentName() { + return isDevelopmentEnvironment() ? "development" : "production"; + } +} diff --git a/common/src/main/resources/META-INF/accesstransformer.cfg b/common/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 0000000..765ec48 --- /dev/null +++ b/common/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1,4 @@ +public net.minecraft.client.gui.screens.options.LanguageSelectScreen$LanguageSelectionList +public net.minecraft.client.multiplayer.ClientChunkCache$Storage + +public-f net.minecraft.client.gui.components.AbstractSelectionList getEntryAtPosition(DD)Lnet/minecraft/client/gui/components/AbstractSelectionList$Entry; # getEntryAtPosition diff --git a/src/main/resources/assets/languagereload/icon.png b/common/src/main/resources/assets/languagereload/icon.png similarity index 100% rename from src/main/resources/assets/languagereload/icon.png rename to common/src/main/resources/assets/languagereload/icon.png diff --git a/src/main/resources/assets/languagereload/lang/be_by.json b/common/src/main/resources/assets/languagereload/lang/be_by.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/be_by.json rename to common/src/main/resources/assets/languagereload/lang/be_by.json diff --git a/src/main/resources/assets/languagereload/lang/en_us.json b/common/src/main/resources/assets/languagereload/lang/en_us.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/en_us.json rename to common/src/main/resources/assets/languagereload/lang/en_us.json diff --git a/src/main/resources/assets/languagereload/lang/es_ar.json b/common/src/main/resources/assets/languagereload/lang/es_ar.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/es_ar.json rename to common/src/main/resources/assets/languagereload/lang/es_ar.json diff --git a/src/main/resources/assets/languagereload/lang/es_es.json b/common/src/main/resources/assets/languagereload/lang/es_es.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/es_es.json rename to common/src/main/resources/assets/languagereload/lang/es_es.json diff --git a/src/main/resources/assets/languagereload/lang/et_ee.json b/common/src/main/resources/assets/languagereload/lang/et_ee.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/et_ee.json rename to common/src/main/resources/assets/languagereload/lang/et_ee.json diff --git a/src/main/resources/assets/languagereload/lang/fr_fr.json b/common/src/main/resources/assets/languagereload/lang/fr_fr.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/fr_fr.json rename to common/src/main/resources/assets/languagereload/lang/fr_fr.json diff --git a/src/main/resources/assets/languagereload/lang/it_it.json b/common/src/main/resources/assets/languagereload/lang/it_it.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/it_it.json rename to common/src/main/resources/assets/languagereload/lang/it_it.json diff --git a/src/main/resources/assets/languagereload/lang/ja_jp.json b/common/src/main/resources/assets/languagereload/lang/ja_jp.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/ja_jp.json rename to common/src/main/resources/assets/languagereload/lang/ja_jp.json diff --git a/src/main/resources/assets/languagereload/lang/ko_kr.json b/common/src/main/resources/assets/languagereload/lang/ko_kr.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/ko_kr.json rename to common/src/main/resources/assets/languagereload/lang/ko_kr.json diff --git a/src/main/resources/assets/languagereload/lang/ms_my.json b/common/src/main/resources/assets/languagereload/lang/ms_my.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/ms_my.json rename to common/src/main/resources/assets/languagereload/lang/ms_my.json diff --git a/src/main/resources/assets/languagereload/lang/pl_pl.json b/common/src/main/resources/assets/languagereload/lang/pl_pl.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/pl_pl.json rename to common/src/main/resources/assets/languagereload/lang/pl_pl.json diff --git a/src/main/resources/assets/languagereload/lang/pt_br.json b/common/src/main/resources/assets/languagereload/lang/pt_br.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/pt_br.json rename to common/src/main/resources/assets/languagereload/lang/pt_br.json diff --git a/src/main/resources/assets/languagereload/lang/ru_ru.json b/common/src/main/resources/assets/languagereload/lang/ru_ru.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/ru_ru.json rename to common/src/main/resources/assets/languagereload/lang/ru_ru.json diff --git a/src/main/resources/assets/languagereload/lang/tt_ru.json b/common/src/main/resources/assets/languagereload/lang/tt_ru.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/tt_ru.json rename to common/src/main/resources/assets/languagereload/lang/tt_ru.json diff --git a/src/main/resources/assets/languagereload/lang/uk_ua.json b/common/src/main/resources/assets/languagereload/lang/uk_ua.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/uk_ua.json rename to common/src/main/resources/assets/languagereload/lang/uk_ua.json diff --git a/src/main/resources/assets/languagereload/lang/vi_vn.json b/common/src/main/resources/assets/languagereload/lang/vi_vn.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/vi_vn.json rename to common/src/main/resources/assets/languagereload/lang/vi_vn.json diff --git a/src/main/resources/assets/languagereload/lang/zh_cn.json b/common/src/main/resources/assets/languagereload/lang/zh_cn.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/zh_cn.json rename to common/src/main/resources/assets/languagereload/lang/zh_cn.json diff --git a/src/main/resources/assets/languagereload/lang/zh_tw.json b/common/src/main/resources/assets/languagereload/lang/zh_tw.json similarity index 98% rename from src/main/resources/assets/languagereload/lang/zh_tw.json rename to common/src/main/resources/assets/languagereload/lang/zh_tw.json index 4aec7d1..fb00f46 100644 --- a/src/main/resources/assets/languagereload/lang/zh_tw.json +++ b/common/src/main/resources/assets/languagereload/lang/zh_tw.json @@ -1,20 +1,20 @@ -{ - "language.default.tooltip": "Minecraft 預設語言", - - "options.languagereload.title": "Language Reload 選項", - "options.languagereload.multilingualItemSearch": "多語言搜尋", - "options.languagereload.multilingualItemSearch.tooltip": "啟用在創造物品欄和配方手冊的所選語言搜尋。", - "options.languagereload.removableDefaultLanguage": "預設語言", - "options.languagereload.removableDefaultLanguage.removable": "可移除", - "options.languagereload.removableDefaultLanguage.removable.tooltip": "允許取消選擇「English (US)」。可能對除錯有用。", - "options.languagereload.removableDefaultLanguage.fixed": "已固定", - "options.languagereload.removableDefaultLanguage.fixed.tooltip": "防止取消選擇「English (US)」。", - - "debug.reload_languages.help": "F3 + J = 重新載入語言(同時按住 Shift 來循環切換語言)", - "debug.reload_languages.message": "已重新載入語言", - "debug.reload_languages.switch.success": "設置語言到 %s", - "debug.reload_languages.switch.failure": "無法循環切換語言。在設定中切換至另一種語言後再試一次", - - "modmenu.summaryTranslation.languagereload": "降低載入時間並增加了後備語言。", - "modmenu.descriptionTranslation.languagereload": "加速語言切換速度,並在語言選單內引進了搜尋框和多重選擇,也讓你可以多語言搜尋物品,並增加了透過 Shift + F3 + J 循環切換語言與 F3 + J 重新載入語言的除錯功能。" -} +{ + "language.default.tooltip": "Minecraft 預設語言", + + "options.languagereload.title": "Language Reload 選項", + "options.languagereload.multilingualItemSearch": "多語言搜尋", + "options.languagereload.multilingualItemSearch.tooltip": "啟用在創造物品欄和配方手冊的所選語言搜尋。", + "options.languagereload.removableDefaultLanguage": "預設語言", + "options.languagereload.removableDefaultLanguage.removable": "可移除", + "options.languagereload.removableDefaultLanguage.removable.tooltip": "允許取消選擇「English (US)」。可能對除錯有用。", + "options.languagereload.removableDefaultLanguage.fixed": "已固定", + "options.languagereload.removableDefaultLanguage.fixed.tooltip": "防止取消選擇「English (US)」。", + + "debug.reload_languages.help": "F3 + J = 重新載入語言(同時按住 Shift 來循環切換語言)", + "debug.reload_languages.message": "已重新載入語言", + "debug.reload_languages.switch.success": "設置語言到 %s", + "debug.reload_languages.switch.failure": "無法循環切換語言。在設定中切換至另一種語言後再試一次", + + "modmenu.summaryTranslation.languagereload": "降低載入時間並增加了後備語言。", + "modmenu.descriptionTranslation.languagereload": "加速語言切換速度,並在語言選單內引進了搜尋框和多重選擇,也讓你可以多語言搜尋物品,並增加了透過 Shift + F3 + J 循環切換語言與 F3 + J 重新載入語言的除錯功能。" +} diff --git a/src/main/resources/assets/languagereload/lang/zlm_arab.json b/common/src/main/resources/assets/languagereload/lang/zlm_arab.json similarity index 100% rename from src/main/resources/assets/languagereload/lang/zlm_arab.json rename to common/src/main/resources/assets/languagereload/lang/zlm_arab.json diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add.png diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add_highlighted.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add_highlighted.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add_highlighted.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/add_highlighted.png diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down.png diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down_highlighted.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down_highlighted.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down_highlighted.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_down_highlighted.png diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up.png diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up_highlighted.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up_highlighted.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up_highlighted.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/move_up_highlighted.png diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove.png diff --git a/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove_highlighted.png b/common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove_highlighted.png similarity index 100% rename from src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove_highlighted.png rename to common/src/main/resources/assets/languagereload/textures/gui/sprites/language_selection/remove_highlighted.png diff --git a/common/src/main/resources/languagereload.accesswidener b/common/src/main/resources/languagereload.accesswidener new file mode 100644 index 0000000..c004664 --- /dev/null +++ b/common/src/main/resources/languagereload.accesswidener @@ -0,0 +1,6 @@ +accessWidener v2 named + +accessible class net/minecraft/client/gui/screens/options/LanguageSelectScreen$LanguageSelectionList +accessible class net/minecraft/client/multiplayer/ClientChunkCache$Storage + +extendable method net/minecraft/client/gui/components/AbstractSelectionList getEntryAtPosition (DD)Lnet/minecraft/client/gui/components/AbstractSelectionList$Entry; diff --git a/src/main/resources/languagereload.mixins.json b/common/src/main/resources/languagereload.mixins.json similarity index 54% rename from src/main/resources/languagereload.mixins.json rename to common/src/main/resources/languagereload.mixins.json index 6d128d3..2c9f451 100644 --- a/src/main/resources/languagereload.mixins.json +++ b/common/src/main/resources/languagereload.mixins.json @@ -7,21 +7,21 @@ "AdvancementsScreenMixin", "AdvancementTabMixin", "AdvancementWidgetAccessor", - "BookScreenAccessor", - "ClientChunkManagerAccessor", - "ClientChunkMapAccessor", - "GameOptionsMixin", - "KeyboardMixin", + "BookViewScreenAccessor", + "ClientChunkCacheAccessor", + "ClientChunkCacheStorageAccessor", + "ClientLanguageMixin", + "KeyboardHandlerMixin", "LanguageManagerMixin", "LanguageMixin", - "LanguageOptionsScreenMixin", "LanguageSelectionListWidgetAccessor", - "RecipeBookWidgetMixin", - "SearchManagerMixin", + "LanguageSelectScreenMixin", + "OptionsMixin", + "RecipeBookComponentMixin", + "SessionSearchTreesMixin", "SignTextAccessor", - "TextDisplayEntityAccessor", - "TranslatableTextContentMixin", - "TranslationStorageMixin" + "TextDisplayAccessor", + "TranslatableContentsMixin" ], "injectors": { "defaultRequire": 1 diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts new file mode 100644 index 0000000..c2ba888 --- /dev/null +++ b/fabric/build.gradle.kts @@ -0,0 +1,45 @@ +import org.gradle.kotlin.dsl.support.uppercaseFirstChar + +plugins { + id("multiloader-loader") + alias(libs.plugins.loom) +} + +dependencies { + minecraft(libs.minecraft) + mappings(loom.layered { + officialMojangMappings() + parchment("org.parchmentmc.data:parchment-${libs.versions.parchmentmc.get()}:${libs.versions.parchment.get()}@zip") + }) + + modImplementation(libs.fabric.loader) + modImplementation(libs.fabric.api) + + modImplementation(libs.modmenu) +} + +loom { + val accessWidener = common.file("src/main/resources/${mod.id}.accesswidener") + + if (accessWidener.exists()) { + accessWidenerPath.set(accessWidener) + } + + mixin { + defaultRefmapName.set("${mod.id}.refmap.json") + } + + runs { + configureEach { + configName = "Fabric ${name.uppercaseFirstChar()}" + runDir("runs/${name}") + ideConfigGenerated(true) + + vmArgs( + "-XX:+AllowEnhancedClassRedefinition", + "-XX:+IgnoreUnrecognizedVMOptions", + "-Dmixin.debug.export=true" + ) + } + } +} \ No newline at end of file diff --git a/src/main/java/jerozgen/languagereload/config/ModMenuEntrypoint.java b/fabric/src/main/java/jerozgen/languagereload/config/ModMenuEntrypoint.java similarity index 100% rename from src/main/java/jerozgen/languagereload/config/ModMenuEntrypoint.java rename to fabric/src/main/java/jerozgen/languagereload/config/ModMenuEntrypoint.java diff --git a/fabric/src/main/java/jerozgen/languagereload/fabric/platform/FabricPlatformHelper.java b/fabric/src/main/java/jerozgen/languagereload/fabric/platform/FabricPlatformHelper.java new file mode 100644 index 0000000..7f49eee --- /dev/null +++ b/fabric/src/main/java/jerozgen/languagereload/fabric/platform/FabricPlatformHelper.java @@ -0,0 +1,28 @@ +package jerozgen.languagereload.fabric.platform; + +import jerozgen.languagereload.platform.services.IPlatformHelper; +import net.fabricmc.loader.api.FabricLoader; + +import java.nio.file.Path; + +public class FabricPlatformHelper implements IPlatformHelper { + @Override + public String getPlatformName() { + return "Fabric"; + } + + @Override + public boolean isModLoaded(String modId) { + return FabricLoader.getInstance().isModLoaded(modId); + } + + @Override + public Path getConfigDir() { + return FabricLoader.getInstance().getConfigDir(); + } + + @Override + public boolean isDevelopmentEnvironment() { + return FabricLoader.getInstance().isDevelopmentEnvironment(); + } +} \ No newline at end of file diff --git a/fabric/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java b/fabric/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java new file mode 100644 index 0000000..c2c327f --- /dev/null +++ b/fabric/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java @@ -0,0 +1,83 @@ +package jerozgen.languagereload.mixin; + +import com.google.common.collect.Maps; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import jerozgen.languagereload.LanguageReload; +import jerozgen.languagereload.access.IClientLanguage; +import jerozgen.languagereload.helper.ClientLanguageHelper; +import net.minecraft.client.resources.language.ClientLanguage; +import net.minecraft.locale.DeprecatedTranslationsInfo; +import net.minecraft.locale.Language; +import net.minecraft.server.packs.resources.ResourceManager; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +@Mixin(ClientLanguage.class) +abstract class ClientLanguageMixin extends Language implements IClientLanguage { + @Unique private static Map> languagereload_separateTranslationsOnLoad; + @Unique private final Map languagereload_targetLanguageByThread = Maps.newConcurrentMap(); + @Unique private Map> languagereload_separateTranslations; + + @Inject(method = "", at = @At("RETURN")) + void onConstructed(Map translations, boolean rightToLeft, CallbackInfo ci) { + languagereload_separateTranslations = languagereload_separateTranslationsOnLoad; + languagereload_separateTranslationsOnLoad = null; + } + + @WrapOperation(method = "loadFrom(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resources/language/ClientLanguage;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/locale/DeprecatedTranslationsInfo;applyToMap(Ljava/util/Map;)V")) + private static void onLoadFrom$applyDeprecatedTranslationsInfo(DeprecatedTranslationsInfo data, Map translations, Operation applier) { + applier.call(data, translations); + for (Map map : languagereload_separateTranslationsOnLoad.values()) { + applier.call(data, map); + } + } + + @Inject(method = "loadFrom(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resources/language/ClientLanguage;", + at = @At("HEAD")) + private static void onLoadFrom(ResourceManager resourceManager, List filenames, boolean defaultRightToLeft, CallbackInfoReturnable cir) { + languagereload_separateTranslationsOnLoad = Maps.newHashMap(); + } + + @Redirect(method = "appendFrom", at = @At(value = "INVOKE", + target = "Lnet/minecraft/locale/Language;loadFromJson(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V")) + private static void onInternalLoad$saveSeparately(InputStream stream, BiConsumer output, String languageName) { + ClientLanguageHelper.saveSeparately(stream, output, languagereload_separateTranslationsOnLoad, languageName); + } + + @Override + public String languagereload_get(String key) { + var targetLanguage = languagereload_getTargetLanguage(); + if (targetLanguage != null) { + var targetTranslations = languagereload_separateTranslations.get(targetLanguage); + return targetTranslations == null ? "" : targetTranslations.getOrDefault(key, ""); + } + return this.getOrDefault(key); + } + + @Override + public String languagereload_getTargetLanguage() { + return languagereload_targetLanguageByThread.get(Thread.currentThread().threadId()); + } + + @Override + public void languagereload_setTargetLanguage(@Nullable String value) { + var threadId = Thread.currentThread().threadId(); + if (value == null) + languagereload_targetLanguageByThread.remove(threadId); + else + languagereload_targetLanguageByThread.put(threadId, value); + } +} \ No newline at end of file diff --git a/fabric/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java b/fabric/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java new file mode 100644 index 0000000..068b8e3 --- /dev/null +++ b/fabric/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java @@ -0,0 +1,23 @@ +package jerozgen.languagereload.mixin; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import jerozgen.languagereload.helper.SessionSearchTreesHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.List; + +@Mixin(value = net.minecraft.client.multiplayer.SessionSearchTrees.class, priority = 990) +abstract class SessionSearchTreesMixin { + @WrapOperation(method = {"method_60365"},at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;getTooltipLines(Lnet/minecraft/world/item/Item$TooltipContext;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/item/TooltipFlag;)Ljava/util/List;")) + private static List addFallbackTranslationsToSearchTooltips(ItemStack items, Item.TooltipContext context, Player player, TooltipFlag type, Operation> operation) { + return SessionSearchTreesHelper.addFallbackTranslationsToSearchTooltips(items, context, player, type, operation); + } +} diff --git a/fabric/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper b/fabric/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper new file mode 100644 index 0000000..0e52648 --- /dev/null +++ b/fabric/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper @@ -0,0 +1 @@ +jerozgen.languagereload.fabric.platform.FabricPlatformHelper \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json similarity index 77% rename from src/main/resources/fabric.mod.json rename to fabric/src/main/resources/fabric.mod.json index 3f71c76..5aa034b 100644 --- a/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -1,11 +1,11 @@ { "schemaVersion": 1, - "id": "languagereload", - "version": "${version}", - "name": "Language Reload", - "description": "Reduces load times and adds fallbacks for languages.", + "id": "${modId}", + "version": "${modVersion}", + "name": "${modName}", + "description": "${modDescription}", "authors": [ - {"name": "Jerozgen", "contact": {"homepage": "https://github.com/Jerozgen", "discord": "jerozgen"}} + {"name": "${modAuthor}", "contact": {"homepage": "https://github.com/Jerozgen", "discord": "jerozgen"}} ], "contributors": [ {"name": "Benonardo", "contact": {"homepage": "https://github.com/Benonardo"}}, @@ -33,17 +33,17 @@ {"name": "dirtTW — Chinese Traditional", "contact": {"homepage": "https://github.com/yichifauzi"}} ], "contact": { - "homepage": "https://modrinth.com/mod/language-reload", - "sources": "https://github.com/Jerozgen/LanguageReload", - "issues": "https://github.com/Jerozgen/LanguageReload/issues" + "homepage": "${modHomepage}", + "sources": "${modSources}", + "issues": "${modIssues}" }, - "license": "MIT", - "icon": "assets/languagereload/icon.png", + "license": "${modLicense}", + "icon": "assets/${modId}/icon.png", "environment": "client", "mixins": [ - "languagereload.mixins.json" + "${modId}.mixins.json" ], - "accessWidener": "languagereload.accesswidener", + "accessWidener": "${modId}.accesswidener", "entrypoints": { "modmenu": [ "jerozgen.languagereload.config.ModMenuEntrypoint" @@ -51,8 +51,9 @@ }, "depends": { "fabric-resource-loader-v0": "*", - "fabricloader": ">=0.16.14", - "minecraft": ">=1.21.6" + "fabricloader": ">=${fabricLoaderVersion}", + "minecraft": "~${minecraftVersion}", + "java": ">=${javaVersion}" }, "suggests": { "modmenu": "*" @@ -60,9 +61,9 @@ "custom": { "modmenu": { "links": { - "modmenu.modrinth": "https://modrinth.com/mod/language-reload", - "modmenu.curseforge": "https://www.curseforge.com/minecraft/mc-mods/language-reload" + "modmenu.modrinth": "${modLinksModrinth}", + "modmenu.curseforge": "${modLinksCurseforge}" } } } -} +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 206d18f..800262e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,19 +1,74 @@ -# Done to increase the memory available to gradle. -org.gradle.jvmargs=-Xmx1G - -# Fabric Properties -# check these on https://modmuss50.me/fabric.html -minecraft_version=1.21.6 -yarn_mappings=1.21.6+build.1 -loader_version=0.16.14 -fabric_version=0.127.0+1.21.6 - -# Mod Properties -mod_version=1.7.4 -maven_group=jerozgen -archives_base_name=language-reload - -# Dependencies -# https://maven.terraformersmc.com/releases/com/terraformersmc/modmenu -# https://modrinth.com/mod/modmenu/versions -modmenu_version=15.0.0-beta.1 +# ================================================================== +# IMPORTANT: +# Every property added here must also be included in the 'expandProps' +# map in buildSrc/src/main/kotlin/multiloader-common.gradle.kts +# ================================================================== + +# ================================== +# Gradle configuration +# ================================== + +# Sets the default memory allocation for Gradle commands. +# This value can be overridden by user or command line properties. +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=warn + +# ================================== +# Project properties +# ================================== + +# Mod details + +# Specifies the mod version following semantic versioning. +# See https://semver.org/ for details. +mod.version=1.7.4 + +# Defines the group ID for the mod. +# This is important when publishing artifacts to a Maven repository. +# The group should match the base package of the mod's source code. +# Refer to https://maven.apache.org/guides/mini/guide-naming-conventions.html for guidelines. +mod.group=jerozgen +mod.archives_base_name=language-reload + +# A unique identifier for the mod. +# Must be lowercase (using the English locale) and match the regex [a-z][a-z0-9_]{1,63}. +# This should correspond to the constant declared in the main mod class annotated with @Mod. +mod.id=languagereload + +# The human-readable display name for the mod. +mod.name=Language Reload + +# The mod's author(s). This a simple text string used for display in the mod list. +mod.author=Jerozgen + +# The license under which the mod is released. +# For license options, see https://choosealicense.com/. +mod.license=MIT + +# Additional credits for the mod. +mod.credits=Benonardo; Alex Gazmanovich - Belarusian; Madis0 - Estonian; Texaliuz - Argentinian Spanish; BroxyZF - Spanish; Calvineries - French; glaav - Italian; ookkoouu - Japanese; wavgado, DominoKorean - Korean; NuruddinPlays - Malay, Malay (Jawi); PRO100KatYT - Polish; FITFC - Brazilian Portuguese; Amirhan-Taipovjan-Greatest-I - Tatar; Altegar - Ukrainian; ImVietnam - Vietnamese; IceAlin, GodGun968, buiawpkgew1, Cccc_owo, Andypsl8 - Chinese Simplified; xMikux, dirtTW - Chinese Traditional + +# A description of the mod. This is a simple text string used for display in the mod list. +# This supports multiline text; newline characters (\n) will be processed accordingly. +mod.description=Reduces load times and adds fallbacks for languages. + +# A URL for your mod's homepage. +# This could be a link to a Modrinth page, CurseForge page, your website, or a GitHub repository. +mod.homepage=https://modrinth.com/mod/language-reload + +# A URL for your mod's source code. +mod.sources=https://github.com/Jerozgen/LanguageReload + +# A URL pointing to your project's issue tracker (e.g., GitHub Issues). +# This gives users a direct link to report bugs. +mod.issues=https://github.com/Jerozgen/LanguageReload/issues + +# A URL pointing to your project's Modrinth page. +mod.links.modrinth=https://modrinth.com/mod/language-reload + +# A URL pointing to your project's CurseForge page. +mod.links.curseforge=https://curseforge.com/minecraft/mc-mods/language-reload diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..50fcc71 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,77 @@ +[versions] +java = "21" + +# Plugins +typesafe-conventions = "0.7.3" + +# ModDevGradle +# See https://projects.neoforged.net/neoforged/moddevgradle for new versions +moddev = "2.0.95" + +# Fabric Loom +# See https://fabricmc.net/develop/ for new versions +loom = "1.10-SNAPSHOT" + +# Minecraft version must agree with the Neo version to get a valid artifact +# Minecraft version range can use any release version of Minecraft as bounds. +# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly +# as they do not follow standard versioning conventions. +minecraft = "1.21.6" +minecraft-range = "[1.21.6,)" + +# The version of ParchmentMC that is used +# See https://parchmentmc.org/docs/getting-started#choose-a-version for new versions +parchment = "2025.06.29" +parchmentmc = "1.21.6" + +# NeoForge +# See https://projects.neoforged.net/neoforged/neoforge for new versions +# NeoForge version must agree with the Minecraft version to get a valid artifact +# NeoForge version range can use any version of Neo as bound +# Loader version range can only use the major version of FML as bounds +neoforge = "21.6.19-beta" +neoforge-range = "[21.6.0,)" +neoforge-loader-range = "[4,)" +# This is the version of minecraft that the 'common' project uses. +# You can find a list of all versions here https://projects.neoforged.net/neoforged/neoform +neoform = "1.21.6-20250617.151856" + +# Fabric +# See https://fabricmc.net/develop/ for new versions +fabric-loader = "0.16.14" +fabric-api = "0.128.0+1.21.6" + +# Mixins +# https://github.com/SpongePowered/Mixin +mixin = "0.8.5" +# https://github.com/LlamaLad7/MixinExtras +# NeoForge 20.2.84+ includes MixinExtras already. +mixinextras = "0.4.1" + +# ModMenu +# https://maven.terraformersmc.com/releases/com/terraformersmc/modmenu +# https://modrinth.com/mod/modmenu/versions +modmenu = "15.0.0-beta.3" + +[libraries] +# Common +minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" } + +# NeoForge +neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" } + +# Fabric +fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" } +fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" } + +# Mixin +mixin = { group = "org.spongepowered", name = "mixin", version.ref = "mixin" } +mixinextras-common = { group = "io.github.llamalad7", name = "mixinextras-common", version.ref = "mixinextras" } + +# ModMenu +modmenu = { group = "com.terraformersmc", name = "modmenu", version.ref = "modmenu" } + +[plugins] +moddev = { id = "net.neoforged.moddev", version.ref = "moddev" } +loom = { id = "fabric-loom", version.ref = "loom" } +typesafe-conventions = { id = "dev.panuszewski.typesafe-conventions", version.ref = "typesafe-conventions" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..2c3521197d7c4586c843d1d3e9090525f1898cde 100644 GIT binary patch delta 3889 zcmV-156^*Y>Trk?aBtSQ(D-o$(D8Px^?ZI-PUB? z*1fv!{YdHme3Fc8%cR@*@zc5A_nq&2=R47Hp@$-JF4Fz*;SLw5}K^y>s-s;V!}b2i=5=M- zComP?ju>8Fe@=H@rlwe1l`J*6BTTo`9b$zjQ@HxrAhp0D#u?M~TxGC_!?ccCHCjt| zF*PgJf@kJB`|Ml}cmsyrAjO#Kjr^E5p29w+#>$C`Q|54BoDv$fQ9D?3n32P9LPMIzu?LjNqggOH=1@T{9bMn*u8(GI z!;MLTtFPHal^S>VcJdiYqX0VU|Rn@A}C1xOlxCribxes0~+n2 z6qDaIA2$?e`opx3_KW!rAgbpzU)gFdjAKXh|5w``#F0R|c)Y)Du0_Ihhz^S?k^pk% zP>9|pIDx)xHH^_~+aA=^$M!<8K~Hy(71nJGf6`HnjtS=4X4=Hk^O71oNia2V{HUCC zoN3RSBS?mZCLw;l4W4a+D8qc)XJS`pUJ5X-f^1ytxwr`@si$lAE?{4G|o; zO0l>`rr?;~c;{ZEFJ!!3=7=FdGJ?Q^xfNQh4A?i;IJ4}B+A?4olTK(fN++3CRBP97 ze~lG9h%oegkn)lpW-4F8o2`*WW0mZHwHez`ko@>U1_;EC_6ig|Drn@=DMV9YEUSCa zIf$kHei3(u#zm9I!Jf(4t`Vm1lltJ&lVHy(eIXE8sy9sUpmz%I_gA#8x^Zv8%w?r2 z{GdkX1SkzRIr>prRK@rqn9j2wG|rUvf6PJbbin=yy-TAXrguvzN8jL$hUrIXzr^s5 zVM?H4;eM-QeRFr06@ifV(ocvk?_)~N@1c2ien56UjWXid6W%6ievIh)>dk|rIs##^kY67ib8Kw%#-oVFaXG7$ERyA9(NSJUvWiOA5H(!{uOpcW zg&-?iqPhds%3%tFspHDqqr;A!e@B#iPQjHd=c>N1LoOEGRehVoPOdxJ>b6>yc#o#+ zl8s8!(|NMeqjsy@0x{8^j0d00SqRZjp{Kj)&4UHYGxG+z9b-)72I*&J70?+8e?p_@ z=>-(>l6z5vYlP~<2%DU02b!mA{7mS)NS_eLe=t)sm&+Pmk?asOEKlkPQ)EUvvfC=;4M&*|I!w}(@V_)eUKLA_t^%`o z0PM9LV|UKTLnk|?M3u!|f2S0?UqZsEIH9*NJS-8lzu;A6-rr-ot=dg9SASoluZUkFH$7X; zP=?kYX!K?JL-b~<#7wU;b;eS)O;@?h%sPPk{4xEBxb{!sm0AY|f9cNvx6>$3F!*0c z75H=dy8JvTyO8}g1w{$9T$p~5en}AeSLoCF>_RT9YPMpChUjl310o*$QocjbH& zbnwg#gssR#jDVN{uEi3n(PZ%PFZ|6J2 z5_rBf0-u>e4sFe0*Km49ATi7>Kn0f9!uc|rRMR1Dtt6m1LW8^>qFlo}h$@br=Rmpi z;mI&>OF64Be{dVeHI8utrh)v^wsZ0jii%x8UgZ8TC%K~@I(4E};GFW&(;WVov}3%H zH;IhRkfD^(vt^DjZz(MyHLZxv8}qzPc(%itBkBwf_fC~sDBgh<3XAv5cxxfF3<2U! z03Xe&z`is!JDHbe;mNmfkH+_LFE*I2^mdL@7(@9DfAcP6O04V-ko;Rpgp<%Cj5r8Z zd0`sXoIjV$j)--;jA6Zy^D5&5v$o^>e%>Q?9GLm{i~p^lAn!%ZtF$I~>39XVZxk0b zROh^Bk9cE0AJBLozZIEmy7xG(yHWGztvfnr0(2ro1%>zsGMS^EMu+S$r=_;9 zWwZkgf7Q7`H9sLf2Go^Xy6&h~a&%s2_T@_Csf19MntF$aVFiFkvE3_hUg(B@&Xw@YJ zpL$wNYf78=0c@!QU6_a$>CPiXT7QAGDM}7Z(0z#_ZA=fmLUj{2z7@Ypo71UDy8GHr z-&TLKf6a5WCf@Adle3VglBt4>Z>;xF}}-S~B7<(%B;Y z0QR55{z-buw>8ilNM3u6I+D$S%?)(p>=eBx-HpvZj{7c*_?K=d()*7q?93us}1dq%FAFYLsW8ZTQ_XZLh`P2*6(NgS}qGcfGXVWpwsp#Rs}IuKbk*`2}&) zI^Vsk6S&Q4@oYS?dJ`NwMVBs6f57+RxdqVub#PvMu?$=^OJy5xEl0<5SLsSRy%%a0 zi}Y#1-F3m;Ieh#Y12UgW?-R)|eX>ZuF-2cc!1>~NS|XSF-6In>zBoZg+ml!6%fk7U zw0LHcz8VQk(jOJ+Yu)|^|15ufl$KQd_1eUZZzj`aC%umU6F1&D5XVWce_wAe(qCSZ zpX-QF4e{EmEVN9~6%bR5U*UT{eMHfcUo`jw*u?4r2s_$`}U{?NjvEm(u&<>B|%mq$Q3weshxk z76<``8vh{+nX`@9CB6IE&z)I%IFjR^LH{s1p|eppv=x za(g_jLU|xjWMAn-V7th$f({|LG8zzIE0g?cyW;%Dmtv%C+0@xVxPE^ zyZzi9P%JAD6ynwHptuzP`Kox7*9h7XSMonCalv;Md0i9Vb-c*!f0ubfk?&T&T}AHh z4m8Bz{JllKcdNg?D^%a5MFQ;#1z|*}H^qHLzW)L}wp?2tY7RejtSh8<;Zw)QGJYUm z|MbTxyj*McKlStlT9I5XlSWtQGN&-LTr2XyNU+`490rg?LYLMRnz-@oKqT1hpCGqP zyRXt4=_Woj$%n5ee<3zhLF>5>`?m9a#xQH+Jk_+|RM8Vi;2*XbK- zEL6sCpaGPzP>k8f4Kh|##_imt#zJMB;ir|JrMPGW`rityK1vHXMLy18%qmMQAm4WZ zP)i30KR&5vs15)C+8dM66&$k~i|ZT;KR&5vs15)C+8dJ(sAmGPijyIz6_bsqKLSFH zlOd=TljEpH0>h4zA*dCTK&emy#FCRCs1=i^sZ9bFmXjf<6_X39E(XY)00000#N437 delta 3990 zcmV;H4{7l5(*nQL0Kr1kzC=_KMxQY0|W5(lc#i zH*M1^P4B}|{x<+fkObwl)u#`$GxKKV&3pg*-y6R6txw)0qU|Clf9Uds3x{_-**c=7 z&*)~RHPM>Rw#Hi1R({;bX|7?J@w}DMF>dQQU2}9yj%iLjJ*KD6IEB2^n#gK7M~}6R zkH+)bc--JU^pV~7W=3{E*4|ZFpDpBa7;wh4_%;?XM-5ZgZNnVJ=vm!%a2CdQb?oTa z70>8rTb~M$5Tp!Se+4_OKWOB1LF+7gv~$$fGC95ToUM(I>vrd$>9|@h=O?eARj0MH zT4zo(M>`LWoYvE>pXvqG=d96D-4?VySz~=tPVNyD$XMshoTX(1ZLB5OU!I2OI{kb) zS8$B8Qm>wLT6diNnyJZC?yp{Kn67S{TCOt-!OonOK7$K)e-13U9GlnQXPAb&SJ0#3 z+vs~+4Qovv(%i8g$I#FCpCG^C4DdyQw3phJ(f#y*pvNDQCRZ~MvW<}fUs~PL=4??j zmhPyg<*I4RbTz|NHFE-DC7lf2=}-sGkE5e!RM%3ohM7_I^IF=?O{m*uUPH(V?gqyc(Rp?-Qu(3bBIL4Fz(v?=_Sh?LbK{nqZMD>#9D_hNhaV$0ef3@9V90|0u#|PUNTO>$F=qRhg1duaE z0`v~X3G{8RVT@kOa-pU+z8{JWyP6GF*u2e8eKr7a2t1fuqQy)@d|Qn(%YLZ62TWtoX@$nL}9?atE#Yw`rd(>cr0gY;dT9~^oL;u)zgHUvxc2I*b&ZkGM-iq=&(?kyO(3}=P! zRp=rErEyMT5UE9GjPHZ#T<`cnD)jyIL!8P{H@IU#`e8cAG5jMK zVyKw7--dAC;?-qEu*rMr$5@y535qZ6p(R#+fLA_)G~!wnT~~)|s`}&fA(s6xXN`9j zP#Fd3GBa#HeS{5&8p?%DKUyN^X9cYUc6vq}D_3xJ&d@=6j(6BZKPl?!k1?!`f3z&a zR4ZF60Mx7oBxLSxGuzA*Dy5n-d2K=+)6VMZh_0KetK|{e;E{8NJJ!)=_E~1uu=A=r zrn&gh)h*SFhsQJo!f+wKMIE;-EOaMSMB@aXRU(UcnJhZW^B^mgs|M9@5WF@s6B0p& zm#CTz)yiQCgURE{%hjxHcJ6G&>G9i`7MyftL!QQd5 z@RflRs?7)99?X`kHNt>W3l7YqscBpi*R2+fsgABor>KVOu(i(`03aytf2UA!&SC9v z!E}whj#^9~=XHMinFZ;6UOJjo=mmNaWkv~nC=qH9$s-8roGeyaW-E~SzZ3Gg>j zZ8}<320rg4=$`M0nxN!w(PtHUjeeU?MvYgWKZ6kkzABK;vMN0|U;X9abJleJA(xy<}5h5P(5 z{RzAFPvMnX2m0yH0Jn2Uo-p`daE|(O`YQiC#jB8;6bVIUf?SY(k$#C0`d6qT`>Xe0+0}Oj0=F&*D;PVe=Z<=0AGI<6$gYLwa#r` zm449x*fU;_+J>Mz!wa;T-wldoBB%&OEMJgtm#oaI60TSYCy7;+$5?q!zi5K`u66Wq zvg)Fx$s`V3Em{=OEY{3lmh_7|08ykS&U9w!kp@Ctuzqe1JFOGz6%i5}Kmm9>^=gih z?kRxqLA<3@e=}G4R_?phW{4DVr?`tPfyZSN@R=^;P;?!2bh~F1I|fB7P=V=9a6XU5 z<#0f>RS0O&rhc&nTRFOW7&QhevP0#>j0eq<1@D5yAlgMl5n&O9X|Vq}%RX}iNyRFF z7sX&u#6?E~bm~N|z&YikXC=I0E*8Z$v7PtWfjy)$e_Ez25fnR1Q=q1`;U!~U>|&YS zaOS8y!^ORmr2L4ik!IYR8@Dcx8MTC=(b4P6iE5CnrbI~7j7DmM8em$!da&D!6Xu)!vKPdLG z9f#)se|6=5yOCe)N6xDhPI!m81*dNe7u985zi%IVfOfJh69+#ag4ELzGne?o`eA`42K4T)h3S+s)5IT97%O>du- z0U54L8m4}rkRQ?QBfJ%DLssy^+a7Ajw;0&`NOTY4o;0-ivm9 zBz1C%nr_hQ)X)^QM6T1?=yeLkuG9Lf50(eH}`tFye;01&(p?8i+6h};VV-2B~qdxeC#=X z(JLlzy&fHkyi9Ksbcs~&r^%lh^2COldLz^H@X!s~mr9Dr6z!j+4?zkD@Ls7F8(t(f z9`U?P$Lmn*Y{K}aR4N&1N=?xtQ1%jqf1~pJyQ4SgBrEtR`j4lQuh7cqP49Em5cO=I zB(He2`iPN5M=Y0}h(IU$37ANTGx&|b-u1BYA*#dE(L-lptoOpo&th~E)_)y-`6kSH z3vvyVrcBwW^_XYReJ=JYd9OBQrzv;f2AQdZH#$Y{Y+Oa33M70XFI((fs;mB4e`<<{ ze4dv2B0V_?Ytsi>>g%qs*}oDGd5d(RNZ*6?7qNbdp7wP4T72=F&r?Ud#kZr8Ze5tB z_oNb7{G+(o2ajL$!69FW@jjPQ2a5C)m!MKKRirC$_VYIuVQCpf9rIms0GRDf)8AH${I`q^~5rjot@#3$2#zT2f`(N^P7Z;6(@EK$q*Jgif00I6*^ZGV+XB5uw*1R-@23yTw&WKD{s1;HTL;dO)%5i#`dc6b7;5@^{KU%N|A-$zsYw4)7LA{3`Zp>1 z-?K9_IE&z)dayUM)wd8K^29m-l$lFhi$zj0l!u~4;VGR6Y!?MAfBC^?QD53hy6VdD z@eUZIui}~L%#SmajaRq1J|#> z4m=o$vZ*34=ZWK2!QMNEcp2Lbc5N1q!lEDq(bz0b;WI9;e>l=CG9^n#ro`w>_0F$Q zfZ={2QyTkfByC&gy;x!r*NyXXbk=a%~~(#K?< zTke0HuF5{Q+~?@!KDXR|g+43$+;ab`^flS%miup_0OUTm=nIc%d5nLP)i308PIjl_YMF6cpQ__6&$n6it8K- z8PIjl_YMF6cpQ_!r)L8IivW`WdK8mBs6PXdjR2DYdK8nCs73=4j{uVadK8oNjwX|E wpAeHLsTu (client, screen) -> new ConfigScreen(screen)); + } +} diff --git a/neoforge/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java b/neoforge/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java new file mode 100644 index 0000000..1b68948 --- /dev/null +++ b/neoforge/src/main/java/jerozgen/languagereload/mixin/ClientLanguageMixin.java @@ -0,0 +1,82 @@ +package jerozgen.languagereload.mixin; + +import com.google.common.collect.Maps; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import jerozgen.languagereload.access.IClientLanguage; +import jerozgen.languagereload.helper.ClientLanguageHelper; +import net.minecraft.client.resources.language.ClientLanguage; +import net.minecraft.locale.DeprecatedTranslationsInfo; +import net.minecraft.locale.Language; +import net.minecraft.server.packs.resources.ResourceManager; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +@Mixin(ClientLanguage.class) +abstract class ClientLanguageMixin extends Language implements IClientLanguage { + @Unique private static Map> languagereload_separateTranslationsOnLoad; + @Unique private final Map languagereload_targetLanguageByThread = Maps.newConcurrentMap(); + @Unique private Map> languagereload_separateTranslations; + + @WrapOperation(method = "loadFrom(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resources/language/ClientLanguage;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/locale/DeprecatedTranslationsInfo;applyToMap(Ljava/util/Map;)V")) + private static void onLoadFrom$applyDeprecatedTranslationsInfo(DeprecatedTranslationsInfo data, Map translations, Operation applier) { + applier.call(data, translations); + for (Map map : languagereload_separateTranslationsOnLoad.values()) { + applier.call(data, map); + } + } + + @Inject(method = "loadFrom(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resources/language/ClientLanguage;", + at = @At("HEAD")) + private static void onLoadFrom(ResourceManager resourceManager, List filenames, boolean defaultRightToLeft, CallbackInfoReturnable cir) { + languagereload_separateTranslationsOnLoad = Maps.newHashMap(); + } + + @Redirect(method = "appendFrom(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;)V", at = @At(value = "INVOKE", + target = "Lnet/minecraft/locale/Language;loadFromJson(Ljava/io/InputStream;Ljava/util/function/BiConsumer;Ljava/util/function/BiConsumer;)V")) + private static void onInternalLoad$saveSeparately(InputStream stream, BiConsumer output, BiConsumer fallback, String languageName) { + ClientLanguageHelper.saveSeparately(stream, output, languagereload_separateTranslationsOnLoad, languageName); + } + + @Inject(method = "(Ljava/util/Map;Z)V", at = @At("RETURN")) + void onConstructed(Map translations, boolean rightToLeft, CallbackInfo ci) { + languagereload_separateTranslations = languagereload_separateTranslationsOnLoad; + languagereload_separateTranslationsOnLoad = null; + } + + @Override + public String languagereload_get(String key) { + var targetLanguage = languagereload_getTargetLanguage(); + if (targetLanguage != null) { + var targetTranslations = languagereload_separateTranslationsOnLoad.get(targetLanguage); + return targetTranslations == null ? "" : targetTranslations.getOrDefault(key, ""); + } + return this.getOrDefault(key); + } + + @Override + public String languagereload_getTargetLanguage() { + return languagereload_targetLanguageByThread.get(Thread.currentThread().threadId()); + } + + @Override + public void languagereload_setTargetLanguage(@Nullable String value) { + var threadId = Thread.currentThread().threadId(); + if (value == null) + languagereload_targetLanguageByThread.remove(threadId); + else + languagereload_targetLanguageByThread.put(threadId, value); + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java b/neoforge/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java new file mode 100644 index 0000000..8ea509e --- /dev/null +++ b/neoforge/src/main/java/jerozgen/languagereload/mixin/SessionSearchTreesMixin.java @@ -0,0 +1,24 @@ +package jerozgen.languagereload.mixin; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import jerozgen.languagereload.helper.SessionSearchTreesHelper; +import net.minecraft.client.multiplayer.SessionSearchTrees; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.List; + +@Mixin(value = SessionSearchTrees.class, priority = 990) +abstract class SessionSearchTreesMixin { + @WrapOperation(method = {"lambda$getTooltipLines$0"}, + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;getTooltipLines(Lnet/minecraft/world/item/Item$TooltipContext;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/item/TooltipFlag;)Ljava/util/List;")) + private static List addFallbackTranslationsToSearchTooltips(ItemStack items, Item.TooltipContext context, Player player, TooltipFlag type, Operation> operation) { + return SessionSearchTreesHelper.addFallbackTranslationsToSearchTooltips(items, context, player, type, operation); + } +} diff --git a/neoforge/src/main/java/jerozgen/languagereload/neoforge/platform/NeoForgePlatformHelper.java b/neoforge/src/main/java/jerozgen/languagereload/neoforge/platform/NeoForgePlatformHelper.java new file mode 100644 index 0000000..b8452b9 --- /dev/null +++ b/neoforge/src/main/java/jerozgen/languagereload/neoforge/platform/NeoForgePlatformHelper.java @@ -0,0 +1,30 @@ +package jerozgen.languagereload.neoforge.platform; + +import jerozgen.languagereload.platform.services.IPlatformHelper; +import net.neoforged.fml.ModList; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.FMLPaths; + +import java.nio.file.Path; + +public class NeoForgePlatformHelper implements IPlatformHelper { + @Override + public String getPlatformName() { + return "NeoForge"; + } + + @Override + public boolean isModLoaded(String modId) { + return ModList.get().isLoaded(modId); + } + + @Override + public Path getConfigDir() { + return FMLPaths.CONFIGDIR.get(); + } + + @Override + public boolean isDevelopmentEnvironment() { + return !FMLLoader.isProduction(); + } +} \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..64ffd4e --- /dev/null +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,134 @@ +# This file is the mod metadata manifest for the NeoForge Mod Loader. +# It tells the loader how to handle the mod(s) inside this JAR file. +# The format is TOML (Tom's Obvious, Minimal Language). +# Placeholders like ${modId} are typically replaced by your build script (e.g., Gradle) +# using values from a properties file (e.g., gradle.properties). + +# The name of the mod loader type to load. +# For standard mods written in Java using the @Mod annotation, this must be "javafml". +# Requirement: Mandatory +modLoader = "javafml" + +# A version range for the specified mod loader. +# For "javafml", this corresponds to the NeoForge version. +# The build script usually provides this value to ensure compatibility. +# Requirement: Mandatory +loaderVersion = "${neoforgeLoaderVersionRange}" + +# The license under which your mod is distributed. +# This is a mandatory field that helps users and pack creators understand what they are allowed to do with your mod. +# It is highly recommended to use a standard SPDX identifier (e.g., "MIT", "GPL-3.0-only"). +# You can review common open-source licenses at https://choosealicense.com/ +# Requirement: Mandatory +license = "${modLicense}" + +# A URL pointing to your project's issue tracker (e.g., GitHub Issues). +# This gives users a direct link to report bugs. +# Requirement: Optional +issueTrackerURL="${modIssues}" + +# This marks the beginning of the list of mods contained in this file. +# A single JAR can contain multiple mods by having multiple [[mods]] blocks. +# This entire block is considered mandatory for a mod to be loaded. +# Requirement: Mandatory +[[mods]] +# The unique identifier for your mod. +# This ID is used for resource locations (textures, models), registry names (items, blocks), commands, and dependency resolution. +# It must be 2-64 characters, start with a lowercase letter, and contain only lowercase letters (a-z), numbers (0-9), and underscores (_). +# Requirement: Mandatory +modId = "${modId}" + +# The version number of your mod. +# It is strongly recommended to use Semantic Versioning (SemVer). +# The placeholder ${modVersion} is typically configured to pull the version from your build script, which ensures it is consistent across your project. +# Requirement: Mandatory +version = "${modVersion}" + +# The human-readable name of your mod that is displayed in the in-game mod list. +# Requirement: Mandatory +displayName = "${modName}" + +# A URL that the game can query to check for updates to your mod. +# For this to work, the URL must point to a JSON file that follows the NeoForge update checker specification. +# See the specification at: https://docs.neoforged.net/docs/misc/updatechecker/ +# Requirement: Optional +# updateJSONURL="https://your-domain.com/updates.json" + +# A URL for your mod's homepage, which is linked in the in-game mod list UI. +# This could be a link to a Modrinth page, CurseForge page, your website, or a GitHub repository. +# Requirement: Optional +displayURL="${modHomepage}" + +# A path to a logo image file within your JAR's resources. +# This logo is displayed in the mod list next to your mod's name. +# The path is relative to the root of your resources directory (e.g., src/main/resources). +# Requirement: Optional +logoFile="${modId}/icon.png" + +# A free-form string where you can list the authors of the mod. +# Requirement: Optional +authors="${modAuthor}" + +# A detailed description of your mod. +# For longer text, use TOML's multiline string syntax to keep it readable. +# This text appears in the mod's information screen in the game. +# Requirement: Optional +description = '''${modDescription}''' + +# Credits and acknowledges for the mod shown on the mod list screen. +# Requirement: Optional +credits = '''${modCredits}''' + +# This block defines the dependencies for the mod specified in the header. +# The syntax [[dependencies."${modId}"]] links this dependency block to a specific mod defined in a [[mods]] block above. +[[dependencies."${modId}"]] +# The modId of the dependency. +# Requirement: Mandatory (within a dependency block) +modId = "neoforge" + +# The type of dependency. This is case-insensitive. +# "required": The game will not start if the dependency is missing or the version doesn't match. +# "optional": The game will start, but if the dependency is present, its version must match. +# "incompatible": The game will not start if this mod is present. +# "discouraged": The game will start, but will show a warning screen. +# Requirement: Mandatory (within a dependency block) +type = "required" + +# The compatible version range for the dependency, using Maven version range syntax. +# e.g., "[1.20.4,)" means version 1.20.4 or newer. +# Requirement: Mandatory (within a dependency block) +versionRange = "${neoforgeVersionRange}" + +# The order in which this mod should be loaded relative to the dependency. +# "NONE": No specific loading order is enforced. +# "BEFORE": This mod must be loaded *before* the dependency. +# "AFTER": This mod must be loaded *after* the dependency. +# Requirement: Optional +ordering = "NONE" + +# The physical side where this dependency is required. +# "BOTH": Required on the client and server. +# "CLIENT": Required only on the client. +# "SERVER": Required only on the server. +# Requirement: Optional +side = "BOTH" + +# The location of the mixin configuration file. +[[mixins]] +config="${modId}.mixins.json" + +[[dependencies."${modId}"]] +modId = "minecraft" +type = "required" +# This version range declares a minimum of the current Minecraft version up to, but not including, the next major version. +versionRange = "${minecraftVersionRange}" +ordering = "NONE" +side = "BOTH" + +# The features section allows you to declare requirements for the game environment itself. +# These are side-aware, so a client-side requirement won't prevent a server from starting. +# Requirement: Optional +# [features."${modId}"] +# This example declares that the mod requires OpenGL version 3.2 or higher to run on the client. +# This is side-aware and will not prevent a dedicated server from loading the mod. +# openGLVersion="[3.2,)" diff --git a/neoforge/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper b/neoforge/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper new file mode 100644 index 0000000..d85a9cb --- /dev/null +++ b/neoforge/src/main/resources/META-INF/services/jerozgen.languagereload.platform.services.IPlatformHelper @@ -0,0 +1 @@ +jerozgen.languagereload.neoforge.platform.NeoForgePlatformHelper \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index f91a4fe..0000000 --- a/settings.gradle +++ /dev/null @@ -1,9 +0,0 @@ -pluginManagement { - repositories { - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' - } - gradlePluginPortal() - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..4e47441 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,25 @@ +// This should match the folder name of the project, or else IDEA may complain (see https://youtrack.jetbrains.com/issue/IDEA-317606) +rootProject.name = "LanguageReload" + +pluginManagement { + repositories { + exclusiveContent { + forRepository { maven("https://maven.fabricmc.net") { name = "Fabric" } } + filter { + includeGroup("net.fabricmc") + includeGroup("fabric-loom") + } + } + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + // This plugin allows Gradle to automatically download arbitrary versions of Java for you + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" +} + +includeBuild("build-logic") + +include(":common", ":fabric", ":neoforge") \ No newline at end of file diff --git a/src/main/java/jerozgen/languagereload/access/ILanguage.java b/src/main/java/jerozgen/languagereload/access/ILanguage.java deleted file mode 100644 index 67e7451..0000000 --- a/src/main/java/jerozgen/languagereload/access/ILanguage.java +++ /dev/null @@ -1,12 +0,0 @@ -package jerozgen.languagereload.access; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.resource.language.TranslationStorage; - -@Environment(EnvType.CLIENT) -public interface ILanguage { - void languagereload_setTranslationStorage(TranslationStorage translationStorage); - - TranslationStorage languagereload_getTranslationStorage(); -} diff --git a/src/main/java/jerozgen/languagereload/access/ITranslationStorage.java b/src/main/java/jerozgen/languagereload/access/ITranslationStorage.java deleted file mode 100644 index 73f843e..0000000 --- a/src/main/java/jerozgen/languagereload/access/ITranslationStorage.java +++ /dev/null @@ -1,14 +0,0 @@ -package jerozgen.languagereload.access; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import org.jetbrains.annotations.Nullable; - -@Environment(EnvType.CLIENT) -public interface ITranslationStorage { - String languagereload_get(String key); - - @Nullable String languagereload_getTargetLanguage(); - - void languagereload_setTargetLanguage(@Nullable String value); -} diff --git a/src/main/java/jerozgen/languagereload/config/ConfigScreen.java b/src/main/java/jerozgen/languagereload/config/ConfigScreen.java deleted file mode 100644 index 69c875d..0000000 --- a/src/main/java/jerozgen/languagereload/config/ConfigScreen.java +++ /dev/null @@ -1,52 +0,0 @@ -package jerozgen.languagereload.config; - -import jerozgen.languagereload.LanguageReload; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.option.GameOptionsScreen; -import net.minecraft.client.gui.tooltip.Tooltip; -import net.minecraft.client.option.SimpleOption; -import net.minecraft.text.Text; - -@Environment(EnvType.CLIENT) -public class ConfigScreen extends GameOptionsScreen { - private static final SimpleOption MULTILINGUAL_SEARCH = SimpleOption.ofBoolean( - "options.languagereload.multilingualItemSearch", - SimpleOption.constantTooltip(Text.translatable("options.languagereload.multilingualItemSearch.tooltip")), - true, - value -> { - Config.getInstance().multilingualItemSearch = value; - Config.save(); - LanguageReload.reloadLanguages(); - }); - - private static final SimpleOption REMOVABLE_DEFAULT_LANGUAGE = SimpleOption.ofBoolean( - "options.languagereload.removableDefaultLanguage", - (value) -> value - ? Tooltip.of(Text.translatable("options.languagereload.removableDefaultLanguage.removable.tooltip")) - : Tooltip.of(Text.translatable("options.languagereload.removableDefaultLanguage.fixed.tooltip")), - (__, value) -> value - ? Text.translatable("options.languagereload.removableDefaultLanguage.removable") - : Text.translatable("options.languagereload.removableDefaultLanguage.fixed"), - false, - value -> { - Config.getInstance().removableDefaultLanguage = value; - Config.save(); - } - ); - - public ConfigScreen(Screen parent) { - super(parent, MinecraftClient.getInstance().options, Text.translatable("options.languagereload.title")); - MULTILINGUAL_SEARCH.setValue(Config.getInstance().multilingualItemSearch); - REMOVABLE_DEFAULT_LANGUAGE.setValue(Config.getInstance().removableDefaultLanguage); - } - - @Override - protected void addOptions() { - if (body != null) { - body.addAll(MULTILINGUAL_SEARCH, REMOVABLE_DEFAULT_LANGUAGE); - } - } -} diff --git a/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java b/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java deleted file mode 100644 index f788aae..0000000 --- a/src/main/java/jerozgen/languagereload/gui/LanguageListWidget.java +++ /dev/null @@ -1,111 +0,0 @@ -package jerozgen.languagereload.gui; - -import jerozgen.languagereload.access.ILanguageOptionsScreen; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.option.LanguageOptionsScreen; -import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.text.Text; -import net.minecraft.util.Colors; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.Nullable; - -import static org.lwjgl.glfw.GLFW.GLFW_KEY_DOWN; -import static org.lwjgl.glfw.GLFW.GLFW_KEY_ENTER; -import static org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE; -import static org.lwjgl.glfw.GLFW.GLFW_KEY_UP; - -public class LanguageListWidget extends AlwaysSelectedEntryListWidget { - private final Text title; - private final LanguageOptionsScreen screen; - - public LanguageListWidget(MinecraftClient client, LanguageOptionsScreen screen, int width, int height, Text title) { - super(client, width, height - 83 - 16, 32 + 16, 24, (int) (9f * 1.5f)); - this.title = title; - this.screen = screen; - centerListVertically = false; - } - - @Override - protected void renderHeader(DrawContext context, int x, int y) { - var headerText = title.copy().formatted(Formatting.UNDERLINE, Formatting.BOLD); - int headerPosX = x + width / 2 - client.textRenderer.getWidth(headerText) / 2; - int headerPosY = Math.min(this.getY() + 3, y); - context.drawTextWithShadow(client.textRenderer, headerText, headerPosX, headerPosY, Colors.WHITE); - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - var selectedEntry = this.getSelectedOrNull(); - if (selectedEntry == null) return super.keyPressed(keyCode, scanCode, modifiers); - - if (keyCode == GLFW_KEY_SPACE || keyCode == GLFW_KEY_ENTER) { - selectedEntry.toggle(); - this.setFocused(null); - ((ILanguageOptionsScreen) screen).languagereload_focusEntry(selectedEntry); - return true; - } - - if (Screen.hasShiftDown()) { - if (keyCode == GLFW_KEY_DOWN) { - selectedEntry.moveDown(); - return true; - } - if (keyCode == GLFW_KEY_UP) { - selectedEntry.moveUp(); - return true; - } - } - - return super.keyPressed(keyCode, scanCode, modifiers); - } - - // Remove hovering in scrollbar area - @Override - @Nullable - protected LanguageEntry getEntryAtPosition(double x, double y) { - var entry = super.getEntryAtPosition(x, y); - return entry != null && this.overflows() && x >= this.getScrollbarX() - ? null - : entry; - } - - @Override - protected void drawSelectionHighlight(DrawContext context, int y, int entryWidth, int entryHeight, int borderColor, int fillColor) { - if (this.overflows()) { - var x1 = this.getRowLeft() - 2; - var x2 = this.getScrollbarX(); - var y1 = y - 2; - var y2 = y + entryHeight + 2; - context.fill(x1, y1, x2, y2, borderColor); - context.fill(x1 + 1, y1 + 1, x2 - 1, y2 - 1, fillColor); - } else { - super.drawSelectionHighlight(context, y, entryWidth, entryHeight, borderColor, fillColor); - } - } - - public int getHoveredSelectionRight() { - return this.overflows() - ? this.getScrollbarX() - : this.getRowRight() - 2; - } - - public LanguageOptionsScreen getScreen() { - return screen; - } - - public int getRowHeight() { - return itemHeight; - } - - @Override - public int getRowWidth() { - return width; - } - - @Override - protected int getScrollbarX() { - return this.getRight() - 6; - } -} diff --git a/src/main/java/jerozgen/languagereload/mixin/BookScreenAccessor.java b/src/main/java/jerozgen/languagereload/mixin/BookScreenAccessor.java deleted file mode 100644 index 728838f..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/BookScreenAccessor.java +++ /dev/null @@ -1,11 +0,0 @@ -package jerozgen.languagereload.mixin; - -import net.minecraft.client.gui.screen.ingame.BookScreen; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(BookScreen.class) -public interface BookScreenAccessor { - @Accessor("cachedPageIndex") - void languagereload_setCachedPageIndex(int cachedPageIndex); -} diff --git a/src/main/java/jerozgen/languagereload/mixin/ClientChunkManagerAccessor.java b/src/main/java/jerozgen/languagereload/mixin/ClientChunkManagerAccessor.java deleted file mode 100644 index 97f64bc..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/ClientChunkManagerAccessor.java +++ /dev/null @@ -1,11 +0,0 @@ -package jerozgen.languagereload.mixin; - -import net.minecraft.client.world.ClientChunkManager; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(ClientChunkManager.class) -public interface ClientChunkManagerAccessor { - @Accessor("chunks") - ClientChunkManager.ClientChunkMap languagereload_getChunks(); -} diff --git a/src/main/java/jerozgen/languagereload/mixin/ClientChunkMapAccessor.java b/src/main/java/jerozgen/languagereload/mixin/ClientChunkMapAccessor.java deleted file mode 100644 index c30df21..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/ClientChunkMapAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package jerozgen.languagereload.mixin; - -import net.minecraft.client.world.ClientChunkManager; -import net.minecraft.world.chunk.WorldChunk; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.concurrent.atomic.AtomicReferenceArray; - -@Mixin(ClientChunkManager.ClientChunkMap.class) -public interface ClientChunkMapAccessor { - @Accessor("chunks") - AtomicReferenceArray languagereload_getChunks(); -} diff --git a/src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java b/src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java deleted file mode 100644 index a70c41d..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/KeyboardMixin.java +++ /dev/null @@ -1,96 +0,0 @@ -package jerozgen.languagereload.mixin; - -import jerozgen.languagereload.LanguageReload; -import jerozgen.languagereload.config.Config; -import net.minecraft.client.Keyboard; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.resource.language.LanguageDefinition; -import net.minecraft.client.util.InputUtil; -import net.minecraft.text.Text; -import net.minecraft.text.Texts; -import org.lwjgl.glfw.GLFW; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.ArrayList; -import java.util.Objects; - -@Mixin(Keyboard.class) -public abstract class KeyboardMixin { - @Shadow @Final private MinecraftClient client; - - @Shadow protected abstract void sendMessage(Text message); - @Shadow protected abstract void debugError(Text message); - @Shadow protected abstract void debugLog(Text text); - @Shadow protected abstract void debugLog(String key); - - @Unique - private void processLanguageReloadKeys() { - if (Screen.hasShiftDown()) { - var config = Config.getInstance(); - var languageManager = client.getLanguageManager(); - - var language = languageManager.getLanguage(config.previousLanguage); - var noLanguage = config.previousLanguage.equals(LanguageReload.NO_LANGUAGE); - if (language == null && !noLanguage) { - this.debugError(Text.translatable("debug.reload_languages.switch.failure")); - } else { - LanguageReload.setLanguage(config.previousLanguage, config.previousFallbacks); - var languages = new ArrayList() {{ - if (noLanguage) - add(Text.of("∅")); - if (language != null) - add(language.getDisplayText()); - addAll(config.fallbacks.stream() - .map(languageManager::getLanguage) - .filter(Objects::nonNull) - .map(LanguageDefinition::getDisplayText) - .toList()); - }}; - this.debugLog(Text.translatable("debug.reload_languages.switch.success", Texts.join(languages, Text.of(", ")))); - } - } else { - LanguageReload.reloadLanguages(); - this.debugLog("debug.reload_languages.message"); - } - } - - @Inject(method = "processF3", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/Keyboard;sendMessage(Lnet/minecraft/text/Text;)V", - ordinal = 6, shift = At.Shift.AFTER)) - private void onProcessF3$addHelp(int key, CallbackInfoReturnable cir) { - this.sendMessage(Text.translatable("debug.reload_languages.help")); - } - - @Inject(method = "processF3", at = @At("RETURN"), cancellable = true) - private void onProcessF3(int key, CallbackInfoReturnable cir) { - if (key == GLFW.GLFW_KEY_J) { - processLanguageReloadKeys(); - cir.setReturnValue(true); - } - } - - @Inject(method = "onKey", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Keyboard;debugCrashStartTime:J", ordinal = 0), cancellable = true) - private void onOnKey(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { - if (client.currentScreen != null && InputUtil.isKeyPressed(window, GLFW.GLFW_KEY_F3) && key == GLFW.GLFW_KEY_J) { - if (action != 0) { - processLanguageReloadKeys(); - } - ci.cancel(); - } - } - - @Inject(method = "onChar", at = @At("HEAD"), cancellable = true) - private void onOnChar(long window, int codePoint, int modifiers, CallbackInfo ci) { - if (InputUtil.isKeyPressed(window, GLFW.GLFW_KEY_F3) && InputUtil.isKeyPressed(window, GLFW.GLFW_KEY_J)) { - ci.cancel(); - } - } -} diff --git a/src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java b/src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java deleted file mode 100644 index e3a3bd2..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/LanguageMixin.java +++ /dev/null @@ -1,42 +0,0 @@ -package jerozgen.languagereload.mixin; - -import jerozgen.languagereload.access.ILanguage; -import net.minecraft.client.resource.language.TranslationStorage; -import net.minecraft.util.Language; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -// Fixes Server Translation API incompatibility (#56) -@Mixin(value = Language.class, priority = 990) -public class LanguageMixin implements ILanguage { - @Unique private @Nullable TranslationStorage translationStorage = null; - @Unique private static @Nullable TranslationStorage translationStorageOnSetInstance = null; - - - @Inject(method = "setInstance", at = @At("HEAD")) - private static void onSetInstance(Language language, CallbackInfo ci) { - if (language instanceof TranslationStorage translationStorage) { - translationStorageOnSetInstance = translationStorage; - } - } - - @Inject(method = "setInstance", at = @At("TAIL")) - private static void afterSetInstance(Language language, CallbackInfo ci) { - ((ILanguage) language).languagereload_setTranslationStorage(translationStorageOnSetInstance); - translationStorageOnSetInstance = null; - } - - @Override - public void languagereload_setTranslationStorage(TranslationStorage translationStorage) { - this.translationStorage = translationStorage; - } - - @Override - public TranslationStorage languagereload_getTranslationStorage() { - return translationStorage; - } -} diff --git a/src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java b/src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java deleted file mode 100644 index a8ed7f6..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/LanguageOptionsScreenMixin.java +++ /dev/null @@ -1,174 +0,0 @@ -package jerozgen.languagereload.mixin; - -import jerozgen.languagereload.LanguageReload; -import jerozgen.languagereload.access.ILanguageOptionsScreen; -import jerozgen.languagereload.config.Config; -import jerozgen.languagereload.gui.LanguageEntry; -import jerozgen.languagereload.gui.LanguageListWidget; -import net.minecraft.client.gui.navigation.GuiNavigationPath; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.option.GameOptionsScreen; -import net.minecraft.client.gui.screen.option.LanguageOptionsScreen; -import net.minecraft.client.gui.widget.DirectionalLayoutWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.gui.widget.TextWidget; -import net.minecraft.client.option.GameOptions; -import net.minecraft.client.resource.language.LanguageManager; -import net.minecraft.text.Text; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.*; -import java.util.stream.Stream; - -@Mixin(LanguageOptionsScreen.class) -public abstract class LanguageOptionsScreenMixin extends GameOptionsScreen implements ILanguageOptionsScreen { - @Unique private LanguageListWidget availableLanguageList; - @Unique private LanguageListWidget selectedLanguageList; - @Unique private TextFieldWidget searchBox; - @Unique private final LinkedList selectedLanguages = new LinkedList<>(); - @Unique private final Map languageEntries = new LinkedHashMap<>(); - - @Shadow private LanguageOptionsScreen.LanguageSelectionListWidget languageSelectionList; - - @Inject(method = "", at = @At("TAIL")) - void onConstructed(Screen parent, GameOptions options, LanguageManager languageManager, CallbackInfo ci) { - var currentLangCode = languageManager.getLanguage(); - if (!currentLangCode.equals(LanguageReload.NO_LANGUAGE)) - selectedLanguages.add(currentLangCode); - selectedLanguages.addAll(Config.getInstance().fallbacks); - languageManager.getAllLanguages().forEach((code, language) -> - languageEntries.put(code, new LanguageEntry(this::refresh, code, language, selectedLanguages))); - - layout.setHeaderHeight(48); - layout.setFooterHeight(53); - } - - @Inject(method = "initBody", at = @At("HEAD"), cancellable = true) - void onInitBody(CallbackInfo ci) { - languageSelectionList = LanguageSelectionListWidgetAccessor.languagereload_init(it(), client); - - var listWidth = Math.min(width / 2 - 4, 200); - availableLanguageList = new LanguageListWidget(client, it(), listWidth, height, Text.translatable("pack.available.title")); - selectedLanguageList = new LanguageListWidget(client, it(), listWidth, height, Text.translatable("pack.selected.title")); - availableLanguageList.setX(width / 2 - 4 - listWidth); - selectedLanguageList.setX(width / 2 + 4); - layout.addBody(availableLanguageList); - layout.addBody(selectedLanguageList); - refresh(); - - ci.cancel(); - } - - @Override - protected void initHeader() { - searchBox = new TextFieldWidget(textRenderer, width / 2 - 100, 22, 200, 20, searchBox, Text.empty()) { - @Override - public void setFocused(boolean focused) { - if (!isFocused() && focused) { - super.setFocused(true); - focusSearch(); - } else super.setFocused(focused); - } - }; - searchBox.setChangedListener(__ -> refresh()); - - var header = layout.addHeader(DirectionalLayoutWidget.vertical().spacing(5)); - header.getMainPositioner().alignHorizontalCenter(); - header.add(new TextWidget(title, textRenderer)); - header.add(searchBox); - } - - @Inject(method = "refreshWidgetPositions", at = @At("HEAD"), cancellable = true) - protected void onRefreshWidgetPositions(CallbackInfo ci) { - super.refreshWidgetPositions(); - - var listWidth = Math.min(width / 2 - 4, 200); - availableLanguageList.position(listWidth, layout); - selectedLanguageList.position(listWidth, layout); - availableLanguageList.setX(width / 2 - 4 - listWidth); - selectedLanguageList.setX(width / 2 + 4); - availableLanguageList.refreshScroll(); - selectedLanguageList.refreshScroll(); - - ci.cancel(); - } - - @Inject(method = "onDone", at = @At("HEAD"), cancellable = true) - private void onDone(CallbackInfo ci) { - if (client == null) return; - client.setScreen(parent); - - var language = selectedLanguages.peekFirst(); - if (language == null) { - LanguageReload.setLanguage(LanguageReload.NO_LANGUAGE, new LinkedList<>()); - } else { - var fallbacks = new LinkedList<>(selectedLanguages); - fallbacks.removeFirst(); - LanguageReload.setLanguage(language, fallbacks); - } - - ci.cancel(); - } - - @Unique - private void refresh() { - refreshList(selectedLanguageList, selectedLanguages.stream().map(languageEntries::get).filter(Objects::nonNull)); - refreshList(availableLanguageList, languageEntries.values().stream() - .filter(entry -> { - if (selectedLanguageList.children().contains(entry)) return false; - var query = searchBox.getText().toLowerCase(Locale.ROOT); - var langCode = entry.getCode().toLowerCase(Locale.ROOT); - var langName = entry.getLanguage().getDisplayText().getString().toLowerCase(Locale.ROOT); - return langCode.contains(query) || langName.contains(query); - })); - } - - @Unique - private void refreshList(LanguageListWidget list, Stream entries) { - var selectedEntry = list.getSelectedOrNull(); - list.setSelected(null); - list.children().clear(); - entries.forEach(entry -> { - list.children().add(entry); - entry.setParent(list); - if (entry == selectedEntry) { - list.setSelected(entry); - } - }); - list.refreshScroll(); - } - - @Override - protected void setInitialFocus() { - focusSearch(); - } - - @Unique - private void focusSearch() { - switchFocus(GuiNavigationPath.of(searchBox, this)); - } - - @Override - public void languagereload_focusList(LanguageListWidget list) { - switchFocus(GuiNavigationPath.of(list, this)); - } - - @Override - public void languagereload_focusEntry(LanguageEntry entry) { - switchFocus(GuiNavigationPath.of(entry, entry.getParent(), this)); - } - - @Unique - LanguageOptionsScreen it() { - return (LanguageOptionsScreen) (Object) this; - } - - LanguageOptionsScreenMixin(Screen parent, GameOptions options, Text title) { - super(parent, options, title); - } -} diff --git a/src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java b/src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java deleted file mode 100644 index 822769b..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/LanguageSelectionListWidgetAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package jerozgen.languagereload.mixin; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.option.LanguageOptionsScreen; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(LanguageOptionsScreen.LanguageSelectionListWidget.class) -public interface LanguageSelectionListWidgetAccessor { - @Invoker("") - static LanguageOptionsScreen.LanguageSelectionListWidget languagereload_init(LanguageOptionsScreen screen, MinecraftClient client) { - throw new AssertionError(); - } -} diff --git a/src/main/java/jerozgen/languagereload/mixin/SearchManagerMixin.java b/src/main/java/jerozgen/languagereload/mixin/SearchManagerMixin.java deleted file mode 100644 index 30593e2..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/SearchManagerMixin.java +++ /dev/null @@ -1,51 +0,0 @@ -package jerozgen.languagereload.mixin; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import jerozgen.languagereload.access.ILanguage; -import jerozgen.languagereload.access.ITranslationStorage; -import jerozgen.languagereload.config.Config; -import net.minecraft.client.search.SearchManager; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.item.tooltip.TooltipType; -import net.minecraft.text.Text; -import net.minecraft.util.Language; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.ArrayList; -import java.util.List; - -@Mixin(value = SearchManager.class, priority = 990) -abstract class SearchManagerMixin { - @WrapOperation(method = {"method_60365"}, - at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getTooltip(Lnet/minecraft/item/Item$TooltipContext;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/tooltip/TooltipType;)Ljava/util/List;")) - private static List addFallbackTranslationsToSearchTooltips(ItemStack instance, Item.TooltipContext context, @Nullable PlayerEntity player, TooltipType type, Operation> operation) { - var original = operation.call(instance, context, player, type); - - if (Config.getInstance() == null) return original; - if (!Config.getInstance().multilingualItemSearch) return original; - - var language = Language.getInstance(); - if (language == null) return original; - - var translationStorage = ((ILanguage) language).languagereload_getTranslationStorage(); - if (translationStorage == null) return original; - - var result = new ArrayList<>(original); - for (var fallbackCode : Config.getInstance().fallbacks) { - ((ITranslationStorage) translationStorage).languagereload_setTargetLanguage(fallbackCode); - operation.call(instance, context, player, type) - .stream() - .map(Text::getString) - .map(Text::literal) - .forEach(result::add); - } - - ((ITranslationStorage) translationStorage).languagereload_setTargetLanguage(null); - return result; - } -} diff --git a/src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java b/src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java deleted file mode 100644 index 059628d..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/SignTextAccessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package jerozgen.languagereload.mixin; - -import net.minecraft.block.entity.SignText; -import net.minecraft.text.OrderedText; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(SignText.class) -public interface SignTextAccessor { - @Accessor("orderedMessages") - void languagereload_setOrderedMessages(OrderedText[] orderedMessages); -} diff --git a/src/main/java/jerozgen/languagereload/mixin/TextDisplayEntityAccessor.java b/src/main/java/jerozgen/languagereload/mixin/TextDisplayEntityAccessor.java deleted file mode 100644 index 7443a18..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/TextDisplayEntityAccessor.java +++ /dev/null @@ -1,11 +0,0 @@ -package jerozgen.languagereload.mixin; - -import net.minecraft.entity.decoration.DisplayEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(DisplayEntity.TextDisplayEntity.class) -public interface TextDisplayEntityAccessor { - @Accessor("textLines") - void languagereload_setTextLines(DisplayEntity.TextDisplayEntity.TextLines textLines); -} diff --git a/src/main/java/jerozgen/languagereload/mixin/TranslatableTextContentMixin.java b/src/main/java/jerozgen/languagereload/mixin/TranslatableTextContentMixin.java deleted file mode 100644 index e546c73..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/TranslatableTextContentMixin.java +++ /dev/null @@ -1,67 +0,0 @@ -package jerozgen.languagereload.mixin; - -import com.google.common.collect.ImmutableList; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import jerozgen.languagereload.access.ILanguage; -import jerozgen.languagereload.access.ITranslationStorage; -import jerozgen.languagereload.config.Config; -import net.minecraft.text.StringVisitable; -import net.minecraft.text.TextContent; -import net.minecraft.text.TranslatableTextContent; -import net.minecraft.text.TranslationException; -import net.minecraft.util.Language; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.List; -import java.util.function.Consumer; - -@Mixin(TranslatableTextContent.class) -abstract class TranslatableTextContentMixin implements TextContent { - @Shadow @Final private String key; - - @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", - at = @At(value = "FIELD", target = "Lnet/minecraft/text/TranslatableTextContent;translations:Ljava/util/List;")) - List onVisit(TranslatableTextContent instance, Operation> translationsGetter) { - var overriddenTranslations = getOverriddenTranslations(); - if (overriddenTranslations != null) return overriddenTranslations; - return translationsGetter.call(instance); - } - - @WrapOperation(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", - at = @At(value = "FIELD", target = "Lnet/minecraft/text/TranslatableTextContent;translations:Ljava/util/List;")) - List onVisitStyled(TranslatableTextContent instance, Operation> translationsGetter) { - var overriddenTranslations = getOverriddenTranslations(); - if (overriddenTranslations != null) return overriddenTranslations; - return translationsGetter.call(instance); - } - - @Unique - List getOverriddenTranslations() { - if (!Config.getInstance().multilingualItemSearch) return null; - - var language = Language.getInstance(); - if (language == null) return null; - - var translationStorage = ((ILanguage) language).languagereload_getTranslationStorage(); - if (translationStorage == null) return null; - - var targetLanguage = ((ITranslationStorage) translationStorage).languagereload_getTargetLanguage(); - if (targetLanguage == null) return null; - - var string = ((ITranslationStorage) translationStorage).languagereload_get(key); - try { - var builder = new ImmutableList.Builder(); - this.forEachPart(string, builder::add); - return builder.build(); - } catch (TranslationException e) { - return ImmutableList.of(StringVisitable.plain(string)); - } - } - - @Shadow protected abstract void forEachPart(String translation, Consumer partsConsumer); -} diff --git a/src/main/java/jerozgen/languagereload/mixin/TranslationStorageMixin.java b/src/main/java/jerozgen/languagereload/mixin/TranslationStorageMixin.java deleted file mode 100644 index d20c569..0000000 --- a/src/main/java/jerozgen/languagereload/mixin/TranslationStorageMixin.java +++ /dev/null @@ -1,83 +0,0 @@ -package jerozgen.languagereload.mixin; - -import com.google.common.collect.Maps; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import jerozgen.languagereload.access.ITranslationStorage; -import jerozgen.languagereload.config.Config; -import net.minecraft.client.resource.language.TranslationStorage; -import net.minecraft.resource.ResourceManager; -import net.minecraft.util.DeprecatedLanguageData; -import net.minecraft.util.Language; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.io.InputStream; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; - -@Mixin(TranslationStorage.class) -abstract class TranslationStorageMixin extends Language implements ITranslationStorage { - @Unique private final Map targetLanguageByThread = Maps.newConcurrentMap(); - @Unique private static Map> separateTranslationsOnLoad; - @Unique private Map> separateTranslations; - - @Inject(method = "", at = @At("RETURN")) - void onConstructed(Map translations, boolean rightToLeft, CallbackInfo ci) { - separateTranslations = separateTranslationsOnLoad; - separateTranslationsOnLoad = null; - } - - @WrapOperation(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", - at = @At(value = "INVOKE", target = "Lnet/minecraft/util/DeprecatedLanguageData;apply(Ljava/util/Map;)V")) - private static void onLoad$applyDeprecatedLanguageData(DeprecatedLanguageData data, Map translations, Operation applier) { - applier.call(data, translations); - for (Map map : separateTranslationsOnLoad.values()) { - applier.call(data, map); - } - } - - @Inject(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Z)Lnet/minecraft/client/resource/language/TranslationStorage;", - at = @At("HEAD")) - private static void onLoad(ResourceManager resourceManager, List definitions, boolean rightToLeft, CallbackInfoReturnable cir) { - separateTranslationsOnLoad = Maps.newHashMap(); - } - - @Redirect(method = "load(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V", at = @At(value = "INVOKE", - target = "Lnet/minecraft/util/Language;load(Ljava/io/InputStream;Ljava/util/function/BiConsumer;)V")) - private static void onInternalLoad$saveSeparately(InputStream inputStream, BiConsumer entryConsumer, String langCode) { - if (Config.getInstance().multilingualItemSearch) { - Language.load(inputStream, entryConsumer.andThen((key, value) -> - separateTranslationsOnLoad.computeIfAbsent(langCode, k -> Maps.newHashMap()).put(key, value))); - } else Language.load(inputStream, entryConsumer); - } - - @Override - public String languagereload_get(String key) { - var targetLanguage = languagereload_getTargetLanguage(); - if (targetLanguage != null) { - var targetTranslations = separateTranslations.get(targetLanguage); - return targetTranslations == null ? "" : targetTranslations.getOrDefault(key, ""); - } - return this.get(key); - } - - @Override - public @Nullable String languagereload_getTargetLanguage() { - return targetLanguageByThread.get(Thread.currentThread().threadId()); - } - - @Override - public void languagereload_setTargetLanguage(@Nullable String value) { - var threadId = Thread.currentThread().threadId(); - if (value == null) targetLanguageByThread.remove(threadId); - else targetLanguageByThread.put(threadId, value); - } -} diff --git a/src/main/resources/languagereload.accesswidener b/src/main/resources/languagereload.accesswidener deleted file mode 100644 index d0c701e..0000000 --- a/src/main/resources/languagereload.accesswidener +++ /dev/null @@ -1,6 +0,0 @@ -accessWidener v1 named - -accessible class net/minecraft/client/gui/screen/option/LanguageOptionsScreen$LanguageSelectionListWidget -extendable class net/minecraft/client/world/ClientChunkManager$ClientChunkMap - -extendable method net/minecraft/client/gui/widget/EntryListWidget getEntryAtPosition (DD)Lnet/minecraft/client/gui/widget/EntryListWidget$Entry; From f76c11e7d89382520bce40b73a05e005075e115b Mon Sep 17 00:00:00 2001 From: AndreAugustoDev <25272842+AndreAugustoDev@users.noreply.github.com> Date: Wed, 2 Jul 2025 18:25:40 -0300 Subject: [PATCH 2/5] Fix build --- .../main/kotlin/multiloader-common.gradle.kts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/build-logic/src/main/kotlin/multiloader-common.gradle.kts b/build-logic/src/main/kotlin/multiloader-common.gradle.kts index 11c8b3f..fd56f65 100644 --- a/build-logic/src/main/kotlin/multiloader-common.gradle.kts +++ b/build-logic/src/main/kotlin/multiloader-common.gradle.kts @@ -4,6 +4,9 @@ plugins { `maven-publish` } +version = "${mod.version}+${libs.versions.minecraft.asProvider().get()}-${project.name}" +group = mod.group + base { archivesName.set(mod.archivesBaseName) } @@ -56,23 +59,26 @@ listOf("apiElements", "runtimeElements", "sourcesElements", "javadocElements").f } tasks { + val modId = mod.id named("sourcesJar") { from(rootProject.file("LICENSE")) { - rename { "${it}_${mod.id}" } + rename { "${it}_${modId}" } } } named("jar") { from(rootProject.file("LICENSE")) { - rename { "${it}_${mod.id}" } + rename { "${it}_${modId}" } } manifest.attributes( mapOf( - "Specification-Title" to mod.name, "Specification-Vendor" to mod.author, - "Specification-Version" to common.mod.version, // project.jar.archiveVersion, - "Implementation-Title" to project.name, "Implementation-Vendor" to mod.author, - "Implementation-Version" to common.mod.version, // project.jar.archiveVersion, + "Specification-Title" to mod.name, + "Specification-Vendor" to mod.author, + "Specification-Version" to version, + "Implementation-Title" to project.name, + "Implementation-Vendor" to mod.author, + "Implementation-Version" to version, "Built-On-Minecraft" to libs.versions.minecraft.asProvider().get() ) ) From 90570bed19452f64b71503e068517e7810d0d808 Mon Sep 17 00:00:00 2001 From: AndreAugustoDev <25272842+AndreAugustoDev@users.noreply.github.com> Date: Wed, 2 Jul 2025 19:33:07 -0300 Subject: [PATCH 3/5] Fix logo path --- build-logic/src/main/kotlin/multiloader-common.gradle.kts | 3 +++ fabric/src/main/resources/fabric.mod.json | 2 +- neoforge/src/main/resources/META-INF/neoforge.mods.toml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/kotlin/multiloader-common.gradle.kts b/build-logic/src/main/kotlin/multiloader-common.gradle.kts index fd56f65..010c5b0 100644 --- a/build-logic/src/main/kotlin/multiloader-common.gradle.kts +++ b/build-logic/src/main/kotlin/multiloader-common.gradle.kts @@ -60,6 +60,8 @@ listOf("apiElements", "runtimeElements", "sourcesElements", "javadocElements").f tasks { val modId = mod.id + val modLogo = "assets/${mod.id}/logo.png" + named("sourcesJar") { from(rootProject.file("LICENSE")) { rename { "${it}_${modId}" } @@ -97,6 +99,7 @@ tasks { "modHomepage" to mod.homepage, "modSources" to mod.sources, "modIssues" to mod.issues, + "modLogo" to modLogo, "modLinksModrinth" to mod.links.modrinth, "modLinksCurseforge" to mod.links.curseforge, "javaVersion" to libs.versions.java.get(), diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 5aa034b..2297bc8 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -38,7 +38,7 @@ "issues": "${modIssues}" }, "license": "${modLicense}", - "icon": "assets/${modId}/icon.png", + "icon": "${modLogo}", "environment": "client", "mixins": [ "${modId}.mixins.json" diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 64ffd4e..aed942c 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -63,7 +63,7 @@ displayURL="${modHomepage}" # This logo is displayed in the mod list next to your mod's name. # The path is relative to the root of your resources directory (e.g., src/main/resources). # Requirement: Optional -logoFile="${modId}/icon.png" +logoFile="${modLogo}" # A free-form string where you can list the authors of the mod. # Requirement: Optional From 13611a699b4c5c94f35a8204be92cb84a0e12482 Mon Sep 17 00:00:00 2001 From: AndreAugustoDev <25272842+AndreAugustoDev@users.noreply.github.com> Date: Wed, 2 Jul 2025 19:35:48 -0300 Subject: [PATCH 4/5] Add logoBlur=false to neoforge.mods.toml --- neoforge/src/main/resources/META-INF/neoforge.mods.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index aed942c..8b33726 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -65,6 +65,10 @@ displayURL="${modHomepage}" # Requirement: Optional logoFile="${modLogo}" +# A boolean indicating whether the logo should be blurred or not when trying to scale the logo. +# Requirement: Optional +logoBlur = false + # A free-form string where you can list the authors of the mod. # Requirement: Optional authors="${modAuthor}" From 8171ec13a546cc3d3ee57ab3aeb2783da667840c Mon Sep 17 00:00:00 2001 From: AndreAugustoDev <25272842+AndreAugustoDev@users.noreply.github.com> Date: Wed, 2 Jul 2025 19:43:34 -0300 Subject: [PATCH 5/5] Fix mod side on neoforge.mods.toml Fix logo path --- build-logic/src/main/kotlin/multiloader-common.gradle.kts | 2 +- neoforge/src/main/resources/META-INF/neoforge.mods.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/kotlin/multiloader-common.gradle.kts b/build-logic/src/main/kotlin/multiloader-common.gradle.kts index 010c5b0..175722a 100644 --- a/build-logic/src/main/kotlin/multiloader-common.gradle.kts +++ b/build-logic/src/main/kotlin/multiloader-common.gradle.kts @@ -60,7 +60,7 @@ listOf("apiElements", "runtimeElements", "sourcesElements", "javadocElements").f tasks { val modId = mod.id - val modLogo = "assets/${mod.id}/logo.png" + val modLogo = "assets/${mod.id}/icon.png" named("sourcesJar") { from(rootProject.file("LICENSE")) { diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 8b33726..2bfdc50 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -115,7 +115,7 @@ ordering = "NONE" # "CLIENT": Required only on the client. # "SERVER": Required only on the server. # Requirement: Optional -side = "BOTH" +side = "CLIENT" # The location of the mixin configuration file. [[mixins]]