Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6057141094 | |||
| 55f7e2cd30 | |||
| 39e923ae79 | |||
| 99852fded6 | |||
| d75bf8afe7 | |||
| 086185783c | |||
| 70321c2d0f | |||
| 9ce353d897 | |||
| 1d1a2d455d | |||
| fa1e7704e0 | |||
| 1227c56353 | |||
| 22c85f1eda | |||
| d75a4f8d1a | |||
| 7f69809345 | |||
| e460aa2e0d | |||
| 705535e574 | |||
| f0966befb2 | |||
| ec3bab3158 | |||
| 7510f23d19 | |||
| 9c9974de5b | |||
| e49a400dba | |||
| 893e82b181 | |||
| f8427c4f40 | |||
| 6576f55c55 | |||
| ddca97f679 | |||
| 6bf405c0b1 | |||
| d97bc912f7 | |||
| ac91979a3f | |||
| 3119fae67b | |||
| b77a53fc2a | |||
| 9d45717356 | |||
| 6235453387 | |||
| 0e52c06d35 | |||
| 680ab28491 | |||
| b31ee404fd | |||
| 4c604a24cb | |||
| ebba4939c5 | |||
| 285a507370 | |||
| 2f7008bbc3 | |||
| 68b0550696 | |||
| bafe93a28a | |||
| b1b464c592 | |||
| b21cfd8304 | |||
| 8dc3f744fe | |||
| 225703cdf1 | |||
| 3fb9fa8609 | |||
| a4fca06531 | |||
| fdd416b514 | |||
| 03f134bde3 | |||
| e8d1ca995c | |||
| 00b43dc90f | |||
| 2efb6fff49 | |||
| 16adf15975 | |||
| 417b98f881 | |||
| ffa64874c9 | |||
| 8454b5056d | |||
| a81fd9a483 | |||
| 4ee1949873 |
@@ -1,19 +0,0 @@
|
|||||||
**/.git
|
|
||||||
**/.gitlab
|
|
||||||
**/.cache
|
|
||||||
|
|
||||||
buildAllJars
|
|
||||||
|
|
||||||
**/_Misc Files
|
|
||||||
*.bat
|
|
||||||
*.md
|
|
||||||
*.sh
|
|
||||||
*.txt
|
|
||||||
|
|
||||||
coreSubProjects/*.md
|
|
||||||
coreSubProjects/*.txt
|
|
||||||
|
|
||||||
**/.gitignore
|
|
||||||
**/.gitattributes
|
|
||||||
**/.gitlab-cy.yml
|
|
||||||
**/.gitmodules
|
|
||||||
-700
@@ -1,700 +0,0 @@
|
|||||||
# DH Main
|
|
||||||
root = true
|
|
||||||
|
|
||||||
# Note: please keep this and the core .editorconfig in sync
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
insert_final_newline = false
|
|
||||||
max_line_length = 1000
|
|
||||||
tab_width = 4
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
ij_continuation_indent_size = 4
|
|
||||||
ij_formatter_off_tag = @formatter:off
|
|
||||||
ij_formatter_on_tag = @formatter:on
|
|
||||||
ij_formatter_tags_enabled = true
|
|
||||||
ij_smart_tabs = false
|
|
||||||
ij_visual_guides = none
|
|
||||||
ij_wrap_on_typing = false
|
|
||||||
|
|
||||||
[*.java]
|
|
||||||
indent_style = tab
|
|
||||||
ij_smart_tabs = true
|
|
||||||
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 = false
|
|
||||||
ij_java_align_multiline_extends_list = false
|
|
||||||
ij_java_align_multiline_for = false
|
|
||||||
ij_java_align_multiline_method_parentheses = false
|
|
||||||
ij_java_align_multiline_parameters = false
|
|
||||||
ij_java_align_multiline_parameters_in_calls = false
|
|
||||||
ij_java_align_multiline_parenthesized_expression = false
|
|
||||||
ij_java_align_multiline_records = false
|
|
||||||
ij_java_align_multiline_resources = false
|
|
||||||
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 = false
|
|
||||||
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 = normal
|
|
||||||
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_initializer = 0
|
|
||||||
ij_java_blank_lines_around_method = 0
|
|
||||||
ij_java_blank_lines_around_method_in_interface = 0
|
|
||||||
ij_java_blank_lines_before_class_end = 1
|
|
||||||
ij_java_blank_lines_before_imports = 1
|
|
||||||
ij_java_blank_lines_before_method_body = 0
|
|
||||||
ij_java_blank_lines_before_package = 1
|
|
||||||
ij_java_block_brace_style = next_line
|
|
||||||
ij_java_block_comment_add_space = false
|
|
||||||
ij_java_block_comment_at_first_column = true
|
|
||||||
ij_java_builder_methods = none
|
|
||||||
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 = normal
|
|
||||||
ij_java_case_statement_on_separate_line = true
|
|
||||||
ij_java_catch_on_new_line = true
|
|
||||||
ij_java_class_annotation_wrap = off
|
|
||||||
ij_java_class_brace_style = next_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 = never
|
|
||||||
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 = false
|
|
||||||
ij_java_doc_align_exception_comments = false
|
|
||||||
ij_java_doc_align_param_comments = false
|
|
||||||
ij_java_doc_do_not_wrap_if_one_line = true
|
|
||||||
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 = false
|
|
||||||
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 = true
|
|
||||||
ij_java_enum_constants_wrap = off
|
|
||||||
ij_java_extends_keyword_wrap = normal
|
|
||||||
ij_java_extends_list_wrap = normal
|
|
||||||
ij_java_field_annotation_wrap = off
|
|
||||||
ij_java_finally_on_new_line = true
|
|
||||||
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_if_brace_force = never
|
|
||||||
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 = 10
|
|
||||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
|
|
||||||
ij_java_keep_blank_lines_in_code = 10
|
|
||||||
ij_java_keep_blank_lines_in_declarations = 10
|
|
||||||
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 = true
|
|
||||||
ij_java_keep_line_breaks = true
|
|
||||||
ij_java_keep_multiple_expressions_in_one_line = true
|
|
||||||
ij_java_keep_simple_blocks_in_one_line = false
|
|
||||||
ij_java_keep_simple_classes_in_one_line = true
|
|
||||||
ij_java_keep_simple_lambdas_in_one_line = true
|
|
||||||
ij_java_keep_simple_methods_in_one_line = true
|
|
||||||
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 = false
|
|
||||||
ij_java_method_annotation_wrap = off
|
|
||||||
ij_java_method_brace_style = next_line
|
|
||||||
ij_java_method_call_chain_wrap = normal
|
|
||||||
ij_java_method_parameters_new_line_after_left_paren = true
|
|
||||||
ij_java_method_parameters_right_paren_on_new_line = false
|
|
||||||
ij_java_method_parameters_wrap = normal
|
|
||||||
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_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
|
|
||||||
ij_java_parameter_annotation_wrap = off
|
|
||||||
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 = false
|
|
||||||
ij_java_replace_sum_lambda_with_method_ref = false
|
|
||||||
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 = on_every_item
|
|
||||||
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 = false
|
|
||||||
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 = false
|
|
||||||
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_within_angle_brackets = false
|
|
||||||
ij_java_spaces_within_annotation_parentheses = false
|
|
||||||
ij_java_spaces_within_array_initializer_braces = false
|
|
||||||
ij_java_spaces_within_braces = true
|
|
||||||
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_subclass_name_suffix = Impl
|
|
||||||
ij_java_ternary_operation_signs_on_next_line = false
|
|
||||||
ij_java_ternary_operation_wrap = on_every_item
|
|
||||||
ij_java_test_name_suffix = Test
|
|
||||||
ij_java_throws_keyword_wrap = normal
|
|
||||||
ij_java_throws_list_wrap = normal
|
|
||||||
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 = always
|
|
||||||
ij_java_while_on_new_line = true
|
|
||||||
ij_java_wrap_comments = false
|
|
||||||
ij_java_wrap_first_method_in_call_chain = false
|
|
||||||
ij_java_wrap_long_lines = false
|
|
||||||
|
|
||||||
[*.nbtt]
|
|
||||||
indent_style = tab
|
|
||||||
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
|
|
||||||
|
|
||||||
[{*.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
|
|
||||||
ij_xml_use_custom_settings = false
|
|
||||||
|
|
||||||
[{*.bash,*.sh,*.zsh}]
|
|
||||||
indent_size = 4
|
|
||||||
tab_width = 4
|
|
||||||
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
|
|
||||||
|
|
||||||
[{*.comp,*.frag,*.fsh,*.geom,*.glsl,*.gsh,*.tesc,*.tese,*.vert,*.vsh}]
|
|
||||||
ij_glsl_space_after_colon = true
|
|
||||||
ij_glsl_space_after_comma = true
|
|
||||||
ij_glsl_space_after_for_semicolon = true
|
|
||||||
ij_glsl_space_after_quest = true
|
|
||||||
ij_glsl_space_before_colon = false
|
|
||||||
ij_glsl_space_before_comma = false
|
|
||||||
ij_glsl_space_before_for_semicolon = false
|
|
||||||
ij_glsl_space_before_quest = true
|
|
||||||
ij_glsl_spaces_around_additive_operators = true
|
|
||||||
ij_glsl_spaces_around_assignment_operators = true
|
|
||||||
ij_glsl_spaces_around_bitwise_operators = true
|
|
||||||
ij_glsl_spaces_around_equality_operators = true
|
|
||||||
ij_glsl_spaces_around_logical_operators = true
|
|
||||||
ij_glsl_spaces_around_multiplicative_operators = true
|
|
||||||
ij_glsl_spaces_around_relational_operators = true
|
|
||||||
ij_glsl_spaces_around_shift_operators = true
|
|
||||||
ij_glsl_spaces_within_brackets = false
|
|
||||||
ij_glsl_spaces_within_parentheses = false
|
|
||||||
|
|
||||||
[{*.gant,*.groovy,*.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
|
|
||||||
|
|
||||||
[{*.har,*.json,*.png.mcmeta,mcmod.info,pack.mcmeta}]
|
|
||||||
indent_size = 4
|
|
||||||
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 = false
|
|
||||||
ij_json_space_before_comma = false
|
|
||||||
ij_json_spaces_within_braces = false
|
|
||||||
ij_json_spaces_within_brackets = false
|
|
||||||
ij_json_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
|
|
||||||
|
|
||||||
[{*.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_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_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
|
|
||||||
|
|
||||||
[{*.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}]
|
|
||||||
ij_toml_keep_indents_on_empty_lines = false
|
|
||||||
|
|
||||||
[{*.yaml,*.yml}]
|
|
||||||
indent_size = 4
|
|
||||||
ij_yaml_align_values_properties = do_not_align
|
|
||||||
ij_yaml_autoinsert_sequence_marker = true
|
|
||||||
ij_yaml_block_mapping_on_new_line = false
|
|
||||||
ij_yaml_indent_sequence_value = true
|
|
||||||
ij_yaml_keep_indents_on_empty_lines = false
|
|
||||||
ij_yaml_keep_line_breaks = true
|
|
||||||
ij_yaml_sequence_on_new_line = false
|
|
||||||
ij_yaml_space_before_colon = false
|
|
||||||
ij_yaml_spaces_within_braces = true
|
|
||||||
ij_yaml_spaces_within_brackets = true
|
|
||||||
+29
-19
@@ -1,3 +1,31 @@
|
|||||||
|
# eclipse
|
||||||
|
bin
|
||||||
|
*.launch
|
||||||
|
.settings
|
||||||
|
.metadata
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
|
||||||
|
# idea
|
||||||
|
out
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# gradle
|
||||||
|
build
|
||||||
|
.gradle
|
||||||
|
|
||||||
|
# other
|
||||||
|
eclipse
|
||||||
|
run
|
||||||
|
|
||||||
|
# Files from Forge MDK
|
||||||
|
logs
|
||||||
|
forge*changelog.txt
|
||||||
|
|
||||||
|
.architectury-transformer/
|
||||||
build/
|
build/
|
||||||
*.ipr
|
*.ipr
|
||||||
run/
|
run/
|
||||||
@@ -5,11 +33,9 @@ run/
|
|||||||
out/
|
out/
|
||||||
*.iml
|
*.iml
|
||||||
.gradle/
|
.gradle/
|
||||||
.gradle-cache/
|
|
||||||
output/
|
output/
|
||||||
bin/
|
bin/
|
||||||
libs/
|
libs/
|
||||||
.architectury-transformer/
|
|
||||||
|
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
@@ -19,28 +45,12 @@ classes/
|
|||||||
.vscode
|
.vscode
|
||||||
.settings
|
.settings
|
||||||
*.launch
|
*.launch
|
||||||
hs_err_pid*
|
|
||||||
|
|
||||||
**/src/generated/
|
**/src/generated/
|
||||||
Merged/
|
Merged/
|
||||||
# Folder created by the buildAll scripts
|
|
||||||
buildAllJars/
|
|
||||||
|
|
||||||
relocate_natives/.venv/
|
|
||||||
relocate_natives/__pycache__/
|
|
||||||
relocate_natives/apple-codesign/
|
|
||||||
|
|
||||||
# file from notepad++
|
# file from notepad++
|
||||||
*.bak
|
*.bak
|
||||||
|
|
||||||
# file genearated via MC version switching using preprocessor
|
# file genearated via MC version switching using preprocessor
|
||||||
build.properties
|
build.properties
|
||||||
|
|
||||||
# Sqlite databases
|
|
||||||
*.sqlite
|
|
||||||
*.sqlite-journal
|
|
||||||
*.sqlite-shm
|
|
||||||
*.sqlite-wal
|
|
||||||
|
|
||||||
# Don't add access transformers to git as they're dynamically generated
|
|
||||||
accesstransformer.cfg
|
|
||||||
+179
-81
@@ -1,104 +1,202 @@
|
|||||||
# use Eclipse's JDK
|
# use Eclipse's JDK
|
||||||
# The ci should always use a unix(-like) OS to work
|
image: gradle:eclipse-temurin
|
||||||
image: eclipse-temurin:21
|
|
||||||
|
|
||||||
# all stages need to be defined here
|
# all stages need to be defined here
|
||||||
# TODO: Make stages depend on what is in versionProperties
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build_19_1
|
||||||
- api
|
- build_19
|
||||||
- pages
|
- build_18_2
|
||||||
|
- build_18_1
|
||||||
|
- build_17_1
|
||||||
|
- build_16_5
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
# Pull core when building
|
# Pull core when building
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
|
|
||||||
|
|
||||||
# These can be extended so code is a bit less duplicated
|
before_script:
|
||||||
.build_java:
|
- echo $CI_JOB_ID
|
||||||
#image: eclipse-temurin:17
|
# Writing GE_JOB_ID variable to environment file, will need the value in the next stage.
|
||||||
|
- echo GE_JOB_ID=$CI_JOB_ID >> generate_jars.env
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 1.16.5 build
|
||||||
|
build_16_5:
|
||||||
|
stage: build_16_5
|
||||||
|
script:
|
||||||
|
- echo "Building 1.16.5..."
|
||||||
|
- ./gradlew deleteMerged -PmcVer="1.16.5" --gradle-user-home cache/;
|
||||||
|
- ./gradlew clean -PmcVer="1.16.5" --gradle-user-home cache/;
|
||||||
|
- ./gradlew core:build -PmcVer="1.16.5" --gradle-user-home cache/;
|
||||||
|
- ./gradlew build -PmcVer="1.16.5" --gradle-user-home cache/;
|
||||||
|
- ./gradlew mergeJars -PmcVer="1.16.5" --gradle-user-home cache/;
|
||||||
|
image: eclipse-temurin:17
|
||||||
|
artifacts:
|
||||||
|
name: "Merged_NightlyBuild_1_16_5-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||||
|
paths:
|
||||||
|
- Merged
|
||||||
|
expire_in: 1 day
|
||||||
|
when: always
|
||||||
cache:
|
cache:
|
||||||
key: "gradleCache_$CI_JOB_NAME_SLUG"
|
key: "gradleCache"
|
||||||
policy: pull-push
|
policy: pull-push
|
||||||
paths:
|
paths:
|
||||||
- .gradle
|
- .gradle
|
||||||
- cache/
|
- cache/
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
retry:
|
|
||||||
max: 2
|
|
||||||
when:
|
|
||||||
- runner_system_failure
|
|
||||||
- stuck_or_timeout_failure
|
|
||||||
|
|
||||||
|
# 1.17.1 build
|
||||||
build:
|
build_17_1:
|
||||||
stage: build
|
stage: build_17_1
|
||||||
parallel:
|
|
||||||
matrix:
|
|
||||||
- MC_VER: [
|
|
||||||
"1.21.11", "1.21.10", "1.21.9", "1.21.8", "1.21.6", "1.21.5", "1.21.4", "1.21.3", "1.21.1",
|
|
||||||
"1.20.6", "1.20.4", "1.20.2", "1.20.1",
|
|
||||||
"1.19.4", "1.19.2",
|
|
||||||
"1.18.2",
|
|
||||||
"1.17.1",
|
|
||||||
"1.16.5"
|
|
||||||
]
|
|
||||||
script:
|
script:
|
||||||
# this both runs the unit tests and assembles the code
|
- echo "Building 1.17.1..."
|
||||||
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
|
- ./gradlew deleteMerged -PmcVer="1.17.1" --gradle-user-home cache/;
|
||||||
- ./gradlew build -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
|
- ./gradlew clean -PmcVer="1.17.1" --gradle-user-home cache/;
|
||||||
- ./gradlew mergeJars -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
|
- ./gradlew core:build -PmcVer="1.17.7" --gradle-user-home cache/;
|
||||||
- cp ./fabric/build/libs/* ./forge/build/libs/* ./neoforge/build/libs/* ./build/merged/* . || true
|
- ./gradlew build -PmcVer="1.17.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew mergeJars -PmcVer="1.17.1" --gradle-user-home cache/;
|
||||||
|
image: eclipse-temurin:17
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
name: "Merged_NightlyBuild_1_17_1-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||||
paths:
|
paths:
|
||||||
- ./*.jar
|
- Merged
|
||||||
exclude:
|
expire_in: 1 day
|
||||||
- ./*-all.jar
|
# even if one build fails, upload the successful jars
|
||||||
- ./*-dev.jar
|
|
||||||
- ./*-sources.jar
|
|
||||||
expire_in: 14 days
|
|
||||||
when: always
|
when: always
|
||||||
extends: .build_java
|
cache:
|
||||||
|
key: "gradleCache"
|
||||||
|
policy: pull-push
|
||||||
api:
|
|
||||||
stage: api
|
|
||||||
needs: []
|
|
||||||
script:
|
|
||||||
# this should only run for the API
|
|
||||||
- ./gradlew api:clean --gradle-user-home cache/;
|
|
||||||
# this also runs unit tests
|
|
||||||
- ./gradlew api:build --gradle-user-home cache/;
|
|
||||||
- ./gradlew api:addSourcesToCompiledJar --gradle-user-home cache/;
|
|
||||||
- cp ./coreSubProjects/api/build/libs/merged/* .
|
|
||||||
artifacts:
|
|
||||||
name: "NightlyBuild_Api-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
|
||||||
paths:
|
paths:
|
||||||
- ./*.jar
|
- .gradle
|
||||||
exclude:
|
- cache/
|
||||||
- ./*-all.jar
|
allow_failure: true
|
||||||
- ./*-dev.jar
|
|
||||||
- ./*-sources.jar
|
# 1.18.1 build
|
||||||
|
build_18_1:
|
||||||
|
stage: build_18_1
|
||||||
|
script:
|
||||||
|
- echo "Building 1.18.1..."
|
||||||
|
- ./gradlew deleteMerged -PmcVer="1.18.1" --gradle-user-home cache/; # make sure any previously merged jars are removed before running this job
|
||||||
|
- ./gradlew clean -PmcVer="1.18.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew core:build -PmcVer="1.18.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew build -PmcVer="1.18.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew mergeJars -PmcVer="1.18.1" --gradle-user-home cache/;
|
||||||
|
# build using Java 17
|
||||||
|
image: eclipse-temurin:17
|
||||||
|
artifacts:
|
||||||
|
name: "Merged_NightlyBuild_1_18_1-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||||
|
paths:
|
||||||
|
# relative to the root directory
|
||||||
|
- Merged
|
||||||
expire_in: 1 day
|
expire_in: 1 day
|
||||||
when: always
|
when: always
|
||||||
extends: .build_java
|
cache:
|
||||||
|
key: "gradleCache"
|
||||||
|
policy: pull-push
|
||||||
# generate and publish API javadocs
|
|
||||||
pages:
|
|
||||||
stage: pages
|
|
||||||
needs: []
|
|
||||||
script:
|
|
||||||
# this should only run for the API
|
|
||||||
- ./gradlew api:clean --gradle-user-home cache/;
|
|
||||||
# this also runs unit tests
|
|
||||||
- ./gradlew api:build --gradle-user-home cache/;
|
|
||||||
- ./gradlew api:javadoc --gradle-user-home cache/;
|
|
||||||
- mkdir public
|
|
||||||
- cp -r $CI_PROJECT_DIR/coreSubProjects/api/build/docs/javadoc/. public
|
|
||||||
artifacts:
|
|
||||||
paths:
|
paths:
|
||||||
- public
|
- .gradle
|
||||||
allow_failure: false
|
- cache/
|
||||||
extends: .build_java
|
allow_failure: true
|
||||||
|
|
||||||
|
# 1.18.2 build
|
||||||
|
build_18_2:
|
||||||
|
stage: build_18_2
|
||||||
|
script:
|
||||||
|
- echo "Building 1.18.2..."
|
||||||
|
- ./gradlew deleteMerged -PmcVer="1.18.2" --gradle-user-home cache/;
|
||||||
|
- ./gradlew clean -PmcVer="1.18.2" --gradle-user-home cache/;
|
||||||
|
- ./gradlew core:build -PmcVer="1.18.2" --gradle-user-home cache/;
|
||||||
|
- ./gradlew build -PmcVer="1.18.2" --gradle-user-home cache/;
|
||||||
|
- ./gradlew mergeJars -PmcVer="1.18.2" --gradle-user-home cache/;
|
||||||
|
image: eclipse-temurin:17
|
||||||
|
artifacts:
|
||||||
|
name: "Merged_NightlyBuild_1_18_2-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||||
|
paths:
|
||||||
|
- Merged
|
||||||
|
expire_in: 1 day
|
||||||
|
when: always
|
||||||
|
cache:
|
||||||
|
key: "gradleCache"
|
||||||
|
policy: pull-push
|
||||||
|
paths:
|
||||||
|
- .gradle
|
||||||
|
- cache/
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
# 1.19 build
|
||||||
|
build_19:
|
||||||
|
stage: build_19
|
||||||
|
script:
|
||||||
|
- echo "Building 1.19..."
|
||||||
|
- ./gradlew deleteMerged -PmcVer="1.19" --gradle-user-home cache/;
|
||||||
|
- ./gradlew clean -PmcVer="1.19" --gradle-user-home cache/;
|
||||||
|
- ./gradlew core:build -PmcVer="1.19" --gradle-user-home cache/;
|
||||||
|
- ./gradlew build -PmcVer="1.19" --gradle-user-home cache/;
|
||||||
|
- ./gradlew mergeJars -PmcVer="1.19" --gradle-user-home cache/;
|
||||||
|
image: eclipse-temurin:17
|
||||||
|
artifacts:
|
||||||
|
name: "Merged_NightlyBuild_1_19-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||||
|
paths:
|
||||||
|
- Merged
|
||||||
|
expire_in: 1 day
|
||||||
|
when: always
|
||||||
|
cache:
|
||||||
|
key: "gradleCache"
|
||||||
|
policy: pull-push
|
||||||
|
paths:
|
||||||
|
- .gradle
|
||||||
|
- cache/
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
# 1.19.1 build
|
||||||
|
build_19_1:
|
||||||
|
stage: build_19_1
|
||||||
|
script:
|
||||||
|
- echo "Building 1.19.1..."
|
||||||
|
- ./gradlew deleteMerged -PmcVer="1.19.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew clean -PmcVer="1.19.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew core:build -PmcVer="1.19.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew build -PmcVer="1.19.1" --gradle-user-home cache/;
|
||||||
|
- ./gradlew mergeJars -PmcVer="1.19.1" --gradle-user-home cache/;
|
||||||
|
image: eclipse-temurin:17
|
||||||
|
artifacts:
|
||||||
|
name: "Merged_NightlyBuild_1_19-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||||
|
paths:
|
||||||
|
- Merged
|
||||||
|
expire_in: 1 day
|
||||||
|
when: always
|
||||||
|
cache:
|
||||||
|
key: "gradleCache"
|
||||||
|
policy: pull-push
|
||||||
|
paths:
|
||||||
|
- .gradle
|
||||||
|
- cache/
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
|
||||||
|
# unused deployment stage
|
||||||
|
#deploy:
|
||||||
|
# stage: deploy
|
||||||
|
# image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||||
|
# script:
|
||||||
|
# - echo 'running release_job'
|
||||||
|
# - echo 'Previous Job ID is printed below'
|
||||||
|
# - echo $GE_JOB_ID
|
||||||
|
# # Specifying that this job requires artifacts from the previous job to succeed
|
||||||
|
# needs:
|
||||||
|
# - job: build
|
||||||
|
# artifacts: true
|
||||||
|
# release:
|
||||||
|
# name: 'Unstable Jars for Latest Commit' #: $CI_COMMIT_SHORT_SHA'
|
||||||
|
# description: 'Created automatically using the release-cli.'
|
||||||
|
# # tag_name is a mendatory field and can not be an empty string
|
||||||
|
# tag_name: 'Unstable-$CI_COMMIT_SHORT_SHA'
|
||||||
|
# assets:
|
||||||
|
# links:
|
||||||
|
# - name: 'Fabric Jars'
|
||||||
|
# url: 'https://gitlab.com/jeseibel/minecraft-lod-mod/cw/-/jobs/${GE_JOB_ID}/artifacts/file/fabric/build/libs'
|
||||||
|
# - name: 'Forge Jars'
|
||||||
|
# url: 'https://gitlab.com/jeseibel/minecraft-lod-mod/cw/-/jobs/${GE_JOB_ID}/artifacts/file/forge/build/libs'
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
## Check off each item in this list before submitting:
|
|
||||||
|
|
||||||
<!--
|
|
||||||
To mark a section as complete either put an "x" in between the square brackets, example: "[x]"
|
|
||||||
Or click the checkbox once the issue has been created.
|
|
||||||
-->
|
|
||||||
|
|
||||||
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
|
|
||||||
[Problems-and-solutions](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
|
||||||
|
|
||||||
2. [ ] Make sure you are not using any mods on the incompatible list:
|
|
||||||
[Mod support FAQ](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/4-mod-support/Mod-Support)
|
|
||||||
|
|
||||||
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
|
|
||||||
[Issues](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues)
|
|
||||||
|
|
||||||
4. [ ] Upload Minecraft's crash report and/or log. \
|
|
||||||
Minecraft crash reports are located in: `.minecraft/crash-reports` \
|
|
||||||
Minecraft logs are located in: `.minecraft/logs`
|
|
||||||
|
|
||||||
5. [ ] Upload your Distant Horizons Config. \
|
|
||||||
The config is found in: `.minecraft/configs/DistantHorizons.toml`
|
|
||||||
|
|
||||||
6. [ ] Fill out the information below:
|
|
||||||
|
|
||||||
* **minecraft version**:
|
|
||||||
|
|
||||||
* **Distant Horizons version**:
|
|
||||||
|
|
||||||
* **Mod loader**:
|
|
||||||
|
|
||||||
* **Installed mods (list the smallest number of mods that you can use to re-create the bug)**:
|
|
||||||
|
|
||||||
* **Describe the bug**:
|
|
||||||
|
|
||||||
* **Steps to reproduce the bug**:
|
|
||||||
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
## Check off each item in this list before submitting:
|
|
||||||
|
|
||||||
<!--
|
|
||||||
To mark a section as complete either put an "x" in between the square brackets, example: "[x]"
|
|
||||||
Or click the checkbox once the issue has been created.
|
|
||||||
-->
|
|
||||||
|
|
||||||
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
|
|
||||||
[Problems-and-solutions](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
|
||||||
|
|
||||||
2. [ ] Make sure you are not using any mods on the incompatible list:
|
|
||||||
[Mod support FAQ](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/4-mod-support/Mod-Support)
|
|
||||||
|
|
||||||
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
|
|
||||||
[Issues](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues)
|
|
||||||
|
|
||||||
4. [ ] Upload Minecraft's crash report and/or log. \
|
|
||||||
Minecraft crash reports are located in: `.minecraft/crash-reports` \
|
|
||||||
Minecraft logs are located in: `.minecraft/logs`
|
|
||||||
|
|
||||||
5. [ ] Upload your Distant Horizons Config. \
|
|
||||||
The config is found in: `.minecraft/configs/DistantHorizons.toml`
|
|
||||||
|
|
||||||
6. [ ] Fill out the information below:
|
|
||||||
|
|
||||||
* **minecraft version**:
|
|
||||||
|
|
||||||
* **Distant Horizons version**:
|
|
||||||
|
|
||||||
* **Mod loader**:
|
|
||||||
|
|
||||||
* **Installed mods (list the smallest number of mods that you can use to re-create the bug)**:
|
|
||||||
|
|
||||||
* **Describe the bug**:
|
|
||||||
|
|
||||||
* **Steps to reproduce the bug**:
|
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
- [ ] Check the existing [feature requests](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
|
|
||||||
|
|
||||||
1. **Describe the feature**:
|
|
||||||
|
|
||||||
2. **Describe why this feature should be added**:
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
1. Check the existing [improvement requests](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
|
|
||||||
|
|
||||||
2. **Describe the improvement**:
|
|
||||||
+3
-2
@@ -1,3 +1,4 @@
|
|||||||
[submodule "coreSubProjects"]
|
[submodule "core"]
|
||||||
path = coreSubProjects
|
path = core
|
||||||
url = https://gitlab.com/jeseibel/distant-horizons-core.git
|
url = https://gitlab.com/jeseibel/distant-horizons-core.git
|
||||||
|
branch = main
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Fabric Client & Server" type="CompoundRunConfigurationType">
|
|
||||||
<toRun name="Fabric Client (:fabric)" type="Application" />
|
|
||||||
<toRun name="Fabric Server (:fabric)" type="Application" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Forge Client & Server" type="CompoundRunConfigurationType">
|
|
||||||
<toRun name="Forge Client (gradle)" type="GradleRunConfiguration" />
|
|
||||||
<toRun name="Forge Server (gradle)" type="GradleRunConfiguration" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Forge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="forge:runClient" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Forge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="forge:runServer" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Neoforge Client & Server" type="CompoundRunConfigurationType">
|
|
||||||
<toRun name="Neoforge Client (gradle)" type="GradleRunConfiguration" />
|
|
||||||
<toRun name="Neoforge Server (gradle)" type="GradleRunConfiguration" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Neoforge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="neoforge:runClient" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Neoforge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="neoforge:runServer" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="build" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [clean]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="clean" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [core:build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="core:build" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [fabric:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="fabric:runClient" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [forge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="forge:runClient" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [mergeJars]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="mergeJars" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [neoforge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="neoforge:runClient" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="distant-horizons [test]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="test" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 1.16.5 version
|
||||||
|
|
||||||
|
java_version=8
|
||||||
|
minecraft_version=1.16.5
|
||||||
|
parchment_version=2022.03.06
|
||||||
|
compatible_minecraft_versions=["1.16.4", "1.16.5"]
|
||||||
|
|
||||||
|
# Fabric loader
|
||||||
|
fabric_loader_version=0.13.2
|
||||||
|
fabric_api_version=0.42.0+1.16
|
||||||
|
# Fabric mod versions
|
||||||
|
modmenu_version=1.16.22
|
||||||
|
starlight_version_fabric=
|
||||||
|
phosphor_version_fabric=
|
||||||
|
lithium_version=mc1.16.5-0.6.6
|
||||||
|
sodium_version=3488820
|
||||||
|
iris_version=1.16.x-v1.1.4
|
||||||
|
bclib_version=
|
||||||
|
immersive_portals_version =
|
||||||
|
|
||||||
|
# Fabric mod run
|
||||||
|
# 0 = Dont enable and dont run
|
||||||
|
# 1 = Can be refranced in code but dosnt run
|
||||||
|
# 2 = Can be refranced in code and runs in client
|
||||||
|
enable_starlight=0
|
||||||
|
enable_phosphor=0
|
||||||
|
enable_lithium=0
|
||||||
|
enable_sodium=1
|
||||||
|
enable_iris=0
|
||||||
|
enable_bclib=0
|
||||||
|
|
||||||
|
# Forge loader
|
||||||
|
forge_version=36.2.28
|
||||||
|
# Forge mod versions
|
||||||
|
starlight_version_forge=
|
||||||
|
terraforged_version=3285909
|
||||||
|
|
||||||
|
# Forge mod run
|
||||||
|
# 0 = Dont enable and dont run
|
||||||
|
# 1 = Can be refranced in code but dosnt run
|
||||||
|
# 2 = Can be refranced in code and runs in client
|
||||||
|
enable_starlight_forge=0
|
||||||
|
enable_terraforged=2
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 1.17.1 version
|
||||||
|
|
||||||
|
java_version=16
|
||||||
|
minecraft_version=1.17.1
|
||||||
|
parchment_version=2021.12.12
|
||||||
|
compatible_minecraft_versions=["1.17", "1.17.1"]
|
||||||
|
|
||||||
|
# Fabric loader
|
||||||
|
fabric_loader_version=0.13.2
|
||||||
|
fabric_api_version=0.46.1+1.17
|
||||||
|
# Fabric mod versions
|
||||||
|
modmenu_version=2.0.14
|
||||||
|
starlight_version_fabric=3442770
|
||||||
|
phosphor_version_fabric=
|
||||||
|
lithium_version=mc1.17.1-0.7.5
|
||||||
|
sodium_version=3605275
|
||||||
|
iris_version=1.17.x-v1.1.4
|
||||||
|
bclib_version=
|
||||||
|
immersive_portals_version = 0.14-1.17
|
||||||
|
|
||||||
|
# Fabric mod run
|
||||||
|
# 0 = Dont enable and dont run
|
||||||
|
# 1 = Can be refranced in code but dosnt run
|
||||||
|
# 2 = Can be refranced in code and runs in client
|
||||||
|
enable_starlight=0
|
||||||
|
enable_phosphor=0
|
||||||
|
enable_lithium=0
|
||||||
|
enable_sodium=1
|
||||||
|
enable_iris=0
|
||||||
|
enable_bclib=0
|
||||||
|
|
||||||
|
# Forge loader
|
||||||
|
forge_version=37.1.1
|
||||||
|
# Forge mod versions
|
||||||
|
starlight_version_forge=3457784
|
||||||
|
terraforged_version=
|
||||||
|
|
||||||
|
# Forge mod run
|
||||||
|
# 0 = Dont enable and dont run
|
||||||
|
# 1 = Can be refranced in code but dosnt run
|
||||||
|
# 2 = Can be refranced in code and runs in client
|
||||||
|
enable_starlight_forge=0
|
||||||
|
enable_terraforged=0
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 1.18.1 version
|
||||||
|
|
||||||
|
java_version = 17
|
||||||
|
minecraft_version=1.18.1
|
||||||
|
parchment_version=2022.03.06
|
||||||
|
compatible_minecraft_versions=["1.18", "1.18.1"]
|
||||||
|
|
||||||
|
# Fabric loader
|
||||||
|
fabric_loader_version=0.13.3
|
||||||
|
fabric_api_version=0.46.6+1.18
|
||||||
|
# Fabric mod versions
|
||||||
|
modmenu_version=3.0.1
|
||||||
|
starlight_version_fabric=3554912
|
||||||
|
phosphor_version_fabric=3573395
|
||||||
|
lithium_version=mc1.18.1-0.7.7
|
||||||
|
sodium_version=3605309
|
||||||
|
iris_version=1.18.x-v1.1.4
|
||||||
|
bclib_version=1.2.5
|
||||||
|
immersive_portals_version = v1.0.4-1.18
|
||||||
|
|
||||||
|
# Fabric mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight=0
|
||||||
|
enable_phosphor=0
|
||||||
|
enable_sodium=1
|
||||||
|
enable_lithium=0
|
||||||
|
enable_iris=0
|
||||||
|
enable_bclib=0
|
||||||
|
|
||||||
|
# Forge loader
|
||||||
|
forge_version=39.1.2
|
||||||
|
# Forge mod versions
|
||||||
|
starlight_version_forge=3559934
|
||||||
|
terraforged_version=
|
||||||
|
|
||||||
|
# Forge mod run
|
||||||
|
# 0 = Dont enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight_forge=0
|
||||||
|
enable_terraforged=0
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 1.18.2 version based stuff
|
||||||
|
|
||||||
|
java_version = 17
|
||||||
|
minecraft_version=1.18.2
|
||||||
|
parchment_version=2022.03.13
|
||||||
|
compatible_minecraft_versions=["1.18.2"]
|
||||||
|
|
||||||
|
# Fabric loader
|
||||||
|
fabric_loader_version=0.13.3
|
||||||
|
fabric_api_version=0.48.0+1.18.2
|
||||||
|
# Fabric mod versions
|
||||||
|
modmenu_version=3.1.0
|
||||||
|
starlight_version_fabric=3667443
|
||||||
|
phosphor_version_fabric=3573395
|
||||||
|
lithium_version=mc1.18.2-0.7.9
|
||||||
|
sodium_version=3669187
|
||||||
|
iris_version=1.18.x-v1.2.2
|
||||||
|
immersive_portals_version = v1.0.4-1.18
|
||||||
|
bclib_version=0
|
||||||
|
|
||||||
|
# Fabric mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight=0
|
||||||
|
enable_phosphor=0
|
||||||
|
enable_sodium=1
|
||||||
|
enable_lithium=0
|
||||||
|
enable_iris=0
|
||||||
|
enable_bclib=0
|
||||||
|
|
||||||
|
# Forge loader
|
||||||
|
forge_version=40.0.18
|
||||||
|
# Forge mod versions
|
||||||
|
starlight_version_forge=0
|
||||||
|
terraforged_version=
|
||||||
|
|
||||||
|
# Forge mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight_forge=0
|
||||||
|
enable_terraforged=0
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 1.18.2 version based stuff
|
||||||
|
|
||||||
|
java_version = 17
|
||||||
|
minecraft_version=1.19.1
|
||||||
|
parchment_version=2022.03.13
|
||||||
|
compatible_minecraft_versions=["1.19.1"]
|
||||||
|
|
||||||
|
# Fabric loader
|
||||||
|
fabric_loader_version=0.14.8
|
||||||
|
fabric_api_version=0.58.5+1.19.1
|
||||||
|
# Fabric mod versions
|
||||||
|
modmenu_version=4.0.0
|
||||||
|
starlight_version_fabric=0
|
||||||
|
phosphor_version_fabric=0
|
||||||
|
lithium_version=0
|
||||||
|
sodium_version=3820973
|
||||||
|
iris_version=1.19.x-v1.2.6
|
||||||
|
immersive_portals_version = 0
|
||||||
|
bclib_version=0
|
||||||
|
|
||||||
|
# Fabric mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight=0
|
||||||
|
enable_phosphor=0
|
||||||
|
enable_sodium=1
|
||||||
|
enable_lithium=0
|
||||||
|
enable_iris=0
|
||||||
|
enable_bclib=0
|
||||||
|
|
||||||
|
# Forge loader
|
||||||
|
forge_version=42.0.0
|
||||||
|
# Forge mod versions
|
||||||
|
starlight_version_forge=0
|
||||||
|
terraforged_version=
|
||||||
|
|
||||||
|
# Forge mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight_forge=0
|
||||||
|
enable_terraforged=0
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 1.18.2 version based stuff
|
||||||
|
|
||||||
|
java_version = 17
|
||||||
|
minecraft_version=1.19.2
|
||||||
|
parchment_version=2022.03.13
|
||||||
|
compatible_minecraft_versions=["1.19.2"]
|
||||||
|
|
||||||
|
# Fabric loader
|
||||||
|
fabric_loader_version=0.14.8
|
||||||
|
fabric_api_version=0.58.5+1.19.1
|
||||||
|
# Fabric mod versions
|
||||||
|
modmenu_version=4.0.0
|
||||||
|
starlight_version_fabric=0
|
||||||
|
phosphor_version_fabric=0
|
||||||
|
lithium_version=0
|
||||||
|
sodium_version=3820973
|
||||||
|
iris_version=1.19.x-v1.2.6
|
||||||
|
immersive_portals_version = 0
|
||||||
|
bclib_version=0
|
||||||
|
|
||||||
|
# Fabric mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight=0
|
||||||
|
enable_phosphor=0
|
||||||
|
enable_sodium=1
|
||||||
|
enable_lithium=0
|
||||||
|
enable_iris=0
|
||||||
|
enable_bclib=0
|
||||||
|
|
||||||
|
# Forge loader
|
||||||
|
forge_version=43.0.0
|
||||||
|
# Forge mod versions
|
||||||
|
starlight_version_forge=0
|
||||||
|
terraforged_version=
|
||||||
|
|
||||||
|
# Forge mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight_forge=0
|
||||||
|
enable_terraforged=0
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 1.18.2 version based stuff
|
||||||
|
|
||||||
|
java_version = 17
|
||||||
|
minecraft_version=1.19
|
||||||
|
parchment_version=2022.03.13
|
||||||
|
compatible_minecraft_versions=["1.19"]
|
||||||
|
|
||||||
|
# Fabric loader
|
||||||
|
fabric_loader_version=0.14.8
|
||||||
|
fabric_api_version=0.57.0+1.19
|
||||||
|
# Fabric mod versions
|
||||||
|
modmenu_version=4.0.0
|
||||||
|
starlight_version_fabric=0
|
||||||
|
phosphor_version_fabric=0
|
||||||
|
lithium_version=0
|
||||||
|
sodium_version=3820973
|
||||||
|
iris_version=1.19.x-v1.2.6
|
||||||
|
immersive_portals_version = 0
|
||||||
|
bclib_version=0
|
||||||
|
|
||||||
|
# Fabric mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight=0
|
||||||
|
enable_phosphor=0
|
||||||
|
enable_sodium=1
|
||||||
|
enable_lithium=0
|
||||||
|
enable_iris=0
|
||||||
|
enable_bclib=0
|
||||||
|
|
||||||
|
# Forge loader
|
||||||
|
forge_version=41.0.94
|
||||||
|
# Forge mod versions
|
||||||
|
starlight_version_forge=0
|
||||||
|
terraforged_version=
|
||||||
|
|
||||||
|
# Forge mod run
|
||||||
|
# 0 = Don't enable and don't run
|
||||||
|
# 1 = Can be referenced in code but doesn't run
|
||||||
|
# 2 = Can be referenced in code and runs in client
|
||||||
|
enable_starlight_forge=0
|
||||||
|
enable_terraforged=0
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
FROM eclipse-temurin:17-jdk
|
|
||||||
|
|
||||||
WORKDIR /home/build/
|
|
||||||
COPY ./gradlew .
|
|
||||||
RUN chmod +x ./gradlew
|
|
||||||
CMD echo "\r========== [CLEAN: $MC_VER] ==========" && \
|
|
||||||
./gradlew clean -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
|
|
||||||
echo "\r========== [BUILD: $MC_VER] ==========" && \
|
|
||||||
./gradlew build -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
|
|
||||||
echo "\r========== [MERGE: $MC_VER] ==========" && \
|
|
||||||
./gradlew mergeJars -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
|
|
||||||
echo "\r========== [DONE: $MC_VER] =========="
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
|
|
||||||
@@ -1,171 +1,144 @@
|
|||||||
# <img src="https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/new/SVG/Distant-Horizons.svg" height="128px">
|
# <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_logo%20files/LOD%20logo%20flat%20-%20with%20boarder.png" width="32"> Distant Horizons
|
||||||
_See farther without turning your game into a slide show._
|
|
||||||
|
|
||||||
<br>
|
> A mod that adds a Level of Detail System to Minecraft
|
||||||
|
|
||||||
# What is Distant Horizons?
|
# What is Distant Horizons?
|
||||||
|
|
||||||
Distant Horizons is a mod which implements a [Level of Detail](https://en.wikipedia.org/wiki/Level_of_detail_(computer_graphics)) system to Minecraft.\
|
This mod adds a Level Of Detail (LOD) system to Minecraft.\
|
||||||
This allows for far greater render distances without harming performance by gradually lowering the quality of distant terrain.
|
This implementation renders simplified chunks outside the normal render distance\
|
||||||
|
allowing for an increased view distance without harming performance.
|
||||||
|
|
||||||
Below is a video demonstrating the system:
|
Or in other words: this mod lets you see farther without turning your game into a slide show.\
|
||||||
|
If you want to see a quick demo, check out a video covering the mod here:
|
||||||
|
|
||||||
<a href="https://youtu.be/SxQdbtjGEsc" target="_blank"></a>
|
<a href="https://youtu.be/_04BZ8W2bDM" target="_blank"></a>
|
||||||
|
|
||||||
<br>
|
### Versions
|
||||||
|
|
||||||
## Minecraft and Library Versions
|
This branch is for these versions of Minecraft
|
||||||
|
- 1.19.1
|
||||||
|
- 1.19
|
||||||
|
- 1.18.2
|
||||||
|
- 1.18.1 & 1.18
|
||||||
|
- 1.17.1 & 1.17
|
||||||
|
- 1.16.5 & 1.16.4
|
||||||
|
|
||||||
### This branch supports the following versions of Minecraft:
|
Architectury version: 3.4-SNAPSHOT\
|
||||||
|
Architectury loom version: 0.12.0-SNAPSHOT\
|
||||||
|
Java Compiler plugin: Manifold Preprocessor
|
||||||
|
|
||||||
#### 1.20.4, 1.20.3 (Default)
|
#### 1.19.1 mods
|
||||||
Fabric: 0.15.1\
|
Forge version: 42.0.0\
|
||||||
Fabric API: 0.91.2+1.20.4\
|
Fabric version: 0.14.8\
|
||||||
Forge: 49.0.30\
|
Fabric API version: 0.58.5+1.19.1\
|
||||||
NeoForge: 118-beta\
|
Modmenu version: 4.0.0
|
||||||
Parchment: 1.20.2:2023.12.10\
|
|
||||||
Modmenu: 9.0.0-pre.1
|
|
||||||
|
|
||||||
#### 1.20.2
|
#### 1.19 mods
|
||||||
Fabric: 0.14.24\
|
Forge version: 41.0.94\
|
||||||
Fabric API: 0.90.4+1.20.2\
|
Fabric version: 0.14.8\
|
||||||
Forge: 48.0.13\
|
Fabric API version: 0.57.0+1.19\
|
||||||
Parchment: 1.20.1:2023.09.03\
|
Modmenu version: 4.0.0
|
||||||
Modmenu: 8.0.0
|
|
||||||
|
|
||||||
#### 1.20.1, 1.20
|
#### 1.18.2 mods
|
||||||
Fabric: 0.14.24\
|
Forge version: 40.0.18\
|
||||||
Fabric API: 0.90.4+1.20.1\
|
Fabric version: 0.13.3\
|
||||||
Forge: 47.2.1\
|
Fabric API version: 0.48.0+1.18.2\
|
||||||
Parchment: 1.20.1:2023.09.03\
|
Modmenu version: 3.1.0
|
||||||
Modmenu: 7.2.2
|
|
||||||
|
|
||||||
#### 1.19.4
|
#### 1.18.1 mods
|
||||||
Fabric: 0.14.24\
|
Forge version: 39.1.2\
|
||||||
Fabric API: 0.87.1+1.19.4\
|
Fabric version: 0.13.3\
|
||||||
Forge: 45.2.4\
|
Fabric API version: 0.42.6+1.18\
|
||||||
Parchment: 1.19.4:2023.06.26\
|
Modmenu version: 3.0.1
|
||||||
Modmenu: 6.3.1
|
|
||||||
|
|
||||||
#### 1.19.2
|
#### 1.17.1 mods
|
||||||
Fabric: 0.14.24\
|
Forge version: 37.1.1\
|
||||||
Fabric API: 0.76.1+1.19.2\
|
Fabric version: 0.13.2\
|
||||||
Forge: 43.3.2\
|
Fabric API version: 0.46.1+1.17\
|
||||||
Parchment: 1.19.2:2022.11.27\
|
Modmenu version: 2.0.14
|
||||||
Modmenu: 4.2.0-beta.2
|
|
||||||
|
|
||||||
#### 1.18.2
|
#### 1.16.5 mods
|
||||||
Fabric: 0.14.24\
|
Forge version: 36.2.28\
|
||||||
Fabric API: 0.76.0+1.18.2\
|
Fabric vetsion: 0.13.2\
|
||||||
Forge: 40.2.10\
|
Fabric API version: 0.42.0+1.16\
|
||||||
Parchment: 1.18.2:2022.11.06\
|
Modmenu version: 1.16.22
|
||||||
Modmenu: 3.2.5
|
|
||||||
|
|
||||||
#### 1.17.1, 1.17
|
|
||||||
Fabric: 0.14.24\
|
|
||||||
Fabric API: 0.46.1+1.17\
|
|
||||||
Forge: 37.1.1\
|
|
||||||
Parchment: 1.17.1:2021.12.12\
|
|
||||||
Modmenu: 2.0.14
|
|
||||||
|
|
||||||
#### 1.16.5, 1.16.4
|
|
||||||
Fabric: 0.14.24\
|
|
||||||
Fabric API: 0.42.0+1.16\
|
|
||||||
Forge: 36.2.39\
|
|
||||||
Parchment: 1.16.5:2022.03.06\
|
|
||||||
Modmenu: 1.16.22
|
|
||||||
|
|
||||||
### Versions no longer supported
|
Notes:\
|
||||||
- 1.18.1, 1.18
|
This version has been confirmed to work in IDE and Retail Minecraft with ether the Fabric or Forge mod-loader.
|
||||||
- 1.19.1, 1.19
|
|
||||||
- 1.19.3
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
### Plugin and Library versions
|
|
||||||
|
|
||||||
Gradle: 8.5\
|
|
||||||
Fabric loom: 1.4-SNAPSHOT\
|
|
||||||
Architectury loom (Forge gradle replacement): 1.4-SNAPSHOT\
|
|
||||||
Sponge vanilla gradle: 0.2.1-SNAPSHOT\
|
|
||||||
Java Preprocessor plugin: Manifold Preprocessor
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## Source Code Installation
|
## Source Code Installation
|
||||||
|
|
||||||
|
#### Nightlly builds
|
||||||
|
This mod has an autobuild system to automatically build the mod on each commit
|
||||||
|
- 1.19.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/1.6.4a_dev/download?job=build_19_1
|
||||||
|
- 1.19: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/1.6.4a_dev/download?job=build_19
|
||||||
|
- 1.18.2: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/1.6.4a_dev/download?job=build_18_2
|
||||||
|
- 1.18.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/1.6.4a_dev/download?job=build_18_1
|
||||||
|
- 1.17.1: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/1.6.4a_dev/download?job=build_17_1
|
||||||
|
- 1.16.5: https://gitlab.com/jeseibel/minecraft-lod-mod/-/jobs/artifacts/1.6.4a_dev/download?job=build_16_5
|
||||||
|
|
||||||
|
See the Fabric Documentation online for more detailed instructions:\
|
||||||
|
https://fabricmc.net/wiki/tutorial:setup
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
* A Java Development Kit (JDK) for Java 17 (recommended) or newer. <br>
|
* A Java Development Kit (JDK) for Java 17 (recommended) or newer. Visit https://www.oracle.com/java/technologies/downloads/ for installers.
|
||||||
Visit https://www.oracle.com/java/technologies/downloads/ for installers.
|
* Git or someway to clone git projects. Visit https://git-scm.com/ for installers.
|
||||||
* Git or someway to clone git projects. <br>
|
* (Not required) Any Java IDE with plugins that support Manifold, for example Intellij IDEA.
|
||||||
Visit https://git-scm.com/ for installers.
|
|
||||||
* (Not required) Any Java IDE with plugins that support Manifold, for example IntelliJ IDEA.
|
|
||||||
|
|
||||||
**If using IntelliJ:**
|
**If using IntelliJ:**
|
||||||
1. Install the Manifold plugin
|
0. Install Manifold plugin
|
||||||
2. Open IDEA and import the build.gradle
|
1. open IDEA and import the build.gradle
|
||||||
3. Refresh the Gradle project in IDEA if required
|
2. refresh the Gradle project in IDEA if required
|
||||||
|
|
||||||
**If using Eclipse: (Note that Eclipse doesn't support Manifold's preprocessor!)**
|
**If using Ecplise: (Note that Eclispe currently doesn't support Manifold's preprocessor!)**
|
||||||
1. Run the command: `./gradlew geneclipseruns`
|
1. run the command: `./gradlew geneclipseruns`
|
||||||
2. Run the command: `./gradlew eclipse`
|
2. run the command: `./gradlew eclipse`
|
||||||
3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft)
|
3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft)
|
||||||
4. Import the project into eclipse
|
4. Import the project into eclipse
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## Switching Versions
|
## Switching Versions
|
||||||
|
This branch support 6 built versions:
|
||||||
|
- 1.19.1
|
||||||
|
- 1.19
|
||||||
|
- 1.18.2
|
||||||
|
- 1.18.1 (which also runs on 1.18)
|
||||||
|
- 1.17.1 (which also runs on 1.17)
|
||||||
|
- 1.16.5 (which also runs 1.16.4)
|
||||||
|
|
||||||
To switch between different Minecraft versions, change `mcVer=1.?` in the `gradle.properties` file.
|
To switch between active versions, change `mcVer=1.?` in `gradle.properties` file.
|
||||||
|
|
||||||
If running in an IDE, to ensure the IDE noticed the version change, run any gradle command to refresh gradle.\
|
|
||||||
In IntelliJ, you will also need to do a gradle sync if it didn't happen automatically.
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
|
If running on IDE, to ensure IDE pickup the changed versions, you will need to run a gradle command again to allow gradle to update all the libs. (In IntellJ you will also need to do a gradle sync again if it didn't start it automatically.)
|
||||||
|
>Note: There may be a `java.nio.file.FileSystemException` thrown on running the command after switching versions. To fix it, either restart your IDE (as your IDE is locking up a file) or use tools like LockHunter to unlock the linked file. (Often a lib file under `common\build\lib` or `forge\build\lib` or `fabric\build\lib`). If anyone knows how to solve this issue please comment to this issue: https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
|
||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
||||||
Prerequisites:
|
**Using GUI**
|
||||||
- JDK 17 or newer
|
1. Download the zip of the project and extract it
|
||||||
|
2. Download the core from https://gitlab.com/jeseibel/distant-horizons-core and extract into a folder called `core`
|
||||||
From the File Explorer:
|
3. Open a command line in the project folder
|
||||||
1. Download and extract the project zip
|
4. Run the command: `./gradlew assemble`
|
||||||
2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
|
5. Then run command: `./gradlew mergeJars`
|
||||||
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
|
|
||||||
4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows)
|
|
||||||
5. Merge the jars with `./gradlew mergeJars`
|
|
||||||
6. The compiled jar file will be in the folder `Merged`
|
6. The compiled jar file will be in the folder `Merged`
|
||||||
|
|
||||||
From the command line:
|
**If in terminal:**
|
||||||
1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git`
|
1. `git clone -b 1.6.4a_dev --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
|
||||||
2. `cd distant-horizons`
|
2. `cd minecraft-lod-mod`
|
||||||
3. `./gradlew assemble`
|
3. `./gradlew assemble`
|
||||||
4. `./gradlew mergeJars`
|
4. `./gradlew mergeJars`
|
||||||
5. The compiled jar file will be in the folder `Merged`
|
5. The compiled jar file will be in the folder `Merged`
|
||||||
|
>Note: You can add the arg: `-PmcVer=1.?` to tell gradle to build a selected MC version instead of having to manually modify the `gradle.properties` file.
|
||||||
|
|
||||||
Run tests with: `./gradlew test`
|
|
||||||
|
|
||||||
>Note: You can add the argument `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file.\
|
|
||||||
> For example: `./gradlew assemble -PmcVer=1.18.2`
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## Compiling with Docker
|
|
||||||
|
|
||||||
`./compile <version>`
|
|
||||||
|
|
||||||
You can also locally compile the DH jars without a Java environment by using Docker. Where `<version>` is the version of Minecraft to compile for (ie `1.20.1`), or the keyword `all`. See [Versions](#minecraft-and-library-versions) for a list of all supported values.
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## Other commands
|
## Other commands
|
||||||
|
|
||||||
`./gradlew --refresh-dependencies` to refresh local dependencies.
|
`./gradlew --refresh-dependencies` to refresh local dependencies.
|
||||||
|
|
||||||
`./gradlew clean` to delete any compiled code.
|
`./gradlew clean` to reset everything (this does not affect your code) and then start the process again.
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## Note to self
|
## Note to self
|
||||||
|
|
||||||
@@ -173,38 +146,22 @@ The Minecraft source code is NOT added to your workspace in an editable way. Min
|
|||||||
|
|
||||||
Source code uses Mojang mappings & [Parchment](https://parchmentmc.org/) mappings.
|
Source code uses Mojang mappings & [Parchment](https://parchmentmc.org/) mappings.
|
||||||
|
|
||||||
To generate the source code run `./gradlew genSources` <br>
|
To generate the source code run `./gradlew genSources`\
|
||||||
If your IDE fails to auto-detect the source jars when browsing Minecraft classes; manually select the JAR file ending with -sources.jar when prompted by your IDE. <br>
|
If your IDE fails to auto-detect the sources JAR when browsing Minecraft classes manually select the JAR file ending with -sources.jar when prompted by your IDE
|
||||||
(In IntelliJ it's at the top where it says "choose sources" when browsing a Minecraft class)
|
|
||||||
|
|
||||||
<br>
|
In IntelliJ it's at the top where it says choose sources when browsing Minecraft classes
|
||||||
|
|
||||||
## Other Useful commands
|
## Useful commands
|
||||||
|
|
||||||
Run the standalone jar: `./gradlew run` <br>
|
Build only Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build`\
|
||||||
Build the standalone jar: `./gradlew core:build` <br>
|
Build only Forge: `./gradlew fabric:assemble` or `./gradlew forge:build`\
|
||||||
Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build` <br>
|
Run the Fabric client (for debugging): `./gradlew fabric:runClient`\
|
||||||
Only build Forge: `./gradlew forge:assemble` or `./gradlew forge:build` <br>
|
Run the Forge client (for debugging): `./gradlew forge:runClient`
|
||||||
Run the Fabric client (for debugging): `./gradlew fabric:runClient` <br>
|
|
||||||
Run the Forge client (for debugging): `./gradlew forge:runClient` <br>
|
|
||||||
|
|
||||||
To build all versions: `./buildAll` (all builds will end up in the `Merged` folder)
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
## Open Source Acknowledgements
|
## Open Source Acknowledgements
|
||||||
|
|
||||||
Forgix (To merge multiple mod versions into one jar) [_Formerly_ [_DHJarMerger_](https://github.com/Ran-helo/DHJarMerger)]\
|
XZ for Java (data compression)\
|
||||||
https://github.com/PacifistMC/Forgix
|
https://tukaani.org/xz/java.html
|
||||||
|
|
||||||
LZ4 for Java (data compression)\
|
DHJarMerger (To merge multiple mod versions into one jar)\
|
||||||
https://github.com/lz4/lz4-java
|
https://github.com/Ran-helo/DHJarMerger
|
||||||
|
|
||||||
NightConfig for JSON & TOML (config handling)\
|
|
||||||
https://github.com/TheElectronWill/night-config
|
|
||||||
|
|
||||||
SVG Salamander for SVG support (not being used atm)\
|
|
||||||
https://github.com/blackears/svgSalamander
|
|
||||||
|
|
||||||
sqlite-jdbc\
|
|
||||||
https://github.com/xerial/sqlite-jdbc
|
|
||||||
|
|||||||
+337
-663
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ===================="
|
|
||||||
mkdir -p buildAllJars
|
|
||||||
rm -rf buildAllJars/*
|
|
||||||
|
|
||||||
# Loop trough everything in the version properties folder
|
|
||||||
for d in versionProperties/*; do
|
|
||||||
# Get the name of the version that is going to be compiled
|
|
||||||
version=$(echo "$d" | sed "s/versionProperties\///" | sed "s/.properties//")
|
|
||||||
|
|
||||||
# Clean out the folders, build it, and merge it
|
|
||||||
# (We could use "./" to run gradlew, but as it is a shell script im going to be running it with the "sh" command)
|
|
||||||
echo "==================== Cleaning workspace to build $version ===================="
|
|
||||||
sh gradlew clean -PmcVer=$version
|
|
||||||
if [ $? != 0 ]; then continue; fi
|
|
||||||
|
|
||||||
echo "====================Building $version ===================="
|
|
||||||
sh gradlew build -PmcVer=$version
|
|
||||||
if [ $? != 0 ]; then continue; fi
|
|
||||||
|
|
||||||
echo "==================== Merging $version ===================="
|
|
||||||
sh gradlew mergeJars -PmcVer=$version
|
|
||||||
if [ $? != 0 ]; then continue; fi
|
|
||||||
|
|
||||||
echo "==================== Moving jar ===================="
|
|
||||||
mv build/merged/*.jar buildAllJars/
|
|
||||||
done
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
@echo off & setlocal enabledelayedexpansion
|
|
||||||
@rem Note for devs: If this script doesnt work, please look at the unix buildAll script
|
|
||||||
@rem This script was originally created on linux, so there may be some problems with this translation
|
|
||||||
|
|
||||||
|
|
||||||
echo ==================== Note: All build jars will be in the folder called 'buildAllJars' ====================
|
|
||||||
mkdir buildAllJars
|
|
||||||
del buildAllJars/*
|
|
||||||
|
|
||||||
@rem Loop trough everything in the version properties folder
|
|
||||||
for %%f in (versionProperties\*) do (
|
|
||||||
@rem Get the name of the version that is going to be compiled
|
|
||||||
set version=%%~nf
|
|
||||||
|
|
||||||
@rem Clean out the folders, build it, and merge it
|
|
||||||
echo ==================== Cleaning workspace to build !version! ====================
|
|
||||||
call .\gradlew.bat clean
|
|
||||||
|
|
||||||
echo ==================== Building !version! ====================
|
|
||||||
call .\gradlew.bat build -PmcVer="!version!"
|
|
||||||
|
|
||||||
echo ==================== Merging !version! ====================
|
|
||||||
call .\gradlew.bat mergeJars -PmcVer="!version!"
|
|
||||||
|
|
||||||
echo ==================== Moving jar ====================
|
|
||||||
move build\merged\*.jar buildAllJars\
|
|
||||||
)
|
|
||||||
|
|
||||||
endlocal
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
import java.io.*;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class AWToAT {
|
|
||||||
private static final Map<String, String> ACCESS_POINT_MAP = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
ACCESS_POINT_MAP.put("accessible", "public");
|
|
||||||
ACCESS_POINT_MAP.put("extendable", "public-f");
|
|
||||||
ACCESS_POINT_MAP.put("mutable", "public-f");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String minecraftVersion;
|
|
||||||
|
|
||||||
public File remap(File file, String minecraftVersion) {
|
|
||||||
this.minecraftVersion = minecraftVersion.replace("_", ".");
|
|
||||||
File atFile = createATFile(file);
|
|
||||||
processFile(file, atFile);
|
|
||||||
return atFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createATFile(File file) {
|
|
||||||
File metaInf = new File(file.getParentFile(), "META-INF");
|
|
||||||
if (!metaInf.exists() && !metaInf.mkdir()) throw new RuntimeException("Error creating META-INF folder");
|
|
||||||
File atFile = new File(metaInf, "accesstransformer.cfg");
|
|
||||||
try {
|
|
||||||
atFile.createNewFile();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Error creating new file", e);
|
|
||||||
}
|
|
||||||
return atFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processFile(File file, File atFile) {
|
|
||||||
/* Validates if we need to recreate the Access Transformer file if it's out of date */
|
|
||||||
// Get the hash of the file
|
|
||||||
String fileHash = getFileHash(file);
|
|
||||||
try (Scanner atScanner = new Scanner(atFile)) {
|
|
||||||
// Check if the AT file is up-to-date by comparing the hash of the file with the hash stored in the AT file
|
|
||||||
boolean hashFound = false;
|
|
||||||
while (atScanner.hasNextLine()) {
|
|
||||||
String line = atScanner.nextLine();
|
|
||||||
if (hashCheck(line, fileHash)) {
|
|
||||||
hashFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the AT file is up-to-date, print a message and return
|
|
||||||
if (hashFound) {
|
|
||||||
System.out.println("Access Transformer file is already up to date.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (FileNotFoundException ignored) {
|
|
||||||
// If the AT file does not exist, continue
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Creates the Access Transformer file */
|
|
||||||
// Opens a scanner for reading the Access Widener file and a writer for writing to the Access Transformer file
|
|
||||||
try (Scanner scanner = new Scanner(file); FileWriter writer = new FileWriter(atFile)) {
|
|
||||||
// Create an ExecutorService with a fixed thread pool size equal to the number of available processors
|
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
|
||||||
// List to hold Future objects representing results of computation
|
|
||||||
List<Future<String>> futures = new ArrayList<>();
|
|
||||||
|
|
||||||
// Write the hash of the file to the AT file
|
|
||||||
writer.write("#DH_MAPPING_HASH:" + fileHash + "\n");
|
|
||||||
|
|
||||||
// Read each line from the file
|
|
||||||
while (scanner.hasNextLine()) {
|
|
||||||
String line = scanner.nextLine();
|
|
||||||
// Skip lines starting with "accessWidener", "#" or blank lines
|
|
||||||
if (line.startsWith("accessWidener") || line.startsWith("#") || line.isBlank()) continue;
|
|
||||||
|
|
||||||
// Submit the line to the executor service for processing
|
|
||||||
// The processing is done by the processLine method
|
|
||||||
futures.add(executor.submit(() -> processLine(line.split(" "))));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the results to the output file
|
|
||||||
// The results are obtained by calling the get method on each Future
|
|
||||||
for (Future<String> future : futures) {
|
|
||||||
writer.write(future.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown the executor service to free up resources
|
|
||||||
executor.shutdown();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error reading or writing to file", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String processLine(String[] fields) {
|
|
||||||
// fields[0] = access point like "accessible", "extendable", "mutable"
|
|
||||||
// fields[1] = type like "field", "method", "class"
|
|
||||||
// fields[2] = class name
|
|
||||||
// fields[3] = field/method name
|
|
||||||
// fields[4] = field/method descriptor
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Store the original field/method name
|
|
||||||
String originalName = "";
|
|
||||||
|
|
||||||
// If there is a class name, replace the slashes with dots in the package name
|
|
||||||
if (fields.length > 2) fields[2] = fields[2].replace("/", ".");
|
|
||||||
|
|
||||||
// If there is a field/method name, store the original name and remap it to SRG
|
|
||||||
if (fields.length > 3) {
|
|
||||||
originalName = fields[3];
|
|
||||||
fields[3] = remapToSRG(fields[2], fields[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder line = new StringBuilder(ACCESS_POINT_MAP.getOrDefault(fields[0], "public")).append(" ");
|
|
||||||
switch (fields[1]) {
|
|
||||||
case "field":
|
|
||||||
line.append(fields[2]).append(" ").append(fields[3]).append(" #").append(originalName);
|
|
||||||
// It'll be like: access-point class-name field-name-SRG # field-name-Mojmap
|
|
||||||
// Eg: public net.minecraft.client.Minecraft f_90981_ # instance
|
|
||||||
break;
|
|
||||||
case "method":
|
|
||||||
line.append(fields[2]).append(" ").append(fields[3]).append(fields[4]).append(" #").append(originalName);
|
|
||||||
// It'll be like: access-point class-name method-name-SRG method-descriptor # method-name-Mojmap
|
|
||||||
// Eg: public net.minecraft.client.Minecraft m_172797_()Lnet/minecraft/client/Minecraft; # getInstance
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
line.append(fields[2]);
|
|
||||||
// It'll be like: access-point class-name
|
|
||||||
// Eg: public net.minecraft.client.Minecraft
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
line.append("\n");
|
|
||||||
return line.toString();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Error processing line", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hashCheck(String line, String fileHash) {
|
|
||||||
if (line.startsWith("#DH_MAPPING_HASH:")) {
|
|
||||||
String hash = line.substring(17);
|
|
||||||
return hash.equals(fileHash);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFileHash(File file) {
|
|
||||||
try {
|
|
||||||
MessageDigest shaDigest = MessageDigest.getInstance("SHA-256");
|
|
||||||
try (InputStream fis = new FileInputStream(file)) {
|
|
||||||
byte[] byteArray = new byte[1024];
|
|
||||||
int bytesCount;
|
|
||||||
|
|
||||||
// Read file data and update in message digest
|
|
||||||
while ((bytesCount = fis.read(byteArray)) != -1) {
|
|
||||||
shaDigest.update(byteArray, 0, bytesCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = shaDigest.digest();
|
|
||||||
|
|
||||||
// Convert byte array into signum representation
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (byte aByte : bytes) {
|
|
||||||
sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return complete hash
|
|
||||||
return sb.toString();
|
|
||||||
} catch (NoSuchAlgorithmException | IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// WARNING: BELOW LIES HIGHLY CURSED CODE AND MIGHT EVEN BE ILLEGAL
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Flag to track if there was an error in the GET request
|
|
||||||
boolean error = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns a field or method name from Mojang mappings as SRG mappings.
|
|
||||||
* It makes a GET request to the Linkie API to fetch the SRG name.
|
|
||||||
*
|
|
||||||
* @param clazz The class name
|
|
||||||
* @param name The field or method name
|
|
||||||
* @return The SRG name
|
|
||||||
* @throws Exception If there is an error in the GET request or the SRG name is not found in the response
|
|
||||||
*/
|
|
||||||
private String remapToSRG(String clazz, String name) throws Exception {
|
|
||||||
// Encode the class and field/method name to be used in the URL
|
|
||||||
String query = URLEncoder.encode(clazz + "." + name, StandardCharsets.UTF_8);
|
|
||||||
// Construct the URL for the GET request
|
|
||||||
String urlString = "https://linkieapi.shedaniel.me/api/search?namespace=mojang&query=" + query + "&version=" + this.minecraftVersion + "&limit=1&allowClasses=false&allowFields=true&allowMethods=true&translate=mojang_srg";
|
|
||||||
URL url = new URI(urlString).toURL();
|
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setRequestMethod("GET");
|
|
||||||
int responseCode = conn.getResponseCode();
|
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
|
||||||
String inputLine;
|
|
||||||
StringBuilder content = new StringBuilder();
|
|
||||||
// Read the response line by line
|
|
||||||
while ((inputLine = in.readLine()) != null) {
|
|
||||||
content.append(inputLine);
|
|
||||||
}
|
|
||||||
in.close();
|
|
||||||
conn.disconnect();
|
|
||||||
// Regex to find the SRG name in the response
|
|
||||||
Pattern pattern = Pattern.compile("\"l\"\\s*:\\s*\\{[^}]*\"i\"\\s*:\\s*\"([^\"]*)\"");
|
|
||||||
Matcher matcher = pattern.matcher(content.toString());
|
|
||||||
if (matcher.find()) return matcher.group(1);
|
|
||||||
else throw new Exception("Couldn't find the SRG mapping for name: " + name + "\nCould not find 'i' in 'l' object in the response"); // `i` is the SRG name which is stored in the `l` JSON object
|
|
||||||
} else {
|
|
||||||
if (error) {
|
|
||||||
// If there was an error in the GET request, and we already tried again, throw an exception
|
|
||||||
throw new Exception("The GET request failed");
|
|
||||||
}
|
|
||||||
// If there was an error in the GET request, wait 2.5 seconds and try again as we probably got rate limited
|
|
||||||
error = true;
|
|
||||||
Thread.sleep(2500);
|
|
||||||
return remapToSRG(clazz, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
class NativeRelocator
|
|
||||||
{
|
|
||||||
private final Path rootDirectory;
|
|
||||||
private final Path cacheRoot;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the NativeRelocator by preparing the environment if necessary.
|
|
||||||
* Executes the appropriate preparation script based on the OS.
|
|
||||||
*/
|
|
||||||
NativeRelocator(Path rootDirectory)
|
|
||||||
{
|
|
||||||
this.rootDirectory = rootDirectory;
|
|
||||||
this.cacheRoot = this.rootDirectory.resolve("cache");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepare() throws Exception
|
|
||||||
{
|
|
||||||
if (this.rootDirectory.resolve(".venv").toFile().exists())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
|
||||||
processBuilder.directory(this.rootDirectory.toFile());
|
|
||||||
|
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
|
||||||
if (os.contains("win"))
|
|
||||||
{
|
|
||||||
processBuilder.command("powershell", "-ExecutionPolicy", "Bypass", "./prepare.ps1");
|
|
||||||
}
|
|
||||||
else if (os.contains("nix")
|
|
||||||
|| os.contains("nux")
|
|
||||||
|| os.contains("mac")
|
|
||||||
|| os.contains("freebsd"))
|
|
||||||
{
|
|
||||||
processBuilder.command("./prepare.sh");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Unsupported operating system: " + os);
|
|
||||||
}
|
|
||||||
|
|
||||||
Process process = processBuilder.start();
|
|
||||||
CompletableFuture<Void> outputFuture = readOutputStreams(process);
|
|
||||||
|
|
||||||
int exitCode = process.waitFor();
|
|
||||||
outputFuture.get();
|
|
||||||
|
|
||||||
if (exitCode != 0)
|
|
||||||
{
|
|
||||||
throw new Exception("Prepare failed: " + exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads and prints the output and error streams of a process asynchronously.
|
|
||||||
*
|
|
||||||
* @param process The process whose streams should be read.
|
|
||||||
* @return A CompletableFuture that completes once all output has been processed.
|
|
||||||
*/
|
|
||||||
private static CompletableFuture<Void> readOutputStreams(Process process)
|
|
||||||
{
|
|
||||||
return CompletableFuture.runAsync(() -> {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (process.isAlive() || process.getInputStream().available() > 0 || process.getErrorStream().available() > 0)
|
|
||||||
{
|
|
||||||
if (process.getInputStream().available() > 0)
|
|
||||||
{
|
|
||||||
byte[] data = new byte[process.getInputStream().available()];
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
process.getInputStream().read(data);
|
|
||||||
System.out.write(data);
|
|
||||||
}
|
|
||||||
if (process.getErrorStream().available() > 0)
|
|
||||||
{
|
|
||||||
byte[] data = new byte[process.getErrorStream().available()];
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
process.getErrorStream().read(data);
|
|
||||||
System.err.write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection BusyWait
|
|
||||||
Thread.sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Throwable ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces occurrences of a target string in a byte array, ensuring null termination.
|
|
||||||
*
|
|
||||||
* @param byteArray The byte array where replacements should occur.
|
|
||||||
* @param target The string to replace.
|
|
||||||
* @param replacement The replacement string (must not be longer than the target).
|
|
||||||
* @throws IllegalArgumentException if the replacement is longer than the target.
|
|
||||||
*/
|
|
||||||
private void replaceInNullTerminatedStrings(byte[] byteArray, String target, String replacement)
|
|
||||||
{
|
|
||||||
if (target.length() < replacement.length())
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("Replacement must be the same length or shorter than the target.");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] targetBytes = target.getBytes(StandardCharsets.US_ASCII);
|
|
||||||
byte[] replacementBytes = replacement.getBytes(StandardCharsets.US_ASCII);
|
|
||||||
|
|
||||||
byte nullByte = 0;
|
|
||||||
|
|
||||||
for (int endPos = 0; endPos < byteArray.length - targetBytes.length - 1; endPos++)
|
|
||||||
{
|
|
||||||
int startPos = endPos;
|
|
||||||
int targetPos = 0;
|
|
||||||
while (targetPos < targetBytes.length && byteArray[endPos] == targetBytes[targetPos])
|
|
||||||
{
|
|
||||||
targetPos++;
|
|
||||||
endPos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetPos == targetBytes.length)
|
|
||||||
{
|
|
||||||
System.arraycopy(replacementBytes, 0, byteArray, startPos, replacementBytes.length);
|
|
||||||
|
|
||||||
startPos = startPos + replacementBytes.length;
|
|
||||||
while (byteArray[endPos] != nullByte)
|
|
||||||
{
|
|
||||||
byteArray[startPos] = byteArray[endPos];
|
|
||||||
endPos++;
|
|
||||||
startPos++;
|
|
||||||
}
|
|
||||||
byteArray[startPos] = nullByte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs an external script to fix a modified binary and returns the processed content.
|
|
||||||
*
|
|
||||||
* @param outputFilePath Path to store the processed binary.
|
|
||||||
* @param content The original binary content.
|
|
||||||
* @return The modified binary content.
|
|
||||||
* @throws Exception if the process execution fails.
|
|
||||||
*/
|
|
||||||
public byte[] fixModifiedBinary(Path outputFilePath, byte[] content) throws Exception
|
|
||||||
{
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
|
||||||
processBuilder.directory(this.rootDirectory.toFile());
|
|
||||||
|
|
||||||
processBuilder.command(
|
|
||||||
this.rootDirectory.resolve(".venv/Scripts").toFile().exists()
|
|
||||||
? this.rootDirectory.resolve(".venv/Scripts/python.exe").toString()
|
|
||||||
: this.rootDirectory.resolve(".venv/bin/python").toString(),
|
|
||||||
"./fix_modified_binary.py",
|
|
||||||
outputFilePath.toString()
|
|
||||||
);
|
|
||||||
|
|
||||||
Process process = processBuilder.start();
|
|
||||||
CompletableFuture<Void> outputFuture = readOutputStreams(process);
|
|
||||||
|
|
||||||
process.getOutputStream().write(content);
|
|
||||||
process.getOutputStream().close();
|
|
||||||
|
|
||||||
int exitCode = process.waitFor();
|
|
||||||
outputFuture.get();
|
|
||||||
|
|
||||||
if (exitCode != 0)
|
|
||||||
{
|
|
||||||
throw new Exception("Process failed: " + exitCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Files.readAllBytes(outputFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a binary file, applying string replacements and fixing modifications.
|
|
||||||
*
|
|
||||||
* @param outputPath The output file path relative to the cache directory.
|
|
||||||
* @param content The binary content to process.
|
|
||||||
* @param replacements A map of string replacements to apply.
|
|
||||||
* @return The modified binary content.
|
|
||||||
* @throws Exception if processing fails.
|
|
||||||
*/
|
|
||||||
public byte[] processBinary(String outputPath, byte[] content, Map<String, String> replacements) throws Exception
|
|
||||||
{
|
|
||||||
Path outputFilePath = this.cacheRoot.resolve(outputPath);
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
outputFilePath.getParent().toFile().mkdirs();
|
|
||||||
|
|
||||||
if (outputFilePath.toFile().exists())
|
|
||||||
{
|
|
||||||
return Files.readAllBytes(outputFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Relocating to " + outputPath + "...");
|
|
||||||
this.prepare();
|
|
||||||
|
|
||||||
for (Map.Entry<String, String> replacement : replacements.entrySet())
|
|
||||||
{
|
|
||||||
this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.fixModifiedBinary(outputFilePath, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
|
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to make participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
||||||
level of experience, education, socio-economic status, nationality, personal
|
|
||||||
appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all project spaces, and it also applies when
|
|
||||||
an individual is representing the project or its community in public spaces.
|
|
||||||
Examples of representing a project or community include using an official
|
|
||||||
project e-mail address, posting via an official social media account, or acting
|
|
||||||
as an appointed representative at an online or offline event. Representation of
|
|
||||||
a project may be further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the team lead James Seibel through Discord at `@backsun`. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see
|
|
||||||
https://www.contributor-covenant.org/faq
|
|
||||||
|
|
||||||
+194
-17
@@ -1,34 +1,71 @@
|
|||||||
|
apply plugin: "java"
|
||||||
|
apply plugin: "architectury-plugin"
|
||||||
|
apply plugin: "maven-publish"
|
||||||
|
apply plugin: "dev.architectury.loom"
|
||||||
|
|
||||||
// temporary fix for broken spongepowered version
|
archivesBaseName = rootProject.archives_base_name
|
||||||
buildscript {
|
version = rootProject.mod_version
|
||||||
configurations.configureEach {
|
group = rootProject.maven_group
|
||||||
resolutionStrategy {
|
|
||||||
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
|
architectury {
|
||||||
// newer versions can be found by going to the link:
|
common(rootProject.enabled_platforms.split(","))
|
||||||
// https://repo.spongepowered.org/#browse/browse:maven-public:org%2Fspongepowered%2Fvanillagradle%2F0.2.1-SNAPSHOT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
loom {
|
||||||
id "org.spongepowered.gradle.vanilla" version "0.2.1-SNAPSHOT"
|
silentMojangMappingsLicense()
|
||||||
|
accessWidenerPath.set(project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener"))
|
||||||
}
|
}
|
||||||
|
|
||||||
minecraft {
|
configurations {
|
||||||
accessWideners(project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener"))
|
common
|
||||||
version(rootProject.minecraft_version)
|
shadowMe
|
||||||
|
implementation.extendsFrom shadowMe
|
||||||
}
|
}
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// So mixins can be written in common
|
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
||||||
compileOnly group:'org.spongepowered', name:'mixin', version:'0.8.5'
|
// The following line declares the mojmap mappings & parchment mappings
|
||||||
|
mappings loom.layered() {
|
||||||
|
// Mojmap mappings
|
||||||
|
officialMojangMappings()
|
||||||
|
// Parchment mappings (it adds parameter mappings & javadoc)
|
||||||
|
if (rootProject.minecraft_version != "1.19" && rootProject.minecraft_version != "1.19.1" && rootProject.minecraft_version != "1.19.2")
|
||||||
|
parchment("org.parchmentmc.data:parchment-${rootProject.minecraft_version}:${rootProject.parchment_version}@zip")
|
||||||
|
else
|
||||||
|
parchment("org.parchmentmc.data:parchment-1.18.2:${rootProject.parchment_version}@zip") // As 1.19 dosnt have parchment mappings yet, we use 1.18.2 mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
//Manifold
|
||||||
|
annotationProcessor "systems.manifold:manifold-preprocessor:${rootProject.manifold_version}"
|
||||||
|
|
||||||
|
// Toml
|
||||||
|
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
|
||||||
|
|
||||||
|
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
||||||
|
// Do NOT use other classes from fabric loader unless working with fabric
|
||||||
|
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"\
|
||||||
|
|
||||||
|
common(project(":core")) { transitive false }
|
||||||
|
shadowMe(project(":core")) { transitive false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allows the jar to run standalone
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes 'Implementation-Title': rootProject.archives_base_name,
|
||||||
|
'Implementation-Version': rootProject.mod_version,
|
||||||
|
'Main-Class': 'com.seibel.lod.core.JarMain' // When changing the main of the jar change this line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
publications {
|
publications {
|
||||||
mavenCommon(MavenPublication) {
|
mavenCommon(MavenPublication) {
|
||||||
artifactId = rootProject.mod_readable_name
|
artifactId = rootProject.archives_base_name
|
||||||
from components.java
|
from components.java
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,3 +76,143 @@ publishing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task printConfigurations {
|
||||||
|
doLast {task ->
|
||||||
|
println "Project Name: $name configurations:"
|
||||||
|
configurations.each {
|
||||||
|
println " $it.name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
|
||||||
|
// For parchment mappings
|
||||||
|
maven { url "https://maven.parchmentmc.org" }
|
||||||
|
|
||||||
|
// used to download and compile dependencies from git repos
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
|
|
||||||
|
// For Manifold Preprocessor
|
||||||
|
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
|
||||||
|
|
||||||
|
// Required for importing Modrinth mods
|
||||||
|
maven {
|
||||||
|
name = "Modrinth"
|
||||||
|
url = "https://api.modrinth.com/maven"
|
||||||
|
content {
|
||||||
|
includeGroup "maven.modrinth"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required for importing CursedForge mods
|
||||||
|
maven {
|
||||||
|
url "https://www.cursemaven.com"
|
||||||
|
content {
|
||||||
|
includeGroup "curse.maven"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These 2 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
|
||||||
|
flatDir {
|
||||||
|
dirs "${rootDir}/mods/fabric"
|
||||||
|
content {
|
||||||
|
includeGroup "fabric-mod"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flatDir {
|
||||||
|
dirs "${rootDir}/mods/forge"
|
||||||
|
content {
|
||||||
|
includeGroup "forge-mod"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies the correct accesswidener and renames it
|
||||||
|
task copyAccessWidener(type: Copy) {
|
||||||
|
from project(":common").file("src/main/resources/${rootProject.acsessWidenerVersion}.lod.accesswidener")
|
||||||
|
into(file("build/resources/main"))
|
||||||
|
rename "${rootProject.acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
|
||||||
|
}
|
||||||
|
|
||||||
|
task copyCoreResources(type: Copy) {
|
||||||
|
from fileTree(project(":core").file("src/main/resources"))
|
||||||
|
into file("build/resources/main")
|
||||||
|
}
|
||||||
|
|
||||||
|
task copyCommonResources(type: Copy) {
|
||||||
|
from fileTree(project(":common").file("src/main/resources"))
|
||||||
|
into file("build/resources/main")
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
def resourceTargets = ["fabric.mod.json", "META-INF/mods.toml"] // Location of where to put
|
||||||
|
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
|
||||||
|
def replaceProperties = [
|
||||||
|
version : mod_version,
|
||||||
|
mod_name : mod_name,
|
||||||
|
authors : mod_authors,
|
||||||
|
description : mod_description,
|
||||||
|
homepage : mod_homepage,
|
||||||
|
source : mod_source,
|
||||||
|
issues : mod_issues,
|
||||||
|
minecraft_version : minecraft_version,
|
||||||
|
compatible_minecraft_versions: compatible_minecraft_versions,
|
||||||
|
java_version : java_version
|
||||||
|
]
|
||||||
|
// The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
|
||||||
|
//TODO: Make Forge loader version also be relaced with non hardcoded value instead of "[36,42)"
|
||||||
|
|
||||||
|
inputs.properties replaceProperties
|
||||||
|
replaceProperties.put 'project', project
|
||||||
|
filesMatching(resourceTargets) {
|
||||||
|
expand replaceProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
intoTargets.each { target ->
|
||||||
|
if (file(target).exists()) {
|
||||||
|
copy {
|
||||||
|
from(sourceSets.main.resources) {
|
||||||
|
include resourceTargets
|
||||||
|
expand replaceProperties
|
||||||
|
}
|
||||||
|
into target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println "Copying [common/src/main/resources/${acsessWidenerVersion}.lod.accesswidner] to [fabric/build/resources/main]."
|
||||||
|
copy {
|
||||||
|
from project(":common").file("src/main/resources/${acsessWidenerVersion}.lod.accesswidener")
|
||||||
|
into project(":fabric").file("build/resources/main")
|
||||||
|
rename "${acsessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
// Add Manifold Preprocessor
|
||||||
|
// def excapedMCVersion = rootProject.minecraft_version.replace(".", "_")
|
||||||
|
// options.compilerArgs += ['-Xplugin:Manifold', "-AMC_VERSION_${excapedMCVersion}"]
|
||||||
|
//
|
||||||
|
//options.compilerArgs += ['-deprecation']
|
||||||
|
//options.compilerArgs += ['-verbose']
|
||||||
|
//options.compilerArgs += ['-Xlint:unchecked']
|
||||||
|
//options.compilerArgs += ['-Xdiags:verbose']
|
||||||
|
//options.compilerArgs += ['-Xprint']
|
||||||
|
//options.compilerArgs += ['-XprintProcessorInfo']
|
||||||
|
//options.compilerArgs += ['-XprintRounds']
|
||||||
|
|
||||||
|
// println options.compilerArgs
|
||||||
|
|
||||||
|
// Set the java version
|
||||||
|
options.compilerArgs += ['-Xplugin:Manifold']
|
||||||
|
options.release = rootProject.java_version as Integer
|
||||||
|
// TODO: make everything use java 8
|
||||||
|
// options.release = 8 // Use Java 8 for everything so back porting is easier
|
||||||
|
}
|
||||||
|
|
||||||
|
sourcesJar {
|
||||||
|
}
|
||||||
|
|
||||||
|
runClient.enabled = false
|
||||||
|
runServer.enabled = false
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
|
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
|
|
||||||
import com.seibel.distanthorizons.common.commands.CommandInitializer;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.gui.DhDebugScreenEntry;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.config.ConfigHandler;
|
|
||||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
|
||||||
import com.seibel.distanthorizons.core.jar.ModJarInfo;
|
|
||||||
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
|
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.dedicated.DedicatedServer;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base for all mod loader initializers
|
|
||||||
* and handles most setup.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractModInitializer
|
|
||||||
{
|
|
||||||
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
private CommandInitializer commandInitializer;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
|
||||||
// abstract methods //
|
|
||||||
//==================//
|
|
||||||
|
|
||||||
protected abstract void createInitialSharedBindings();
|
|
||||||
protected abstract void createInitialClientBindings();
|
|
||||||
protected abstract IEventProxy createClientProxy();
|
|
||||||
protected abstract IEventProxy createServerProxy(boolean isDedicated);
|
|
||||||
protected abstract void initializeModCompat();
|
|
||||||
|
|
||||||
protected abstract void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler);
|
|
||||||
|
|
||||||
protected abstract void subscribeClientStartedEvent(Runnable eventHandler);
|
|
||||||
protected abstract void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler);
|
|
||||||
protected abstract void runDelayedSetup();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
|
||||||
// initialize events //
|
|
||||||
//===================//
|
|
||||||
|
|
||||||
public void onInitializeClient()
|
|
||||||
{
|
|
||||||
DependencySetup.createClientBindings();
|
|
||||||
this.createInitialClientBindings();
|
|
||||||
|
|
||||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client, firing DhApiBeforeDhInitEvent...");
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
|
|
||||||
|
|
||||||
this.startup();
|
|
||||||
this.logBuildInfo();
|
|
||||||
|
|
||||||
this.createClientProxy().registerEvents();
|
|
||||||
this.createServerProxy(false).registerEvents();
|
|
||||||
|
|
||||||
this.initializeModCompat();
|
|
||||||
|
|
||||||
// Client uses config for auto-updater, so it's initialized here instead of post-init stage
|
|
||||||
this.initConfig();
|
|
||||||
logModIncompatibilityWarnings(); // needs to be called after config loading
|
|
||||||
|
|
||||||
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
// debug screen rendering handled via a mixin
|
|
||||||
#else
|
|
||||||
DhDebugScreenEntry.register();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.subscribeClientStartedEvent(this::postInit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onInitializeServer()
|
|
||||||
{
|
|
||||||
DependencySetup.createServerBindings();
|
|
||||||
|
|
||||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server, firing DhApiBeforeDhInitEvent event...");
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
|
|
||||||
|
|
||||||
this.startup();
|
|
||||||
this.logBuildInfo();
|
|
||||||
|
|
||||||
// This prevents returning uninitialized Config values,
|
|
||||||
// resulting from a circular reference mid-initialization in a static class
|
|
||||||
// noinspection ResultOfMethodCallIgnored
|
|
||||||
ThreadPresetConfigEventHandler.INSTANCE.toString();
|
|
||||||
|
|
||||||
this.createServerProxy(true).registerEvents();
|
|
||||||
|
|
||||||
this.initializeModCompat();
|
|
||||||
|
|
||||||
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers...");
|
|
||||||
this.commandInitializer = new CommandInitializer();
|
|
||||||
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer.initCommands(dispatcher); });
|
|
||||||
|
|
||||||
this.subscribeServerStartingEvent(server ->
|
|
||||||
{
|
|
||||||
MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
|
|
||||||
|
|
||||||
this.initConfig();
|
|
||||||
this.postInit();
|
|
||||||
this.commandInitializer.onServerReady();
|
|
||||||
|
|
||||||
this.checkForUpdates();
|
|
||||||
|
|
||||||
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server.getServerDirectory());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========================//
|
|
||||||
// inner initializer methods //
|
|
||||||
//===========================//
|
|
||||||
|
|
||||||
private void startup()
|
|
||||||
{
|
|
||||||
DependencySetup.createSharedBindings();
|
|
||||||
SharedApi.init();
|
|
||||||
this.createInitialSharedBindings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logBuildInfo()
|
|
||||||
{
|
|
||||||
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
|
|
||||||
|
|
||||||
// if the build is stable the branch/commit/etc shouldn't be needed
|
|
||||||
if (ModInfo.IS_DEV_BUILD)
|
|
||||||
{
|
|
||||||
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
|
|
||||||
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
|
|
||||||
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <T extends IModAccessor> void tryCreateModCompatAccessor(String modId, Class<? super T> accessorClass, Supplier<T> accessorConstructor)
|
|
||||||
{
|
|
||||||
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
|
|
||||||
if (modChecker.isModLoaded(modId))
|
|
||||||
{
|
|
||||||
//noinspection unchecked
|
|
||||||
ModAccessorInjector.INSTANCE.bind((Class<? extends IModAccessor>) accessorClass, accessorConstructor.get());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.debug("Skipping mod compatibility accessor for: ["+modId+"]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initConfig()
|
|
||||||
{
|
|
||||||
ConfigHandler.tryRunFirstTimeSetup();
|
|
||||||
Config.completeDelayedSetup();
|
|
||||||
DhLogger.runDelayedConfigSetup();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkForUpdates()
|
|
||||||
{
|
|
||||||
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get())
|
|
||||||
{
|
|
||||||
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
|
|
||||||
{
|
|
||||||
LOGGER.info("Silent updates are not allowed for dedicated servers; force disabling.");
|
|
||||||
Config.Client.Advanced.AutoUpdater.enableSilentUpdates.set(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
SelfUpdater.onStart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postInit()
|
|
||||||
{
|
|
||||||
LOGGER.info("Running Delayed setup...");
|
|
||||||
this.runDelayedSetup();
|
|
||||||
|
|
||||||
if (ConfigHandler.INSTANCE == null)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.info("Delayed setup complete, firing DhApiAfterDhInitEvent event...");
|
|
||||||
|
|
||||||
// should be fired after all delayed setup so singletons and config can be accessed
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================//
|
|
||||||
// mod partial compatibility checks //
|
|
||||||
//==================================//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some mods will work with a few tweaks
|
|
||||||
* or will partially work but have some known issues we can't solve.
|
|
||||||
* This method will log (and display to chat if enabled)
|
|
||||||
* these warnings and potential fixes.
|
|
||||||
*/
|
|
||||||
private static void logModIncompatibilityWarnings()
|
|
||||||
{
|
|
||||||
boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get();
|
|
||||||
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
|
|
||||||
|
|
||||||
String startingString = "Partially Incompatible Distant Horizons mod detected: ";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Alex's caves
|
|
||||||
if (modChecker.isModLoaded("alexscaves"))
|
|
||||||
{
|
|
||||||
// There've been a few reports about this mod breaking DH at a few different points in time
|
|
||||||
// the fixes for said breakage changes depending on the version so unfortunately
|
|
||||||
// all we can do is log a warning so the user can handle it.
|
|
||||||
|
|
||||||
if (showChatWarnings)
|
|
||||||
{
|
|
||||||
String message =
|
|
||||||
MinecraftTextFormat.ORANGE + "Distant Horizons: Alex's Cave detected." + MinecraftTextFormat.CLEAR_FORMATTING +
|
|
||||||
"You may have to change Alex's config for DH to render. ";
|
|
||||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.warn(startingString + "[Alex's Caves] may require some config changes in order to render Distant Horizons correctly.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// William Wythers' Overhauled Overworld (WWOO)
|
|
||||||
if (modChecker.isModLoaded("wwoo"))
|
|
||||||
{
|
|
||||||
// WWOO has a bug with it's world gen that can't be fixed by DH or WWOO
|
|
||||||
// (at least that is what James learned after talking with WWOO)
|
|
||||||
// WWOO will cause grid lines to appear in the world when DH generates the chunks
|
|
||||||
// this might be due to how WWOO uses features for everything when generating
|
|
||||||
// and said features don't always get to the edge of said chunks.
|
|
||||||
|
|
||||||
String wwooWarning = "LODs generated by DH may have grid lines between sections. Disabling either WWOO or DH's distant generator will fix the problem.";
|
|
||||||
|
|
||||||
if (showChatWarnings)
|
|
||||||
{
|
|
||||||
String message =
|
|
||||||
MinecraftTextFormat.ORANGE + "Distant Horizons: WWOO detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
|
|
||||||
wwooWarning;
|
|
||||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.warn(startingString + "[WWOO] "+ wwooWarning);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chunky
|
|
||||||
boolean chunkyPresent = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Class.forName("org.popcraft.chunky.api.ChunkyAPI");
|
|
||||||
chunkyPresent = true;
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ignore) { }
|
|
||||||
|
|
||||||
if (chunkyPresent)
|
|
||||||
{
|
|
||||||
// Chunky can generate chunks faster than DH can process them,
|
|
||||||
// causing holes in the LODs.
|
|
||||||
// Generally it's better and faster to use DH's world generator.
|
|
||||||
|
|
||||||
String chunkyWarning = "Chunky can cause DH LODs to have holes " +
|
|
||||||
"since Chunky can generate chunks faster than DH can process them. \n" +
|
|
||||||
"Using DH's distant generator instead of chunky or increasing DH's CPU thread count can resolve the issue.";
|
|
||||||
|
|
||||||
if (showChatWarnings)
|
|
||||||
{
|
|
||||||
String message =
|
|
||||||
MinecraftTextFormat.ORANGE + "Distant Horizons: Chunky detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
|
|
||||||
chunkyWarning;
|
|
||||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.warn(startingString + "[Chunky] "+ chunkyWarning);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper classes //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
public interface IEventProxy
|
|
||||||
{
|
|
||||||
void registerEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-138
@@ -1,138 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
|
||||||
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import io.netty.buffer.ByteBufUtil;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
#else
|
|
||||||
import net.minecraft.resources.Identifier;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder()
|
|
||||||
.fileLevelConfig(Config.Common.Logging.logNetworkEventToFile)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_6
|
|
||||||
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
|
||||||
#else
|
|
||||||
public static final Identifier WRAPPER_PACKET_RESOURCE = Identifier.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// "Forge byte" is an unused packet ID. We have our own system which works with all mod loaders,
|
|
||||||
// so we're just accounting for it by reading the protocol version as a byte instead of a short in Forge, to keep cross-loader compatibility
|
|
||||||
private final boolean forgeByteInProtocolVersion;
|
|
||||||
|
|
||||||
|
|
||||||
public AbstractPluginPacketSender() { this(false); }
|
|
||||||
public AbstractPluginPacketSender(boolean forgeByteInProtocolVersion)
|
|
||||||
{
|
|
||||||
this.forgeByteInProtocolVersion = forgeByteInProtocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void sendToClient(IServerPlayerWrapper serverPlayer, AbstractNetworkMessage message)
|
|
||||||
{
|
|
||||||
this.sendToClient((ServerPlayer) serverPlayer.getWrappedMcObject(), message);
|
|
||||||
}
|
|
||||||
public abstract void sendToClient(ServerPlayer serverPlayer, AbstractNetworkMessage message);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract void sendToServer(AbstractNetworkMessage message);
|
|
||||||
|
|
||||||
public AbstractNetworkMessage decodeMessage(FriendlyByteBuf in)
|
|
||||||
{
|
|
||||||
AbstractNetworkMessage message = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
in.markReaderIndex();
|
|
||||||
|
|
||||||
int protocolVersion = this.forgeByteInProtocolVersion ? in.readByte() : in.readShort();
|
|
||||||
if (protocolVersion != ModInfo.PROTOCOL_VERSION)
|
|
||||||
{
|
|
||||||
return new IncompatibleMessageInternalEvent(protocolVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
message = MessageRegistry.INSTANCE.createMessage(in.readUnsignedShort());
|
|
||||||
message.decode(in);
|
|
||||||
|
|
||||||
if (in.isReadable())
|
|
||||||
{
|
|
||||||
throw new IOException("Buffer has not been fully read");
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
in.resetReaderIndex();
|
|
||||||
|
|
||||||
LOGGER.error("Failed to decode message", e);
|
|
||||||
LOGGER.error("Buffer: ["+in+"]");
|
|
||||||
LOGGER.error("Buffer contents: ["+ByteBufUtil.hexDump(in)+"]");
|
|
||||||
|
|
||||||
return new ProtocolErrorInternalEvent(e, message, true);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Prevent connection crashing if not entire buffer has been read
|
|
||||||
in.readerIndex(in.writerIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encodeMessage(FriendlyByteBuf out, AbstractNetworkMessage message)
|
|
||||||
{
|
|
||||||
// This is intentionally unhandled, because errors related to this are unlikely to appear in wild
|
|
||||||
Objects.requireNonNull(message);
|
|
||||||
|
|
||||||
if (this.forgeByteInProtocolVersion)
|
|
||||||
{
|
|
||||||
out.writeByte(ModInfo.PROTOCOL_VERSION);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out.writeShort(ModInfo.PROTOCOL_VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
out.markWriterIndex();
|
|
||||||
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
|
|
||||||
message.encode(out);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Failed to encode message", e);
|
|
||||||
LOGGER.error("Message: ["+message+"]");
|
|
||||||
|
|
||||||
message.getSession().tryHandleMessage(new ProtocolErrorInternalEvent(e, message, false));
|
|
||||||
|
|
||||||
// Encode close reason message instead
|
|
||||||
out.resetWriterIndex();
|
|
||||||
message = new CloseReasonMessage("Internal error on other side");
|
|
||||||
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
|
|
||||||
message.encode(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_20_6
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public record CommonPacketPayload(@Nullable AbstractNetworkMessage message) implements CustomPacketPayload
|
|
||||||
{
|
|
||||||
public static final Type<CommonPacketPayload> TYPE = new Type<>(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE);
|
|
||||||
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Type<? extends CustomPacketPayload> type() { return TYPE; }
|
|
||||||
|
|
||||||
|
|
||||||
public static class Codec implements StreamCodec<FriendlyByteBuf, CommonPacketPayload>
|
|
||||||
{
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public CommonPacketPayload decode(@NotNull FriendlyByteBuf in)
|
|
||||||
{ return new CommonPacketPayload(PACKET_SENDER.decodeMessage(in)); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encode(@NotNull FriendlyByteBuf out, CommonPacketPayload payload)
|
|
||||||
{ PACKET_SENDER.encodeMessage(out, payload.message()); }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.commands;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_19_2
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
#else // < 1.19.2
|
|
||||||
import net.minecraft.network.chat.TranslatableComponent;
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract class providing common functionality for DH's commands.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractCommand
|
|
||||||
{
|
|
||||||
public abstract LiteralArgumentBuilder<CommandSourceStack> buildCommand();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a success response to the player with the given text.
|
|
||||||
*
|
|
||||||
* @param commandContext The command context to send the response to.
|
|
||||||
* @param text The text to display in the success message.
|
|
||||||
* @return 1, indicating that the command was successful.
|
|
||||||
*/
|
|
||||||
protected int sendSuccessResponse(CommandContext<CommandSourceStack> commandContext, String text, boolean notifyAdmins)
|
|
||||||
{
|
|
||||||
#if MC_VER >= MC_1_20_1
|
|
||||||
commandContext.getSource().sendSuccess(() -> Component.literal(text), notifyAdmins);
|
|
||||||
#elif MC_VER >= MC_1_19_2
|
|
||||||
commandContext.getSource().sendSuccess(Component.literal(text), notifyAdmins);
|
|
||||||
#else
|
|
||||||
commandContext.getSource().sendSuccess(new TranslatableComponent(text), notifyAdmins);
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a failure response to the player with the given text.
|
|
||||||
*
|
|
||||||
* @param commandContext The command context to send the response to.
|
|
||||||
* @param text The text to display in the failure message.
|
|
||||||
* @return 1, indicating that the command was successful.
|
|
||||||
*/
|
|
||||||
protected int sendFailureResponse(CommandContext<CommandSourceStack> commandContext, String text)
|
|
||||||
{
|
|
||||||
#if MC_VER >= MC_1_20_1
|
|
||||||
commandContext.getSource().sendFailure(Component.literal(text));
|
|
||||||
#elif MC_VER >= MC_1_19_2
|
|
||||||
commandContext.getSource().sendFailure(Component.literal(text));
|
|
||||||
#else
|
|
||||||
commandContext.getSource().sendFailure(new TranslatableComponent(text));
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the server player from a command context.
|
|
||||||
*
|
|
||||||
* @param commandContext The command context to get the server player from.
|
|
||||||
* @return The server player wrapper for the player who sent the command.
|
|
||||||
*/
|
|
||||||
protected IServerPlayerWrapper getSourcePlayer(CommandContext<CommandSourceStack> commandContext) #if MC_VER < MC_1_19_2 throws CommandSyntaxException #endif
|
|
||||||
{
|
|
||||||
#if MC_VER >= MC_1_19_2
|
|
||||||
return ServerPlayerWrapper.getWrapper(Objects.requireNonNull(commandContext.getSource().getPlayer()));
|
|
||||||
#else
|
|
||||||
return ServerPlayerWrapper.getWrapper(commandContext.getSource().getPlayerOrException());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the source of a command is a player.
|
|
||||||
*
|
|
||||||
* @param source The source of the command to check.
|
|
||||||
* @return True if the source is a player, false otherwise.
|
|
||||||
*/
|
|
||||||
protected boolean isPlayerSource(CommandSourceStack source)
|
|
||||||
{
|
|
||||||
#if MC_VER >= MC_1_19_2
|
|
||||||
return source.isPlayer();
|
|
||||||
#else
|
|
||||||
try
|
|
||||||
{
|
|
||||||
source.getPlayerOrException();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (CommandSyntaxException e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-84
@@ -1,84 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.commands;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
|
|
||||||
import static net.minecraft.commands.Commands.literal;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
#else
|
|
||||||
import net.minecraft.server.permissions.PermissionCheck;
|
|
||||||
import net.minecraft.server.permissions.Permissions;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public class CommandInitializer
|
|
||||||
{
|
|
||||||
private boolean serverReady = false;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
private static final int REQUIRED_PERMISSION_LEVEL = 4;
|
|
||||||
#else
|
|
||||||
private static final PermissionCheck COMMAND_PERMISSION_CHECK = new PermissionCheck.Require(Permissions.COMMANDS_OWNER);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A received command dispatcher, which is held until the server is ready to initialize the commands.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
private CommandDispatcher<CommandSourceStack> commandDispatcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the command initializer that the game is ready to accept commands.
|
|
||||||
* If {@link CommandInitializer#initCommands(CommandDispatcher)} has been fired before it was ready, it will also initialize the commands.
|
|
||||||
*/
|
|
||||||
public void onServerReady()
|
|
||||||
{
|
|
||||||
this.serverReady = true;
|
|
||||||
if (this.commandDispatcher != null)
|
|
||||||
{
|
|
||||||
this.initCommands(this.commandDispatcher);
|
|
||||||
this.commandDispatcher = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes all available commands.
|
|
||||||
* If the game is not ready yet, it stores the dispatcher to initialize the commands later.
|
|
||||||
*
|
|
||||||
* @param commandDispatcher The command dispatcher to register commands to.
|
|
||||||
*/
|
|
||||||
public void initCommands(CommandDispatcher<CommandSourceStack> commandDispatcher)
|
|
||||||
{
|
|
||||||
if (!this.serverReady)
|
|
||||||
{
|
|
||||||
this.commandDispatcher = commandDispatcher;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh")
|
|
||||||
.requires((source) ->
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
return source.hasPermission(REQUIRED_PERMISSION_LEVEL);
|
|
||||||
#else
|
|
||||||
return COMMAND_PERMISSION_CHECK.check(source.permissions());
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.then(new ConfigCommand().buildCommand());
|
|
||||||
builder.then(new DebugCommand().buildCommand());
|
|
||||||
builder.then(new PregenCommand().buildCommand());
|
|
||||||
|
|
||||||
if (DEBUG_CODEC_CRASH_MESSAGE)
|
|
||||||
{
|
|
||||||
builder.then(new CrashCommand().buildCommand());
|
|
||||||
}
|
|
||||||
|
|
||||||
commandDispatcher.register(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.commands;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.arguments.*;
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
|
||||||
import com.seibel.distanthorizons.core.config.ConfigHandler;
|
|
||||||
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
|
|
||||||
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.function.ToIntBiFunction;
|
|
||||||
|
|
||||||
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
|
|
||||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
|
||||||
import static net.minecraft.commands.Commands.argument;
|
|
||||||
import static net.minecraft.commands.Commands.literal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command for managing config.
|
|
||||||
*/
|
|
||||||
public class ConfigCommand extends AbstractCommand
|
|
||||||
{
|
|
||||||
private static final List<CommandArgumentData<?>> commandArguments = Arrays.asList(
|
|
||||||
new CommandArgumentData<>(Integer.class, configEntry -> integer(configEntry.getMin(), configEntry.getMax()), IntegerArgumentType::getInteger),
|
|
||||||
new CommandArgumentData<>(Double.class, configEntry -> doubleArg(configEntry.getMin(), configEntry.getMax()), DoubleArgumentType::getDouble),
|
|
||||||
new CommandArgumentData<>(Boolean.class, BoolArgumentType::bool, BoolArgumentType::getBool),
|
|
||||||
new CommandArgumentData<>(String.class, StringArgumentType::string, StringArgumentType::getString)
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a command tree.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
|
||||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
|
||||||
{
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
|
|
||||||
HashSet<String> addedCommands = new HashSet<>();
|
|
||||||
|
|
||||||
for (AbstractConfigBase<?> type : ConfigHandler.INSTANCE.configBaseList)
|
|
||||||
{
|
|
||||||
// Skip non-config entries
|
|
||||||
if (!(type instanceof ConfigEntry))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection PatternVariableCanBeUsed
|
|
||||||
ConfigEntry configEntry = (ConfigEntry) type;
|
|
||||||
if (configEntry.getChatCommandName() == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addedCommands.add(configEntry.getChatCommandName()))
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Duplicate command name: " + configEntry.getChatCommandName());
|
|
||||||
}
|
|
||||||
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getChatCommandName())
|
|
||||||
.executes(commandContext -> this.sendSuccessResponse(commandContext,
|
|
||||||
"\n" +
|
|
||||||
"Description of §l" + configEntry.getChatCommandName() + "§r:\n" +
|
|
||||||
"§o" + configEntry.getComment().trim() + "§r\n" +
|
|
||||||
"§7Config file name: §f" + configEntry.name + "§7, category: §f" + configEntry.category + "\n" +
|
|
||||||
"\n" +
|
|
||||||
"Current value of " + configEntry.getChatCommandName() + " is §n" + configEntry.get() + "§r",
|
|
||||||
false
|
|
||||||
));
|
|
||||||
|
|
||||||
ToIntBiFunction<CommandContext<CommandSourceStack>, Object> updateConfigValue = (commandContext, value) -> {
|
|
||||||
configEntry.set(value);
|
|
||||||
return this.sendSuccessResponse(commandContext, "Changed the value of [" + configEntry.getChatCommandName() + "] to [" + value + "]", true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enum type needs a special case since enums aren't represented by existing argument type
|
|
||||||
// and need literals for each individual value
|
|
||||||
if (Enum.class.isAssignableFrom(configEntry.getType()))
|
|
||||||
{
|
|
||||||
for (Object choice : configEntry.getType().getEnumConstants())
|
|
||||||
{
|
|
||||||
subcommand.then(
|
|
||||||
literal(choice.toString())
|
|
||||||
.executes(c -> updateConfigValue.applyAsInt(c, choice))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boolean setterAdded = false;
|
|
||||||
for (CommandArgumentData<?> commandArgumentData : commandArguments)
|
|
||||||
{
|
|
||||||
if (!commandArgumentData.argumentClass.isAssignableFrom(configEntry.getType()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
subcommand.then(argument("value", commandArgumentData.getArgumentType(configEntry))
|
|
||||||
.executes(c -> updateConfigValue.applyAsInt(c, commandArgumentData.getValue(c, "value"))));
|
|
||||||
|
|
||||||
setterAdded = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!setterAdded)
|
|
||||||
{
|
|
||||||
throw new RuntimeException("Config type of " + type.getName() + " is not supported: " + configEntry.getType().getSimpleName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.then(subcommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static class CommandArgumentData<T>
|
|
||||||
{
|
|
||||||
public final Class<T> argumentClass;
|
|
||||||
public final Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction;
|
|
||||||
private final BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter;
|
|
||||||
|
|
||||||
public CommandArgumentData(Class<T> argumentClass, Supplier<ArgumentType<T>> argumentTypeSupplier, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
|
|
||||||
{
|
|
||||||
this(argumentClass, configEntry -> argumentTypeSupplier.get(), valueGetter);
|
|
||||||
}
|
|
||||||
public CommandArgumentData(Class<T> argumentClass, Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
|
|
||||||
{
|
|
||||||
this.argumentClass = argumentClass;
|
|
||||||
this.argumentTypeFunction = argumentTypeFunction;
|
|
||||||
this.valueGetter = valueGetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArgumentType<T> getArgumentType(ConfigEntry<T> configEntry)
|
|
||||||
{
|
|
||||||
return this.argumentTypeFunction.apply(configEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getValue(CommandContext<CommandSourceStack> commandContext, String argumentName)
|
|
||||||
{
|
|
||||||
return this.valueGetter.apply(commandContext, argumentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.commands;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
|
||||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
|
|
||||||
import static net.minecraft.commands.Commands.literal;
|
|
||||||
|
|
||||||
public class CrashCommand extends AbstractCommand
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
|
||||||
{
|
|
||||||
return literal("crash")
|
|
||||||
.requires(this::isPlayerSource)
|
|
||||||
.then(literal("encode")
|
|
||||||
.executes(c -> {
|
|
||||||
assert SharedApi.tryGetDhServerWorld() != null;
|
|
||||||
|
|
||||||
ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
|
|
||||||
.getConnectedPlayer(this.getSourcePlayer(c));
|
|
||||||
if (serverPlayerState != null)
|
|
||||||
{
|
|
||||||
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.ENCODE));
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}))
|
|
||||||
.then(literal("decode")
|
|
||||||
.executes(c -> {
|
|
||||||
assert SharedApi.tryGetDhServerWorld() != null;
|
|
||||||
|
|
||||||
ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
|
|
||||||
.getConnectedPlayer(this.getSourcePlayer(c));
|
|
||||||
if (serverPlayerState != null)
|
|
||||||
{
|
|
||||||
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.DECODE));
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.commands;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static net.minecraft.commands.Commands.literal;
|
|
||||||
|
|
||||||
public class DebugCommand extends AbstractCommand
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
|
||||||
{
|
|
||||||
return literal("debug")
|
|
||||||
.executes(c -> {
|
|
||||||
List<String> lines = new ArrayList<>();
|
|
||||||
F3Screen.addStringToDisplay(lines);
|
|
||||||
return this.sendSuccessResponse(c, String.join("\n", lines), false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.commands;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.generation.PregenManager;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.commands.arguments.DimensionArgument;
|
|
||||||
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
|
|
||||||
import net.minecraft.server.level.ColumnPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
|
|
||||||
import java.util.concurrent.CancellationException;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
|
||||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
|
||||||
import static net.minecraft.commands.Commands.argument;
|
|
||||||
import static net.minecraft.commands.Commands.literal;
|
|
||||||
|
|
||||||
public class PregenCommand extends AbstractCommand
|
|
||||||
{
|
|
||||||
private final PregenManager pregenManager = new PregenManager();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
|
||||||
{
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> statusCommand = literal("status")
|
|
||||||
.executes(this::pregenStatus);
|
|
||||||
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> startCommand = literal("start")
|
|
||||||
.then(argument("dimension", DimensionArgument.dimension())
|
|
||||||
.then(argument("origin", ColumnPosArgument.columnPos())
|
|
||||||
.then(argument("chunkRadius", integer(32))
|
|
||||||
.executes(this::pregenStart))));
|
|
||||||
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> stopCommand = literal("stop")
|
|
||||||
.executes(this::pregenStop);
|
|
||||||
|
|
||||||
return literal("pregen")
|
|
||||||
.then(statusCommand)
|
|
||||||
.then(startCommand)
|
|
||||||
.then(stopCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private int pregenStatus(CommandContext<CommandSourceStack> c)
|
|
||||||
{
|
|
||||||
String statusString = this.pregenManager.getStatusString();
|
|
||||||
//noinspection ReplaceNullCheck
|
|
||||||
if (statusString != null)
|
|
||||||
{
|
|
||||||
return this.sendSuccessResponse(c, statusString, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return this.sendSuccessResponse(c, "Pregen is not running", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int pregenStart(CommandContext<CommandSourceStack> c) throws CommandSyntaxException
|
|
||||||
{
|
|
||||||
this.sendSuccessResponse(c, "Starting pregen. Progress will be in the server console.", true);
|
|
||||||
|
|
||||||
ServerLevel level = DimensionArgument.getDimension(c, "dimension");
|
|
||||||
ColumnPos origin = ColumnPosArgument.getColumnPos(c, "origin");
|
|
||||||
int chunkRadius = getInteger(c, "chunkRadius");
|
|
||||||
|
|
||||||
CompletableFuture<Void> future = this.pregenManager.startPregen(
|
|
||||||
ServerLevelWrapper.getWrapper(level),
|
|
||||||
new DhBlockPos2D(#if MC_VER >= MC_1_19_2 origin.x(), origin.z() #else origin.x, origin.z #endif),
|
|
||||||
chunkRadius
|
|
||||||
);
|
|
||||||
|
|
||||||
future.whenComplete((result, throwable) -> {
|
|
||||||
if (throwable instanceof CancellationException)
|
|
||||||
{
|
|
||||||
this.sendSuccessResponse(c, "Pregen is cancelled", true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (throwable != null)
|
|
||||||
{
|
|
||||||
this.sendFailureResponse(c, "Pregen failed: " + throwable.getMessage() + "\n Check the logs for more details.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sendSuccessResponse(c, "Pregen is complete", true);
|
|
||||||
});
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int pregenStop(CommandContext<CommandSourceStack> c)
|
|
||||||
{
|
|
||||||
CompletableFuture<Void> runningPregen = this.pregenManager.getRunningPregen();
|
|
||||||
if (runningPregen == null)
|
|
||||||
{
|
|
||||||
return this.sendFailureResponse(c, "Pregen is not running");
|
|
||||||
}
|
|
||||||
|
|
||||||
runningPregen.cancel(true);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.commonMixins;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.ServerApi;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
public class MixinChunkMapCommon
|
|
||||||
{
|
|
||||||
|
|
||||||
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
|
|
||||||
{
|
|
||||||
// is this position already being updated?
|
|
||||||
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(chunk.getPos().x, chunk.getPos().z))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// is this chunk being saved to disk?
|
|
||||||
boolean savingChunkToDisk = ci.getReturnValue();
|
|
||||||
// true means a chunk was saved to disk
|
|
||||||
if (!savingChunkToDisk)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO are the following validations necessary since we are checking above if
|
|
||||||
// the callback return value should state if the chunk was actually saved or not?
|
|
||||||
// Do we trust it to always be correct?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// corrupt/incomplete chunk validation //
|
|
||||||
|
|
||||||
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
|
|
||||||
// this logic should prevent that from happening
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// biome validation //
|
|
||||||
|
|
||||||
// some chunks may be missing their biomes, which cause issues when attempting to save them
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
if (chunk.getBiomes() == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// this will throw an exception if the biomes aren't set up
|
|
||||||
chunk.getNoiseBiome(0,0,0);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// submit the update event
|
|
||||||
ServerApi.INSTANCE.serverChunkSaveEvent(
|
|
||||||
new ChunkWrapper(chunk, ServerLevelWrapper.getWrapper(level)),
|
|
||||||
ServerLevelWrapper.getWrapper(level)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Added to MC's dynamic textures via mixins
|
|
||||||
* in order to denote whether a texture is a lightmap or not. <br><br>
|
|
||||||
*
|
|
||||||
* If not done any dynamic texture could be used as the lightmap
|
|
||||||
* which causes some weird rendering bugs.
|
|
||||||
*/
|
|
||||||
public interface ILightTextureMarker
|
|
||||||
{
|
|
||||||
void markLightTexture();
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.util;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
|
||||||
|
|
||||||
public class ProxyUtil
|
|
||||||
{
|
|
||||||
|
|
||||||
public static ILevelWrapper getLevelWrapper(LevelAccessor level)
|
|
||||||
{
|
|
||||||
ILevelWrapper levelWrapper;
|
|
||||||
if (level instanceof ServerLevel)
|
|
||||||
{
|
|
||||||
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel) level);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel) level);
|
|
||||||
}
|
|
||||||
|
|
||||||
return levelWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds all necessary dependencies, so we
|
|
||||||
* can access them in Core. <br>
|
|
||||||
* This needs to be called before any Core classes
|
|
||||||
* are loaded.
|
|
||||||
*
|
|
||||||
* @author James Seibel
|
|
||||||
* @author Ran
|
|
||||||
* @version 12-1-2021
|
|
||||||
*/
|
|
||||||
public class DependencySetup
|
|
||||||
{
|
|
||||||
|
|
||||||
public static void createSharedBindings()
|
|
||||||
{
|
|
||||||
SingletonInjector.INSTANCE.bind(ILangWrapper.class, LangWrapper.INSTANCE);
|
|
||||||
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
|
|
||||||
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
|
|
||||||
SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void createServerBindings()
|
|
||||||
{ SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE); }
|
|
||||||
|
|
||||||
public static void createClientBindings()
|
|
||||||
{
|
|
||||||
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
|
|
||||||
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
|
|
||||||
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
|
|
||||||
SingletonInjector.INSTANCE.bind(IMinecraftGLWrapper.class, MinecraftGLWrapper.INSTANCE);
|
|
||||||
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-148
@@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers;
|
|
||||||
|
|
||||||
import java.nio.FloatBuffer;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class converts to and from Minecraft objects (Ex: Matrix4f)
|
|
||||||
* and objects we created (Ex: Mat4f).
|
|
||||||
*
|
|
||||||
* @author James Seibel
|
|
||||||
* @version 11-20-2021
|
|
||||||
*/
|
|
||||||
public class McObjectConverter
|
|
||||||
{
|
|
||||||
private static int bufferIndex(int x, int y)
|
|
||||||
{
|
|
||||||
return y * 4 + x;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** 4x4 float matrix converter */
|
|
||||||
@Deprecated
|
|
||||||
public static Mat4f Convert(
|
|
||||||
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
|
|
||||||
#elif MC_VER < MC_1_21_6 org.joml.Matrix4f
|
|
||||||
#else org.joml.Matrix4fc
|
|
||||||
#endif
|
|
||||||
mcMatrix)
|
|
||||||
{
|
|
||||||
FloatBuffer buffer = FloatBuffer.allocate(16);
|
|
||||||
storeMatrix(mcMatrix, buffer);
|
|
||||||
Mat4f matrix = new Mat4f(buffer);
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
matrix.transpose(); // In 1.19.3 and later, we no longer need to transpose it
|
|
||||||
#endif
|
|
||||||
return matrix;
|
|
||||||
}
|
|
||||||
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
|
|
||||||
private static void storeMatrix(
|
|
||||||
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
|
|
||||||
#elif MC_VER < MC_1_21_6 org.joml.Matrix4f
|
|
||||||
#else org.joml.Matrix4fc
|
|
||||||
#endif
|
|
||||||
matrix,
|
|
||||||
FloatBuffer buffer)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
matrix.store(buffer);
|
|
||||||
#else
|
|
||||||
// Mojang starts to use joml's Matrix4f libary in 1.19.3 so we copy their store method and use it here if its newer than 1.19.3
|
|
||||||
buffer.put(bufferIndex(0, 0), matrix.m00());
|
|
||||||
buffer.put(bufferIndex(0, 1), matrix.m01());
|
|
||||||
buffer.put(bufferIndex(0, 2), matrix.m02());
|
|
||||||
buffer.put(bufferIndex(0, 3), matrix.m03());
|
|
||||||
buffer.put(bufferIndex(1, 0), matrix.m10());
|
|
||||||
buffer.put(bufferIndex(1, 1), matrix.m11());
|
|
||||||
buffer.put(bufferIndex(1, 2), matrix.m12());
|
|
||||||
buffer.put(bufferIndex(1, 3), matrix.m13());
|
|
||||||
buffer.put(bufferIndex(2, 0), matrix.m20());
|
|
||||||
buffer.put(bufferIndex(2, 1), matrix.m21());
|
|
||||||
buffer.put(bufferIndex(2, 2), matrix.m22());
|
|
||||||
buffer.put(bufferIndex(2, 3), matrix.m23());
|
|
||||||
buffer.put(bufferIndex(3, 0), matrix.m30());
|
|
||||||
buffer.put(bufferIndex(3, 1), matrix.m31());
|
|
||||||
buffer.put(bufferIndex(3, 2), matrix.m32());
|
|
||||||
buffer.put(bufferIndex(3, 3), matrix.m33());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static final Direction[] directions;
|
|
||||||
static final EDhDirection[] lodDirections;
|
|
||||||
static
|
|
||||||
{
|
|
||||||
EDhDirection[] lodDirs = EDhDirection.values();
|
|
||||||
directions = new Direction[lodDirs.length];
|
|
||||||
lodDirections = new EDhDirection[lodDirs.length];
|
|
||||||
for (EDhDirection lodDir : lodDirs)
|
|
||||||
{
|
|
||||||
Direction dir;
|
|
||||||
switch (lodDir.name().toUpperCase())
|
|
||||||
{
|
|
||||||
case "DOWN":
|
|
||||||
dir = Direction.DOWN;
|
|
||||||
break;
|
|
||||||
case "UP":
|
|
||||||
dir = Direction.UP;
|
|
||||||
break;
|
|
||||||
case "NORTH":
|
|
||||||
dir = Direction.NORTH;
|
|
||||||
break;
|
|
||||||
case "SOUTH":
|
|
||||||
dir = Direction.SOUTH;
|
|
||||||
break;
|
|
||||||
case "WEST":
|
|
||||||
dir = Direction.WEST;
|
|
||||||
break;
|
|
||||||
case "EAST":
|
|
||||||
dir = Direction.EAST;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dir = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir == null)
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
|
|
||||||
}
|
|
||||||
directions[lodDir.ordinal()] = dir;
|
|
||||||
lodDirections[dir.ordinal()] = lodDir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); }
|
|
||||||
public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); }
|
|
||||||
|
|
||||||
public static Direction Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; }
|
|
||||||
public static EDhDirection Convert(Direction direction) { return lodDirections[direction.ordinal()]; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author James Seibel
|
|
||||||
* @version 12-11-2021
|
|
||||||
*/
|
|
||||||
public class VersionConstants implements IVersionConstants
|
|
||||||
{
|
|
||||||
public static final VersionConstants INSTANCE = new VersionConstants();
|
|
||||||
|
|
||||||
|
|
||||||
private VersionConstants()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMinecraftVersion()
|
|
||||||
{
|
|
||||||
// these values are hard-coded to prevent an issue with Forge (specifically 1.18.2) where
|
|
||||||
// it can't load client classes when running as a dedicated server,
|
|
||||||
// which was how we were dynamically accessing the MC version string
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5
|
|
||||||
return "1.16.5";
|
|
||||||
|
|
||||||
#elif MC_VER == MC_1_17_1
|
|
||||||
return "1.17.1";
|
|
||||||
|
|
||||||
#elif MC_VER == MC_1_18_2
|
|
||||||
return "1.18.2";
|
|
||||||
|
|
||||||
#elif MC_VER == MC_1_19_2
|
|
||||||
return "1.19.2";
|
|
||||||
#elif MC_VER == MC_1_19_4
|
|
||||||
return "1.19.4";
|
|
||||||
|
|
||||||
#elif MC_VER == MC_1_20_1
|
|
||||||
return "1.20.1";
|
|
||||||
#elif MC_VER == MC_1_20_2
|
|
||||||
return "1.20.2";
|
|
||||||
#elif MC_VER == MC_1_20_4
|
|
||||||
return "1.20.4";
|
|
||||||
#elif MC_VER == MC_1_20_6
|
|
||||||
return "1.20.6";
|
|
||||||
|
|
||||||
#elif MC_VER == MC_1_21_1
|
|
||||||
return "1.21.1";
|
|
||||||
#elif MC_VER == MC_1_21_3
|
|
||||||
return "1.21.3";
|
|
||||||
#elif MC_VER == MC_1_21_4
|
|
||||||
return "1.21.4";
|
|
||||||
#elif MC_VER == MC_1_21_5
|
|
||||||
return "1.21.5";
|
|
||||||
#elif MC_VER == MC_1_21_6
|
|
||||||
return "1.21.6";
|
|
||||||
#elif MC_VER == MC_1_21_8
|
|
||||||
return "1.21.8";
|
|
||||||
|
|
||||||
#elif MC_VER == MC_1_21_9
|
|
||||||
return "1.21.9";
|
|
||||||
#elif MC_VER == MC_1_21_10
|
|
||||||
return "1.21.10";
|
|
||||||
#elif MC_VER == MC_1_21_11
|
|
||||||
return "1.21.11";
|
|
||||||
#else
|
|
||||||
ERROR MC version constant missing
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,357 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
|
||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
|
||||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
#if MC_VER > MC_1_17_1
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
#endif
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.LevelReader;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This handles creating abstract wrapper objects.
|
|
||||||
*/
|
|
||||||
public class WrapperFactory implements IWrapperFactory
|
|
||||||
{
|
|
||||||
public static final WrapperFactory INSTANCE = new WrapperFactory();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// core methods //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBatchGeneratorEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
|
|
||||||
{
|
|
||||||
if (targetLevel instanceof IDhServerLevel)
|
|
||||||
{
|
|
||||||
return new BatchGenerationEnvironment((IDhServerLevel) targetLevel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("The target level must be a server-side level.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
|
|
||||||
{
|
|
||||||
if (!(levelWrapper instanceof ILevelWrapper))
|
|
||||||
{
|
|
||||||
throw new ClassCastException("levelWrapper must be returned by DH and of type ["+ILevelWrapper.class.getName()+"].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return BiomeWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
|
|
||||||
{
|
|
||||||
if (!(levelWrapper instanceof ILevelWrapper))
|
|
||||||
{
|
|
||||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return BlockStateWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); }
|
|
||||||
@Override
|
|
||||||
public IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper) // TODO is there a way we could get this without the levelWrapper? it isn't necessary but would clean up the code a bit
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, levelWrapper);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
throw new LodUtil.AssertFailureException("Unable to parse plains resource string ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], error:\n " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); }
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
|
|
||||||
@Override
|
|
||||||
public HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); }
|
|
||||||
@Override
|
|
||||||
public void resetRendererIgnoredBlocksSet() { BlockStateWrapper.clearRendererIgnoredBlocks(); }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: when this is updated for different MC versions, make sure you also update the documentation in
|
|
||||||
* {@link IDhApiWorldGenerator#generateChunks} and the type list in {@link WrapperFactory#createChunkWrapperErrorMessage}. <br><br>
|
|
||||||
*
|
|
||||||
* For full method documentation please see: {@link IWrapperFactory#createChunkWrapper}
|
|
||||||
*
|
|
||||||
* @see IWrapperFactory#createChunkWrapper
|
|
||||||
*/
|
|
||||||
public IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException
|
|
||||||
{
|
|
||||||
if (objectArray.length == 1 && objectArray[0] instanceof IChunkWrapper)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// this path should only happen when called by Distant Horizons code
|
|
||||||
// API implementors should never hit this path
|
|
||||||
return (IChunkWrapper) objectArray[0];
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//#if MC_VER <= MC_1_XX_X
|
|
||||||
else if (objectArray.length == 2)
|
|
||||||
{
|
|
||||||
// correct number of parameters from the API
|
|
||||||
|
|
||||||
// chunk
|
|
||||||
if (!(objectArray[0] instanceof ChunkAccess))
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
ChunkAccess chunk = (ChunkAccess) objectArray[0];
|
|
||||||
|
|
||||||
// level / light source
|
|
||||||
if (!(objectArray[1] instanceof Level))
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
// the level is needed for the DH level wrapper...
|
|
||||||
Level level = (Level) objectArray[1];
|
|
||||||
|
|
||||||
|
|
||||||
// level wrapper
|
|
||||||
ILevelWrapper levelWrapper = level.isClientSide()
|
|
||||||
? ClientLevelWrapper.getWrapper((ClientLevel)level)
|
|
||||||
: ServerLevelWrapper.getWrapper((ServerLevel)level);
|
|
||||||
|
|
||||||
|
|
||||||
return new ChunkWrapper(chunk, levelWrapper);
|
|
||||||
}
|
|
||||||
// incorrect number of parameters from the API
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
//#endif
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Note: when this is updated for different MC versions,
|
|
||||||
* make sure you also update the documentation in {@link IDhApiWorldGenerator#generateChunks}.
|
|
||||||
*/
|
|
||||||
private static String createChunkWrapperErrorMessage(Object[] objectArray)
|
|
||||||
{
|
|
||||||
String[] expectedClassNames;
|
|
||||||
|
|
||||||
//#if MC_VER <= MC_1_XX_X
|
|
||||||
expectedClassNames = new String[]
|
|
||||||
{
|
|
||||||
ChunkAccess.class.getName(),
|
|
||||||
"[ServerLevel] or [ClientLevel]" // Classes are not referenced by names to avoid exception when one of them is missing
|
|
||||||
};
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// api methods //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
// documentation should be in the API interface
|
|
||||||
|
|
||||||
public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// confirm the API level wrapper is also a Core wrapper
|
|
||||||
if (!(levelWrapper instanceof ILevelWrapper))
|
|
||||||
{
|
|
||||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
|
||||||
}
|
|
||||||
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_4
|
|
||||||
if (objectArray.length != 1)
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
if (!(objectArray[0] instanceof Biome))
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
|
|
||||||
Biome biome = (Biome) objectArray[0];
|
|
||||||
return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper);
|
|
||||||
#else
|
|
||||||
if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome))
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
|
|
||||||
Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0];
|
|
||||||
return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Note: when this is updated for different MC versions,
|
|
||||||
* make sure you also update the documentation in {@link IDhApiWrapperFactory#getBiomeWrapper}.
|
|
||||||
*/
|
|
||||||
private static String createBiomeWrapperErrorMessage(Object[] objectArray)
|
|
||||||
{
|
|
||||||
String[] expectedClassNames;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
expectedClassNames = new String[] { Biome.class.getName() };
|
|
||||||
#else
|
|
||||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// confirm the API level wrapper is also a Core wrapper
|
|
||||||
if (!(levelWrapper instanceof ILevelWrapper))
|
|
||||||
{
|
|
||||||
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
|
|
||||||
}
|
|
||||||
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//#if MC_VER <= MC_1_XX_X
|
|
||||||
if (objectArray.length != 1)
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
if (!(objectArray[0] instanceof BlockState))
|
|
||||||
{
|
|
||||||
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockState blockState = (BlockState) objectArray[0];
|
|
||||||
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
|
|
||||||
//#endif
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Note: when this is updated for different MC versions,
|
|
||||||
* make sure you also update the documentation in {@link IDhApiWrapperFactory#getBlockStateWrapper}.
|
|
||||||
*/
|
|
||||||
private static String createBlockStateWrapperErrorMessage(Object[] objectArray)
|
|
||||||
{
|
|
||||||
String[] expectedClassNames;
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
expectedClassNames = new String[] { Biome.class.getName() };
|
|
||||||
#else
|
|
||||||
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper methods //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
private static String createWrapperErrorMessage(String wrapperName, String[] expectedClassNames, Object[] objectArray)
|
|
||||||
{
|
|
||||||
// error header
|
|
||||||
StringBuilder message = new StringBuilder(
|
|
||||||
wrapperName + " creation failed. \n" +
|
|
||||||
"Expected object array parameters: \n");
|
|
||||||
|
|
||||||
|
|
||||||
// expected parameters
|
|
||||||
for (String expectedClassName : expectedClassNames)
|
|
||||||
{
|
|
||||||
message.append("[").append(expectedClassName).append("], \n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// given parameters
|
|
||||||
if (objectArray.length != 0)
|
|
||||||
{
|
|
||||||
message.append("Given parameters: ");
|
|
||||||
for (Object obj : objectArray)
|
|
||||||
{
|
|
||||||
String objClassName = (obj != null) ? obj.getClass().getName() : "NULL";
|
|
||||||
message.append("[").append(objClassName).append("], ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message.append(" No parameters given.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return message.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-337
@@ -1,337 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.block;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.dataObjects.BlockBiomeWrapperPair;
|
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
|
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.world.level.BlockAndTintGetter;
|
|
||||||
import net.minecraft.world.level.ColorResolver;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class AbstractDhTintGetter implements BlockAndTintGetter
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
private static final ConcurrentHashMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
|
|
||||||
#else
|
|
||||||
private static final ConcurrentHashMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private static final ConcurrentHashMap<BlockBiomeWrapperPair, Integer> COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>();
|
|
||||||
/** returned if the color cache is incomplete */
|
|
||||||
public static final int INVALID_COLOR = Integer.MIN_VALUE;
|
|
||||||
|
|
||||||
|
|
||||||
protected BiomeWrapper biomeWrapper;
|
|
||||||
protected BlockStateWrapper blockStateWrapper;
|
|
||||||
protected FullDataSourceV2 fullDataSource;
|
|
||||||
protected int smoothingRadiusInBlocks;
|
|
||||||
protected IClientLevelWrapper clientLevelWrapper;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public AbstractDhTintGetter() { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mutates this getter so we can access the necessary
|
|
||||||
* variables for tint getting.
|
|
||||||
*/
|
|
||||||
public void update(BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
|
|
||||||
{
|
|
||||||
this.biomeWrapper = biomeWrapper;
|
|
||||||
this.blockStateWrapper = blockStateWrapper;
|
|
||||||
this.fullDataSource = fullDataSource;
|
|
||||||
this.clientLevelWrapper = clientLevelWrapper;
|
|
||||||
this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// shared methods //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
/** Called by MC's tint getter */
|
|
||||||
@Override
|
|
||||||
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
|
|
||||||
{
|
|
||||||
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
|
||||||
return this.tryGetBlockTint(mutableBlockPos, colorResolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be called by DH directly, skipping some of MC's logic
|
|
||||||
* to speed up tint getting slightly.
|
|
||||||
*
|
|
||||||
* @return {@link AbstractDhTintGetter#INVALID_COLOR} if any of the biomes needed for this position
|
|
||||||
* were not cached. In that case calling {@link AbstractDhTintGetter#getBlockTint(BlockPos, ColorResolver)}
|
|
||||||
* will need to be called by MC's ColorResolver so we can
|
|
||||||
* populate the color cache.
|
|
||||||
*/
|
|
||||||
public int tryGetBlockTint(DhBlockPosMutable mutableBlockPos)
|
|
||||||
{ return this.tryGetBlockTint(mutableBlockPos, null); }
|
|
||||||
|
|
||||||
private int tryGetBlockTint(DhBlockPosMutable mutableBlockPos, @Nullable ColorResolver colorResolver)
|
|
||||||
{
|
|
||||||
// determine how wide this data source is so we can determine
|
|
||||||
// if blending should be used
|
|
||||||
byte dataSourceDetailLevel = DhSectionPos.getDetailLevel(this.fullDataSource.getPos());
|
|
||||||
// convert from section detail level to absolute detail level
|
|
||||||
dataSourceDetailLevel = (byte)(dataSourceDetailLevel - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
|
||||||
int dataSourceLodWidthInBlocks = DhSectionPos.getDetailLevelWidthInBlocks(dataSourceDetailLevel);
|
|
||||||
|
|
||||||
// don't do any smoothing if smoothing is disabled or if the LOD
|
|
||||||
// is to large for block-based smoothing to show up
|
|
||||||
if (this.smoothingRadiusInBlocks == 0
|
|
||||||
|| dataSourceLodWidthInBlocks > this.smoothingRadiusInBlocks)
|
|
||||||
{
|
|
||||||
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// use a rolling average to calculate the color
|
|
||||||
int dataPointCount = 0;
|
|
||||||
int rollingRed = 0;
|
|
||||||
int rollingGreen = 0;
|
|
||||||
int rollingBlue = 0;
|
|
||||||
|
|
||||||
int xMin = mutableBlockPos.getX() - this.smoothingRadiusInBlocks;
|
|
||||||
int xMax = mutableBlockPos.getX() + this.smoothingRadiusInBlocks + 1; // +1 to account for the center block
|
|
||||||
|
|
||||||
int zMin = mutableBlockPos.getZ() - this.smoothingRadiusInBlocks;
|
|
||||||
int zMax = mutableBlockPos.getZ() + this.smoothingRadiusInBlocks + 1;
|
|
||||||
|
|
||||||
int levelMinY = this.clientLevelWrapper.getMinHeight();
|
|
||||||
|
|
||||||
for (int x = xMin; x < xMax; x++)
|
|
||||||
{
|
|
||||||
for (int z = zMin; z < zMax; z++)
|
|
||||||
{
|
|
||||||
mutableBlockPos.setX(x);
|
|
||||||
mutableBlockPos.setZ(z);
|
|
||||||
|
|
||||||
// this can return the same position/datapoint for larger LODs duplicating work,
|
|
||||||
// however for small smoothing ranges that isn't a big deal and for large LODs
|
|
||||||
// we ignore smoothing anyway
|
|
||||||
long dataPoint = this.fullDataSource.getDataPointAtBlockPos(mutableBlockPos.getX(), mutableBlockPos.getY(), mutableBlockPos.getZ(), levelMinY);
|
|
||||||
if (dataPoint == FullDataPointUtil.EMPTY_DATA_POINT)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// get the color for this nearby position
|
|
||||||
int id = FullDataPointUtil.getId(dataPoint);
|
|
||||||
BiomeWrapper biomeWrapper = (BiomeWrapper) this.fullDataSource.mapping.getBiomeWrapper(id);
|
|
||||||
int color = this.tryGetClientBiomeColor(colorResolver, biomeWrapper);
|
|
||||||
if (color == INVALID_COLOR)
|
|
||||||
{
|
|
||||||
return INVALID_COLOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// rolling average
|
|
||||||
rollingRed += ColorUtil.getRed(color);
|
|
||||||
rollingGreen += ColorUtil.getGreen(color);
|
|
||||||
rollingBlue += ColorUtil.getBlue(color);
|
|
||||||
|
|
||||||
dataPointCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// if no data was present (rarely possible)
|
|
||||||
// just use the default center's color
|
|
||||||
if (dataPointCount == 0)
|
|
||||||
{
|
|
||||||
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
int colorInt = ColorUtil.argbToInt(
|
|
||||||
255, // blending often ignores alpha, having it always 255 prevents multiplication issues later
|
|
||||||
rollingRed / dataPointCount,
|
|
||||||
rollingGreen / dataPointCount,
|
|
||||||
rollingBlue / dataPointCount);
|
|
||||||
return colorInt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If given a ColorResolver this will always succeed. <Br>
|
|
||||||
* If not it will attempt to use the cached color.
|
|
||||||
*/
|
|
||||||
private int tryGetClientBiomeColor(@Nullable ColorResolver colorResolver, BiomeWrapper biomeWrapper)
|
|
||||||
{
|
|
||||||
BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(this.blockStateWrapper, biomeWrapper);
|
|
||||||
|
|
||||||
// use the cached color if possible
|
|
||||||
Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); // explicit Integer return here reduces unnecessary allocations
|
|
||||||
if (cachedColor != null)
|
|
||||||
{
|
|
||||||
return cachedColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorResolver == null)
|
|
||||||
{
|
|
||||||
// no color resolver is present,
|
|
||||||
// the cache needs to be populated before
|
|
||||||
// we can use the fast path
|
|
||||||
return INVALID_COLOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int color = colorResolver.getColor(unwrapClientBiome(biomeWrapper), 0, 0);
|
|
||||||
COLOR_BY_BLOCK_BIOME_PAIR.put(pair, color);
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Biome unwrapClientBiome(BiomeWrapper biomeWrapper)
|
|
||||||
{
|
|
||||||
String biomeString = biomeWrapper.getSerialString();
|
|
||||||
if (biomeString == null
|
|
||||||
|| biomeString.isEmpty()
|
|
||||||
|| biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING))
|
|
||||||
{
|
|
||||||
// default to "plains" for empty/invalid biomes
|
|
||||||
biomeString = "minecraft:plains";
|
|
||||||
}
|
|
||||||
|
|
||||||
return unwrapBiome(getClientBiome(biomeString));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Biome unwrapBiome(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
|
||||||
{
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
return biome.value();
|
|
||||||
#else
|
|
||||||
return biome;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Previously, this class might have immediately unwrapped the Holder like this:</p>
|
|
||||||
* <pre>{@code
|
|
||||||
* // Inside constructor (OLD WAY - PROBLEMATIC):
|
|
||||||
* Holder<Biome> biomeHolder = getTheHolderFromSomewhere();
|
|
||||||
* this.biome = biomeHolder.value(); // <-- PROBLEM HERE
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* <p>This approach is problematic because the {@link net.minecraft.core.Holder} system,
|
|
||||||
* particularly {@code Holder.Reference}, is designed for <strong>late binding</strong>. Here's why storing
|
|
||||||
* the Holder itself is now necessary:</p>
|
|
||||||
* <ol>
|
|
||||||
* <li>A {@code Holder.Reference<Biome>} might be created initially just with a
|
|
||||||
* {@link net.minecraft.resources.ResourceKey} (like {@code minecraft:plains}), but its actual
|
|
||||||
* {@link net.minecraft.core.Holder#value() value()} (the {@code Biome} object itself) might be {@code null}
|
|
||||||
* at construction time.</li>
|
|
||||||
* <li>Later, during game loading, registry population, or potentially due to modifications by other mods
|
|
||||||
* (e.g., Polytone), the system calls internal binding methods (like {@code bindValue(Biome)})
|
|
||||||
* on the {@code Holder} instance. This sets or <strong>updates</strong> the internal reference to the
|
|
||||||
* actual {@code Biome} object.</li>
|
|
||||||
* <li>Crucially, the binding process might assign a completely <strong>new</strong> {@code Biome} object
|
|
||||||
* instance to the {@code Holder} reference, replacing any previous one.</li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* <p>If we unwrapped the {@code Holder} using {@code .value()} within the constructor (the old way),
|
|
||||||
* our class's internal {@code biome} field would permanently store a reference to whatever {@code Biome}
|
|
||||||
* object the {@code Holder} pointed to *at that exact moment*. It would have no link back to the
|
|
||||||
* {@code Holder} and would be unaware if the {@code Holder} was later updated to point to a different
|
|
||||||
* (or the initially missing) {@code Biome} object. This would lead to using stale or even {@code null} data.</p>
|
|
||||||
*
|
|
||||||
* <p>By storing the {@code Holder<Biome>} itself, this class can call {@link net.minecraft.core.Holder#value()}
|
|
||||||
* whenever the biome information is needed, ensuring it always retrieves the most current {@code Biome}
|
|
||||||
* instance associated with the holder at that time.</p>
|
|
||||||
*/
|
|
||||||
private static #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif getClientBiome(String biomeResourceString)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
Biome biome;
|
|
||||||
#else
|
|
||||||
Holder<Biome> biome;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// calling get instead of compute is slightly faster for already
|
|
||||||
// computed values
|
|
||||||
biome = BIOME_BY_RESOURCE_STRING.get(biomeResourceString);
|
|
||||||
if (biome != null)
|
|
||||||
{
|
|
||||||
return biome;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// cache the client biomes so we don't have to re-parse the resource location every time
|
|
||||||
return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString,
|
|
||||||
(resourceString, existingBiome) ->
|
|
||||||
{
|
|
||||||
if (existingBiome != null)
|
|
||||||
{
|
|
||||||
return existingBiome;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientLevel clientLevel = Minecraft.getInstance().level;
|
|
||||||
if (clientLevel == null)
|
|
||||||
{
|
|
||||||
// shouldn't happen, but just in case
|
|
||||||
throw new IllegalStateException("Attempted to get client biome when no client level was loaded.");
|
|
||||||
}
|
|
||||||
|
|
||||||
BiomeWrapper.BiomeDeserializeResult result;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = BiomeWrapper.deserializeBiome(resourceString, clientLevel.registryAccess());
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Unable to deserialize client biome ["+resourceString+"], using fallback...");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = BiomeWrapper.deserializeBiome(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, clientLevel.registryAccess());
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
// should never happen, if it does this log will explode, but just in case
|
|
||||||
LOGGER.error("Unable to deserialize fallback client biome ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], returning NULL.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.success)
|
|
||||||
{
|
|
||||||
existingBiome = result.biome;
|
|
||||||
}
|
|
||||||
|
|
||||||
return existingBiome;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-411
@@ -1,411 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.block;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.data.BuiltinRegistries;
|
|
||||||
#else
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
#else
|
|
||||||
import net.minecraft.resources.Identifier;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
|
|
||||||
public class BiomeWrapper implements IBiomeWrapper
|
|
||||||
{
|
|
||||||
// must be defined before AIR, otherwise a null pointer will be thrown
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
public static final ConcurrentMap<Biome, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
|
|
||||||
#else
|
|
||||||
public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public static final ConcurrentHashMap<String, BiomeWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public static final String EMPTY_BIOME_STRING = "EMPTY";
|
|
||||||
public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(null, null);
|
|
||||||
|
|
||||||
public static final String PLAINS_RESOURCE_LOCATION_STRING = "minecraft:plains";
|
|
||||||
|
|
||||||
/** keep track of broken biomes so we don't log every time */
|
|
||||||
private static final HashSet<String> brokenResourceLocationStrings = new HashSet<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only display this warning once, otherwise the log may be spammed <br>
|
|
||||||
* This is a known issue when joining Hypixel.
|
|
||||||
*/
|
|
||||||
private static boolean emptyStringWarningLogged = false;
|
|
||||||
private static boolean emptyLevelSerializeFailLogged = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// properties //
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
public final Biome biome;
|
|
||||||
#else
|
|
||||||
public final Holder<Biome> biome;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** technically final, but since it requires a method call to generate it can't be marked as such */
|
|
||||||
private String serialString;
|
|
||||||
private final int hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// constructors //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
public static BiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
if (biome == null)
|
|
||||||
{
|
|
||||||
return EMPTY_WRAPPER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BiomeWrapper biomeWrapper = WRAPPER_BY_BIOME.get(biome);
|
|
||||||
if (biomeWrapper != null)
|
|
||||||
{
|
|
||||||
return biomeWrapper;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BiomeWrapper newWrapper = new BiomeWrapper(biome, levelWrapper);
|
|
||||||
WRAPPER_BY_BIOME.put(biome, newWrapper);
|
|
||||||
return newWrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private BiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
this.biome = biome;
|
|
||||||
this.serialString = this.serialize(levelWrapper);
|
|
||||||
this.hashCode = Objects.hash(this.serialString);
|
|
||||||
|
|
||||||
//LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// methods //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName()
|
|
||||||
{
|
|
||||||
if (this == EMPTY_WRAPPER)
|
|
||||||
{
|
|
||||||
return EMPTY_BIOME_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
return biome.toString();
|
|
||||||
#else
|
|
||||||
return this.biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj)
|
|
||||||
{
|
|
||||||
if (this == obj)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (obj == null || this.getClass() != obj.getClass())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
BiomeWrapper that = (BiomeWrapper) obj;
|
|
||||||
// the serialized value is used so we can test the contents instead of the references
|
|
||||||
return Objects.equals(this.getSerialString(), that.getSerialString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() { return this.hashCode; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSerialString() { return this.serialString; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getWrappedMcObject() { return this.biome; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return this.getSerialString(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======================//
|
|
||||||
// serialization methods //
|
|
||||||
//=======================//
|
|
||||||
|
|
||||||
public String serialize(ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
if (this.biome == null)
|
|
||||||
{
|
|
||||||
return EMPTY_BIOME_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// we can't generate a serial string if the level is null
|
|
||||||
if (levelWrapper == null)
|
|
||||||
{
|
|
||||||
if (!emptyLevelSerializeFailLogged)
|
|
||||||
{
|
|
||||||
emptyLevelSerializeFailLogged = true;
|
|
||||||
LOGGER.warn("Unable to serialize biome: [" + this.biome + "] because the passed in level wrapper is null. Future errors of this type won't be logged.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return EMPTY_BIOME_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// generate the serial string //
|
|
||||||
|
|
||||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
|
||||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_11
|
|
||||||
ResourceLocation resourceLocation;
|
|
||||||
#else
|
|
||||||
Identifier resourceLocation;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
|
|
||||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
|
||||||
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
|
|
||||||
#else
|
|
||||||
resourceLocation = registryAccess.lookupOrThrow(Registries.BIOME).getKey(this.biome.value());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (resourceLocation == null)
|
|
||||||
{
|
|
||||||
String biomeName;
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
biomeName = this.biome.toString();
|
|
||||||
#else
|
|
||||||
biomeName = this.biome.value().toString();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LOGGER.warn("unable to serialize: " + biomeName);
|
|
||||||
// shouldn't normally happen, but just in case
|
|
||||||
this.serialString = "";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.serialString = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.serialString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO would it be worth while to cache these objects in a ConcurrentHashMap<string, IBiomeWrapper>?
|
|
||||||
public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException
|
|
||||||
{
|
|
||||||
// we need the final string for the concurrent hash map later
|
|
||||||
final String finalResourceStateString = resourceLocationString;
|
|
||||||
|
|
||||||
if (resourceLocationString.equals(EMPTY_BIOME_STRING))
|
|
||||||
{
|
|
||||||
if (!emptyStringWarningLogged)
|
|
||||||
{
|
|
||||||
emptyStringWarningLogged = true;
|
|
||||||
LOGGER.warn("[" + EMPTY_BIOME_STRING + "] biome string deserialized. This may mean the level was null when a save was attempted, a file saving error, or a biome saving error. Future errors will not be logged.");
|
|
||||||
}
|
|
||||||
return EMPTY_WRAPPER;
|
|
||||||
}
|
|
||||||
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
|
|
||||||
{
|
|
||||||
LOGGER.warn("Null biome string deserialized.");
|
|
||||||
return EMPTY_WRAPPER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
|
|
||||||
{
|
|
||||||
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if no wrapper is found, default to the empty wrapper
|
|
||||||
BiomeWrapper foundWrapper = EMPTY_WRAPPER;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Level level = (Level) levelWrapper.getWrappedMcObject();
|
|
||||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
|
||||||
|
|
||||||
BiomeDeserializeResult deserializeResult = deserializeBiome(resourceLocationString, registryAccess);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!deserializeResult.success)
|
|
||||||
{
|
|
||||||
if (!brokenResourceLocationStrings.contains(resourceLocationString))
|
|
||||||
{
|
|
||||||
brokenResourceLocationStrings.add(resourceLocationString);
|
|
||||||
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
|
|
||||||
}
|
|
||||||
return EMPTY_WRAPPER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foundWrapper = getBiomeWrapper(deserializeResult.biome, levelWrapper);
|
|
||||||
return foundWrapper;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BiomeWrapper: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BiomeDeserializeResult deserializeBiome(String resourceLocationString, net.minecraft.core.RegistryAccess registryAccess) throws IOException
|
|
||||||
{
|
|
||||||
// parse the resource location
|
|
||||||
int separatorIndex = resourceLocationString.indexOf(":");
|
|
||||||
if (separatorIndex == -1)
|
|
||||||
{
|
|
||||||
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_11
|
|
||||||
ResourceLocation resourceLocation;
|
|
||||||
#else
|
|
||||||
Identifier resourceLocation;
|
|
||||||
#endif
|
|
||||||
try
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_20_6
|
|
||||||
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
|
||||||
#else
|
|
||||||
resourceLocation = Identifier.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new IOException("No Resource Location found for the string: [" + resourceLocationString + "] Error: [" + e.getMessage() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boolean success;
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
|
||||||
success = (biome != null);
|
|
||||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
|
||||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
|
||||||
success = (unwrappedBiome != null);
|
|
||||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
|
|
||||||
success = (unwrappedBiome != null);
|
|
||||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
|
||||||
#else
|
|
||||||
Holder<Biome> biome;
|
|
||||||
Optional<Holder.Reference<Biome>> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation);
|
|
||||||
if (optionalBiomeHolder.isPresent())
|
|
||||||
{
|
|
||||||
Biome unwrappedBiome = optionalBiomeHolder.get().value();
|
|
||||||
success = (unwrappedBiome != null);
|
|
||||||
biome = new Holder.Direct<>(unwrappedBiome);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
biome = null;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return new BiomeDeserializeResult(success, biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper classes //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
public static class BiomeDeserializeResult
|
|
||||||
{
|
|
||||||
public final boolean success;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
public final Biome biome;
|
|
||||||
#else
|
|
||||||
public final Holder<Biome> biome;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public BiomeDeserializeResult(boolean success, #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome)
|
|
||||||
{
|
|
||||||
this.success = success;
|
|
||||||
this.biome = biome;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-923
@@ -1,923 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.block;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
|
||||||
import net.minecraft.tags.BlockTags;
|
|
||||||
import net.minecraft.world.level.block.BeaconBeamBlock;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.Blocks;
|
|
||||||
import net.minecraft.world.level.block.SoundType;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
|
||||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
|
||||||
#else
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
#else
|
|
||||||
import net.minecraft.resources.Identifier;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public class BlockStateWrapper implements IBlockStateWrapper
|
|
||||||
{
|
|
||||||
/** example "minecraft:water" */
|
|
||||||
public static final String RESOURCE_LOCATION_SEPARATOR = ":";
|
|
||||||
/** example "minecraft:water_STATE_{level:0}" */
|
|
||||||
public static final String STATE_STRING_SEPARATOR = "_STATE_";
|
|
||||||
|
|
||||||
|
|
||||||
// must be defined before AIR, otherwise a null pointer will be thrown
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
|
|
||||||
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public static final String AIR_STRING = "AIR";
|
|
||||||
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
|
|
||||||
|
|
||||||
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
|
|
||||||
public static final String WATER_RESOURCE_LOCATION_STRING = "minecraft:water";
|
|
||||||
|
|
||||||
/** Used to handle older MC versions that don't have an simple way of getting the block's tags */
|
|
||||||
public static final List<String> OLD_BEACON_BASE_BLOCK_NAME_LIST = Arrays.asList(
|
|
||||||
"iron_block",
|
|
||||||
"gold_block",
|
|
||||||
"diamond_block",
|
|
||||||
"emerald_block",
|
|
||||||
"netherite_block"
|
|
||||||
);
|
|
||||||
|
|
||||||
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
|
|
||||||
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
|
|
||||||
|
|
||||||
/** keep track of broken blocks so we don't log every time */
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
private static final HashSet<ResourceLocation> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
|
|
||||||
#else
|
|
||||||
private static final HashSet<Identifier> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// properties //
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public final BlockState blockState;
|
|
||||||
/** technically final, but since it requires a method call to generate it can't be marked as such */
|
|
||||||
private String serialString;
|
|
||||||
private final int hashCode;
|
|
||||||
/** Should be between {@link LodUtil#BLOCK_FULLY_OPAQUE} and {@link LodUtil#BLOCK_FULLY_OPAQUE} */
|
|
||||||
private final int opacity;
|
|
||||||
/** used by the Iris shader mod to determine how each LOD should be rendered */
|
|
||||||
private byte blockMaterialId = 0;
|
|
||||||
|
|
||||||
private final boolean isBeaconBlock;
|
|
||||||
private final boolean isBeaconBaseBlock;
|
|
||||||
private final boolean allowsBeaconBeamPassage;
|
|
||||||
/** null if this block can't tint beacons */
|
|
||||||
private final Color beaconTintColor;
|
|
||||||
private final Color mapColor;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// constructors //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
if (blockState == null || blockState.isAir())
|
|
||||||
{
|
|
||||||
return AIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (WRAPPER_BY_BLOCK_STATE.containsKey(blockState))
|
|
||||||
{
|
|
||||||
return WRAPPER_BY_BLOCK_STATE.get(blockState);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BlockStateWrapper newWrapper = new BlockStateWrapper(blockState, levelWrapper);
|
|
||||||
WRAPPER_BY_BLOCK_STATE.put(blockState, newWrapper);
|
|
||||||
return newWrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be faster than {@link BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)}
|
|
||||||
* in cases where the same block state is expected to be referenced multiple times.
|
|
||||||
*/
|
|
||||||
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess)
|
|
||||||
{
|
|
||||||
BlockState guessBlockState = (guess == null || guess.isAir()) ? null : (BlockState) guess.getWrappedMcObject();
|
|
||||||
BlockState inputBlockState = (blockState == null || blockState.isAir()) ? null : blockState;
|
|
||||||
|
|
||||||
if (guess instanceof BlockStateWrapper
|
|
||||||
&& guessBlockState == inputBlockState)
|
|
||||||
{
|
|
||||||
return (BlockStateWrapper) guess;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return fromBlockState(blockState, levelWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
this.blockState = blockState;
|
|
||||||
this.serialString = this.serialize(levelWrapper);
|
|
||||||
this.hashCode = Objects.hash(this.serialString);
|
|
||||||
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
|
|
||||||
this.opacity = this.calculateOpacity();
|
|
||||||
|
|
||||||
String lowercaseSerial = this.serialString.toLowerCase();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// beacon base blocks
|
|
||||||
#if MC_VER <= MC_1_18_2
|
|
||||||
// Older MC versions are harder to get block tags, so just use a static list to determine beacon blocks
|
|
||||||
boolean isBeaconBaseBlock = false;
|
|
||||||
for (int i = 0; i < OLD_BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
|
|
||||||
{
|
|
||||||
String baseBlockName = OLD_BEACON_BASE_BLOCK_NAME_LIST.get(i);
|
|
||||||
if (lowercaseSerial.contains(baseBlockName))
|
|
||||||
{
|
|
||||||
isBeaconBaseBlock = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.isBeaconBaseBlock = isBeaconBaseBlock;
|
|
||||||
#else
|
|
||||||
if (blockState != null)
|
|
||||||
{
|
|
||||||
// check if this block has any tags
|
|
||||||
Stream<TagKey<Block>> tags = blockState.getTags();
|
|
||||||
this.isBeaconBaseBlock = tags.anyMatch((TagKey<Block> tag) -> tag.location().getPath().toLowerCase().contains("beacon_base_blocks"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.isBeaconBaseBlock = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// beacon block
|
|
||||||
this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon");
|
|
||||||
|
|
||||||
|
|
||||||
// beacon tint color
|
|
||||||
Color beaconTintColor = null;
|
|
||||||
if (this.blockState != null
|
|
||||||
// beacon blocks also show up here, but since they block the beacon beam we don't want their color
|
|
||||||
&& !this.isBeaconBlock)
|
|
||||||
{
|
|
||||||
Block block = this.blockState.getBlock();
|
|
||||||
if (block instanceof BeaconBeamBlock)
|
|
||||||
{
|
|
||||||
int colorInt;
|
|
||||||
#if MC_VER <= MC_1_19_4
|
|
||||||
colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col;
|
|
||||||
#else
|
|
||||||
colorInt = ((BeaconBeamBlock) block).getColor().getMapColor().col;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.beaconTintColor = beaconTintColor;
|
|
||||||
|
|
||||||
|
|
||||||
// allow/deny beacon beam passage
|
|
||||||
boolean allowsBeaconBeamPassage;
|
|
||||||
if (this.blockState != null)
|
|
||||||
{
|
|
||||||
// get block properties (defaults to the values used by air)
|
|
||||||
boolean canOcclude = this.getCanOcclude();
|
|
||||||
boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown();
|
|
||||||
|
|
||||||
if (lowercaseSerial.contains("minecraft:bedrock"))
|
|
||||||
{
|
|
||||||
// bedrock is a special case fully opaque block that does allow beacons through
|
|
||||||
allowsBeaconBeamPassage = true;
|
|
||||||
}
|
|
||||||
else if (lowercaseSerial.contains("minecraft:tinted_glass"))
|
|
||||||
{
|
|
||||||
// tinted glass is a special case where it isn't fully opaque,
|
|
||||||
// but should block beacons
|
|
||||||
allowsBeaconBeamPassage = false;
|
|
||||||
}
|
|
||||||
else if (propagatesSkyLightDown || !canOcclude)
|
|
||||||
{
|
|
||||||
// stairs, cake, fences, etc.
|
|
||||||
allowsBeaconBeamPassage = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// non-opaque blocks (glass, mob spawners, etc.)
|
|
||||||
// all allow beacons through
|
|
||||||
allowsBeaconBeamPassage = (this.opacity != LodUtil.BLOCK_FULLY_OPAQUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// air allows beacons through
|
|
||||||
allowsBeaconBeamPassage = true;
|
|
||||||
}
|
|
||||||
this.allowsBeaconBeamPassage = allowsBeaconBeamPassage;
|
|
||||||
|
|
||||||
|
|
||||||
int mcColor = 0;
|
|
||||||
if (this.blockState != null)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
mcColor = this.blockState.getMaterial().getColor().col;
|
|
||||||
#else
|
|
||||||
mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col;
|
|
||||||
#endif
|
|
||||||
this.mapColor = ColorUtil.toColorObjRGB(mcColor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.mapColor = new Color(0,0,0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//====================//
|
|
||||||
// LodBuilder methods //
|
|
||||||
//====================//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
|
|
||||||
* This way the method won't accidentally be called before the deserialization can be completed.
|
|
||||||
*/
|
|
||||||
public static HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// use the cached version if possible
|
|
||||||
if (rendererIgnoredBlocks != null)
|
|
||||||
{
|
|
||||||
return rendererIgnoredBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
|
||||||
baseIgnoredBlock.add(AIR_STRING);
|
|
||||||
rendererIgnoredBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
|
|
||||||
return rendererIgnoredBlocks;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
|
|
||||||
* This way the method won't accidentally be called before the deserialization can be completed.
|
|
||||||
*/
|
|
||||||
public static HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// use the cached version if possible
|
|
||||||
if (rendererIgnoredCaveBlocks != null)
|
|
||||||
{
|
|
||||||
return rendererIgnoredCaveBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
|
||||||
baseIgnoredBlock.add(AIR_STRING);
|
|
||||||
rendererIgnoredCaveBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
|
|
||||||
return rendererIgnoredCaveBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; }
|
|
||||||
public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// lod builder helpers //
|
|
||||||
|
|
||||||
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// get the base blocks
|
|
||||||
HashSet<String> blockStringList = new HashSet<>();
|
|
||||||
if (baseResourceLocations != null)
|
|
||||||
{
|
|
||||||
blockStringList.addAll(baseResourceLocations);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the config blocks
|
|
||||||
String ignoreBlockCsv = config.get();
|
|
||||||
if (ignoreBlockCsv != null)
|
|
||||||
{
|
|
||||||
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
|
|
||||||
}
|
|
||||||
|
|
||||||
return getAllBlockWrappers(blockStringList, levelWrapper);
|
|
||||||
}
|
|
||||||
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// deserialize each of the given resource locations
|
|
||||||
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
|
|
||||||
for (String blockResourceLocation : blockResourceLocationSet)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (blockResourceLocation == null)
|
|
||||||
{
|
|
||||||
// shouldn't happen, but just in case
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String cleanedResourceLocation = blockResourceLocation.trim();
|
|
||||||
if (cleanedResourceLocation.length() == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BlockStateWrapper defaultBlockStateToIgnore = (BlockStateWrapper) deserialize(cleanedResourceLocation, levelWrapper);
|
|
||||||
blockStateWrappers.add(defaultBlockStateToIgnore);
|
|
||||||
|
|
||||||
if (defaultBlockStateToIgnore != AIR)
|
|
||||||
{
|
|
||||||
// add all possible blockstates (to account for light blocks with different light values and such)
|
|
||||||
List<BlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
|
|
||||||
for (BlockState blockState : blockStatesToIgnore)
|
|
||||||
{
|
|
||||||
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
|
|
||||||
blockStateWrappers.add(newBlockToIgnore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// air is a special case so it must be handled separately
|
|
||||||
blockStateWrappers.add(AIR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Unable to deserialize block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Unexpected error deserializing block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return blockStateWrappers;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// wrapper methods //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOpacity() { return this.opacity; }
|
|
||||||
private int calculateOpacity()
|
|
||||||
{
|
|
||||||
// get block properties (defaults to the values used by air)
|
|
||||||
boolean canOcclude = this.getCanOcclude();
|
|
||||||
boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this method isn't perfect, but works well enough for our use case
|
|
||||||
int opacity;
|
|
||||||
if (this.isAir())
|
|
||||||
{
|
|
||||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
|
||||||
}
|
|
||||||
else if (this.isLiquid() && !canOcclude)
|
|
||||||
{
|
|
||||||
// probably not a waterlogged block (which should block light entirely)
|
|
||||||
|
|
||||||
// +1 to indicate that the block is translucent (in between transparent and opaque)
|
|
||||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
|
|
||||||
}
|
|
||||||
else if (propagatesSkyLightDown && !canOcclude)
|
|
||||||
{
|
|
||||||
// probably glass or some other fully transparent block
|
|
||||||
|
|
||||||
// !canOcclude is required to ignore stairs and slabs since
|
|
||||||
// propagateSkyLightDown is true for them, but they're solid and don't actually let light through
|
|
||||||
|
|
||||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// default for all other blocks
|
|
||||||
opacity = LodUtil.BLOCK_FULLY_OPAQUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return opacity;
|
|
||||||
}
|
|
||||||
private boolean getCanOcclude()
|
|
||||||
{
|
|
||||||
// defaults to the value used by air
|
|
||||||
boolean canOcclude = false;
|
|
||||||
if (this.blockState != null)
|
|
||||||
{
|
|
||||||
canOcclude = this.blockState.canOcclude();
|
|
||||||
}
|
|
||||||
|
|
||||||
return canOcclude;
|
|
||||||
}
|
|
||||||
private boolean getPropagatesSkyLightDown()
|
|
||||||
{
|
|
||||||
// defaults to the value used by air
|
|
||||||
boolean propagatesSkyLightDown = true;
|
|
||||||
if (this.blockState != null)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
propagatesSkyLightDown = this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
|
||||||
#else
|
|
||||||
propagatesSkyLightDown = this.blockState.propagatesSkylightDown();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return propagatesSkyLightDown;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSerialString() { return this.serialString; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj)
|
|
||||||
{
|
|
||||||
if (this == obj)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj == null || this.getClass() != obj.getClass())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockStateWrapper that = (BlockStateWrapper) obj;
|
|
||||||
// the serialized value is used so we can test the contents instead of the references
|
|
||||||
return Objects.equals(this.getSerialString(), that.getSerialString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() { return this.hashCode; }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getWrappedMcObject() { return this.blockState; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAir() { return this.isAir(this.blockState); }
|
|
||||||
public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSolid()
|
|
||||||
{
|
|
||||||
if (this.isAir())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
return this.blockState.getMaterial().isSolid();
|
|
||||||
#else
|
|
||||||
return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLiquid()
|
|
||||||
{
|
|
||||||
if (this.isAir())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
return this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty();
|
|
||||||
#else
|
|
||||||
return !this.blockState.getFluidState().isEmpty();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBeaconBlock() { return this.isBeaconBlock; }
|
|
||||||
@Override
|
|
||||||
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
|
|
||||||
@Override
|
|
||||||
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
|
|
||||||
@Override
|
|
||||||
public boolean allowsBeaconBeamPassage() { return this.allowsBeaconBeamPassage; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getMapColor() { return this.mapColor; }
|
|
||||||
@Override
|
|
||||||
public Color getBeaconTintColor() { return this.beaconTintColor; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getMaterialId() { return this.blockMaterialId; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return this.getSerialString(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======================//
|
|
||||||
// serialization methods //
|
|
||||||
//=======================//
|
|
||||||
|
|
||||||
private String serialize(ILevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
if (this.blockState == null)
|
|
||||||
{
|
|
||||||
return AIR_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// older versions of MC have a static registry
|
|
||||||
#if MC_VER > MC_1_17_1
|
|
||||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
|
||||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_11
|
|
||||||
ResourceLocation resourceLocation;
|
|
||||||
#else
|
|
||||||
Identifier resourceLocation;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
|
|
||||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
|
||||||
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
|
|
||||||
#else
|
|
||||||
resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (resourceLocation == null)
|
|
||||||
{
|
|
||||||
LOGGER.warn("No ResourceLocation found, unable to serialize: " + this.blockState);
|
|
||||||
return AIR_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.serialString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath()
|
|
||||||
+ STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState);
|
|
||||||
|
|
||||||
return this.serialString;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** will only work if a level is currently loaded */
|
|
||||||
public static IBlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException
|
|
||||||
{
|
|
||||||
// we need the final string for the concurrent hash map later
|
|
||||||
final String finalResourceStateString = resourceStateString;
|
|
||||||
|
|
||||||
if (finalResourceStateString.equals(AIR_STRING)
|
|
||||||
|| finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
|
|
||||||
{
|
|
||||||
return AIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to use the existing wrapper
|
|
||||||
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
|
|
||||||
{
|
|
||||||
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if no wrapper is found, default to air
|
|
||||||
BlockStateWrapper foundWrapper = AIR;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// try to parse out the BlockState
|
|
||||||
String blockStatePropertiesString = null; // will be null if no properties were included
|
|
||||||
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
|
|
||||||
if (stateSeparatorIndex != -1)
|
|
||||||
{
|
|
||||||
// blockstate properties found
|
|
||||||
blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
|
|
||||||
resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the resource location
|
|
||||||
int separatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
|
|
||||||
if (separatorIndex == -1)
|
|
||||||
{
|
|
||||||
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_11
|
|
||||||
ResourceLocation resourceLocation;
|
|
||||||
#else
|
|
||||||
Identifier resourceLocation;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_1
|
|
||||||
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
|
||||||
#else
|
|
||||||
resourceLocation = Identifier.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new IOException("No Resource Location found for the string: [" + resourceStateString + "] Error: [" + e.getMessage() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// attempt to get the BlockState from all possible BlockStates
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
#if MC_VER > MC_1_17_1
|
|
||||||
LodUtil.assertTrue(levelWrapper != null && levelWrapper.getWrappedMcObject() != null);
|
|
||||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Block block;
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
block = Registry.BLOCK.get(resourceLocation);
|
|
||||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
|
||||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
|
||||||
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
|
||||||
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
|
|
||||||
#else
|
|
||||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
|
||||||
Optional<Holder.Reference<Block>> optionalBlockHolder = registryAccess.lookupOrThrow(Registries.BLOCK).get(resourceLocation);
|
|
||||||
block = optionalBlockHolder.isPresent() ? optionalBlockHolder.get().value() : null;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
// shouldn't normally happen, but here to make the compiler happy
|
|
||||||
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
|
||||||
{
|
|
||||||
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
|
||||||
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return AIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// attempt to find the blockstate from all possibilities
|
|
||||||
BlockState foundState = null;
|
|
||||||
if (blockStatePropertiesString != null)
|
|
||||||
{
|
|
||||||
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
|
|
||||||
for (BlockState possibleState : possibleStateList)
|
|
||||||
{
|
|
||||||
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
|
|
||||||
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
|
|
||||||
{
|
|
||||||
foundState = possibleState;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the default if no state was found or given
|
|
||||||
if (foundState == null)
|
|
||||||
{
|
|
||||||
if (blockStatePropertiesString != null)
|
|
||||||
{
|
|
||||||
// we should have found a blockstate, but didn't
|
|
||||||
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
|
||||||
{
|
|
||||||
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
|
||||||
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foundState = block.defaultBlockState();
|
|
||||||
}
|
|
||||||
|
|
||||||
foundWrapper = new BlockStateWrapper(foundState, levelWrapper);
|
|
||||||
return foundWrapper;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// put if absent in case two threads deserialize at the same time
|
|
||||||
// unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions
|
|
||||||
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** used to compare and save BlockStates based on their properties */
|
|
||||||
private static String serializeBlockStateProperties(BlockState blockState)
|
|
||||||
{
|
|
||||||
// get the property list for this block (doesn't contain this block state's values, just the names and possible values)
|
|
||||||
java.util.Collection<net.minecraft.world.level.block.state.properties.Property<?>> blockPropertyCollection = blockState.getProperties();
|
|
||||||
|
|
||||||
// alphabetically sort the list so they are always in the same order
|
|
||||||
List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection);
|
|
||||||
sortedBlockPropteryList.sort((a, b) -> a.getName().compareTo(b.getName()));
|
|
||||||
|
|
||||||
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
for (net.minecraft.world.level.block.state.properties.Property<?> property : sortedBlockPropteryList)
|
|
||||||
{
|
|
||||||
String propertyName = property.getName();
|
|
||||||
|
|
||||||
String value = "NULL";
|
|
||||||
if (blockState.hasProperty(property))
|
|
||||||
{
|
|
||||||
value = blockState.getValue(property).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
stringBuilder.append("{");
|
|
||||||
stringBuilder.append(propertyName).append(RESOURCE_LOCATION_SEPARATOR).append(value);
|
|
||||||
stringBuilder.append("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringBuilder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// Iris methods //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
private EDhApiBlockMaterial calculateEDhApiBlockMaterialId()
|
|
||||||
{
|
|
||||||
if (this.blockState == null)
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.AIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
String serialString = this.getSerialString().toLowerCase();
|
|
||||||
|
|
||||||
if (this.blockState.is(BlockTags.LEAVES)
|
|
||||||
|| serialString.contains("bamboo")
|
|
||||||
|| serialString.contains("cactus")
|
|
||||||
|| serialString.contains("chorus_flower")
|
|
||||||
|| serialString.contains("mushroom")
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.LEAVES;
|
|
||||||
}
|
|
||||||
else if (this.blockState.is(Blocks.LAVA))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.LAVA;
|
|
||||||
}
|
|
||||||
else if (this.isLiquid() || this.blockState.is(Blocks.WATER))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.WATER;
|
|
||||||
}
|
|
||||||
else if (this.blockState.getSoundType() == SoundType.WOOD
|
|
||||||
|| serialString.contains("root")
|
|
||||||
#if MC_VER >= MC_1_19_4
|
|
||||||
|| this.blockState.getSoundType() == SoundType.CHERRY_WOOD
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.WOOD;
|
|
||||||
}
|
|
||||||
else if (this.blockState.getSoundType() == SoundType.METAL
|
|
||||||
#if MC_VER >= MC_1_19_2
|
|
||||||
|| this.blockState.getSoundType() == SoundType.COPPER
|
|
||||||
#endif
|
|
||||||
#if MC_VER >= MC_1_20_4
|
|
||||||
|| this.blockState.getSoundType() == SoundType.COPPER_BULB
|
|
||||||
|| this.blockState.getSoundType() == SoundType.COPPER_GRATE
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.METAL;
|
|
||||||
}
|
|
||||||
else if (serialString.contains("grass_block"))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.GRASS;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
serialString.contains("dirt")
|
|
||||||
|| serialString.contains("gravel")
|
|
||||||
|| serialString.contains("mud")
|
|
||||||
|| serialString.contains("podzol")
|
|
||||||
|| serialString.contains("mycelium")
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.DIRT;
|
|
||||||
}
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
else if (this.blockState.getSoundType() == SoundType.DEEPSLATE
|
|
||||||
|| this.blockState.getSoundType() == SoundType.DEEPSLATE_BRICKS
|
|
||||||
|| this.blockState.getSoundType() == SoundType.DEEPSLATE_TILES
|
|
||||||
|| this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE
|
|
||||||
|| serialString.contains("deepslate") )
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.DEEPSLATE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else if (this.serialString.contains("snow"))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.SNOW;
|
|
||||||
}
|
|
||||||
else if (serialString.contains("sand"))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.SAND;
|
|
||||||
}
|
|
||||||
else if (serialString.contains("terracotta"))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.TERRACOTTA;
|
|
||||||
}
|
|
||||||
else if (this.blockState.is(BlockTags.BASE_STONE_NETHER))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.NETHER_STONE;
|
|
||||||
}
|
|
||||||
else if (serialString.contains("stone")
|
|
||||||
|| serialString.contains("ore"))
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.STONE;
|
|
||||||
}
|
|
||||||
else if (this.blockState.getLightEmission() > 0)
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.ILLUMINATED;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return EDhApiBlockMaterial.UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-601
@@ -1,601 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.block;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
|
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.level.block.*;
|
|
||||||
#if MC_VER >= MC_1_19_2
|
|
||||||
import net.minecraft.util.RandomSource;
|
|
||||||
#else
|
|
||||||
import java.util.Random;
|
|
||||||
#endif
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import net.minecraft.world.level.block.state.properties.SlabType;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
#else
|
|
||||||
import net.minecraft.client.renderer.block.model.BlockModelPart;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This stores and calculates the colors
|
|
||||||
* the given {@link BlockState} should have based
|
|
||||||
* on the given {@link IClientLevelWrapper}.
|
|
||||||
*
|
|
||||||
* @see ColorUtil
|
|
||||||
*/
|
|
||||||
public class ClientBlockStateColorCache
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
private static final Minecraft MC = Minecraft.getInstance();
|
|
||||||
|
|
||||||
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
|
|
||||||
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Methods using MC's "RandomSource" object aren't thread safe <br>
|
|
||||||
* so we need to put locks around that logic. <br>
|
|
||||||
* specifically:
|
|
||||||
* <code>
|
|
||||||
* getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM)
|
|
||||||
* </code>
|
|
||||||
*/
|
|
||||||
private static final ReentrantLock RESOLVE_LOCK = new ReentrantLock();
|
|
||||||
|
|
||||||
|
|
||||||
/** This is the order each direction on a block is processed when attempting to get the texture/color */
|
|
||||||
private static final @Nullable Direction[] COLOR_RESOLUTION_DIRECTION_ORDER =
|
|
||||||
{
|
|
||||||
Direction.UP,
|
|
||||||
null, // null represents "unculled" faces, IE the top of farmland
|
|
||||||
Direction.NORTH,
|
|
||||||
Direction.EAST,
|
|
||||||
Direction.WEST,
|
|
||||||
Direction.SOUTH,
|
|
||||||
Direction.DOWN
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final int FLOWER_COLOR_SCALE = 5;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
private static final Random RANDOM = new Random(0);
|
|
||||||
#else
|
|
||||||
/** Note: this object isn't thread safe and must be put in a lock */
|
|
||||||
private static final RandomSource RANDOM = RandomSource.create();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private final IClientLevelWrapper clientLevelWrapper;
|
|
||||||
private final BlockState blockState;
|
|
||||||
private final BlockStateWrapper blockStateWrapper;
|
|
||||||
|
|
||||||
private boolean isColorResolved = false;
|
|
||||||
private int baseColor = 0;
|
|
||||||
private boolean needPostTinting = false;
|
|
||||||
private int tintIndex = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
|
||||||
// constants //
|
|
||||||
//===========//
|
|
||||||
|
|
||||||
private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13)
|
|
||||||
private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
|
|
||||||
private static final float MIN_SRGB_BOUND = Float.intBitsToFloat(MIN_SRGB_BITS);
|
|
||||||
private static final float MAX_SRGB_BOUND = Float.intBitsToFloat(MAX_SRGB_BITS);
|
|
||||||
|
|
||||||
private static final int[] linearToSrgbTable = new int[]
|
|
||||||
{
|
|
||||||
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
|
|
||||||
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
|
|
||||||
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
|
|
||||||
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
|
|
||||||
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
|
|
||||||
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
|
|
||||||
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
|
|
||||||
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
|
|
||||||
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
|
|
||||||
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
|
|
||||||
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
|
|
||||||
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
|
|
||||||
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final float[] srgbToLinearTable = new float[]
|
|
||||||
{
|
|
||||||
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
|
|
||||||
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
|
|
||||||
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
|
|
||||||
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
|
|
||||||
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
|
|
||||||
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
|
|
||||||
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
|
|
||||||
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
|
|
||||||
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
|
|
||||||
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
|
|
||||||
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
|
|
||||||
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
|
|
||||||
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
|
|
||||||
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
|
|
||||||
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
|
|
||||||
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
|
|
||||||
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
|
|
||||||
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
|
|
||||||
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
|
|
||||||
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
|
|
||||||
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
|
|
||||||
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
|
|
||||||
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
|
|
||||||
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
|
|
||||||
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
|
|
||||||
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
|
|
||||||
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
|
|
||||||
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
|
|
||||||
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
|
|
||||||
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
|
|
||||||
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
|
|
||||||
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final ThreadLocal<TintWithoutLevelOverrider> TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(() -> new TintWithoutLevelOverrider());
|
|
||||||
private static final ThreadLocal<TintGetterOverride> TintOverrideGetter = ThreadLocal.withInitial(() -> new TintGetterOverride());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper)
|
|
||||||
{
|
|
||||||
this.blockState = blockState;
|
|
||||||
this.blockStateWrapper = BlockStateWrapper.fromBlockState(blockState, clientLevelWrapper);
|
|
||||||
this.clientLevelWrapper = clientLevelWrapper;
|
|
||||||
|
|
||||||
this.resolveColors();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
|
||||||
// color calculation //
|
|
||||||
//===================//
|
|
||||||
|
|
||||||
private void resolveColors()
|
|
||||||
{
|
|
||||||
if (this.isColorResolved)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// getQuads() isn't thread safe so we need to put this logic in a lock
|
|
||||||
RESOLVE_LOCK.lock();
|
|
||||||
|
|
||||||
if (this.blockState.getFluidState().isEmpty())
|
|
||||||
{
|
|
||||||
// look for the first non-empty direction
|
|
||||||
List<BakedQuad> quads = null;
|
|
||||||
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
|
|
||||||
{
|
|
||||||
quads = this.getQuadsForDirection(direction);
|
|
||||||
if (quads != null && !quads.isEmpty()
|
|
||||||
&& !(
|
|
||||||
this.blockState.getBlock() instanceof RotatedPillarBlock
|
|
||||||
&& direction == Direction.UP
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quads == null || quads.isEmpty())
|
|
||||||
{
|
|
||||||
quads = this.getUnculledQuads();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quads != null
|
|
||||||
&& !quads.isEmpty()
|
|
||||||
&& quads.get(0) != null)
|
|
||||||
{
|
|
||||||
BakedQuad firstQuad = quads.get(0);
|
|
||||||
|
|
||||||
this.needPostTinting = firstQuad.isTinted();
|
|
||||||
#if MC_VER <= MC_1_21_4
|
|
||||||
this.tintIndex = firstQuad.getTintIndex();
|
|
||||||
#else
|
|
||||||
this.tintIndex = firstQuad.tintIndex();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
this.baseColor = calculateColorFromTexture(
|
|
||||||
firstQuad.sprite,
|
|
||||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
|
||||||
#elif MC_VER < MC_1_21_5
|
|
||||||
this.baseColor = calculateColorFromTexture(
|
|
||||||
firstQuad.getSprite(),
|
|
||||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
|
||||||
#else
|
|
||||||
this.baseColor = calculateColorFromTexture(
|
|
||||||
firstQuad.sprite(),
|
|
||||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Backup method.
|
|
||||||
this.needPostTinting = false;
|
|
||||||
this.tintIndex = 0;
|
|
||||||
this.baseColor = this.getParticleIconColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Liquid Block
|
|
||||||
this.needPostTinting = true;
|
|
||||||
this.tintIndex = 0;
|
|
||||||
this.baseColor = this.getParticleIconColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.isColorResolved = true;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
RESOLVE_LOCK.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private List<BakedQuad> getUnculledQuads() { return this.getQuadsForDirection(null); }
|
|
||||||
@Nullable
|
|
||||||
private List<BakedQuad> getQuadsForDirection(@Nullable Direction direction)
|
|
||||||
{
|
|
||||||
BlockState effectiveBlockState = this.blockState;
|
|
||||||
|
|
||||||
// if this block is a slab, use it's double variant so we can get the top face,
|
|
||||||
// otherwise the color will use the side, which isn't as accurate
|
|
||||||
if (this.blockState.getBlock() instanceof SlabBlock)
|
|
||||||
{
|
|
||||||
effectiveBlockState = this.blockState.setValue( SlabBlock.TYPE, SlabType.DOUBLE );
|
|
||||||
}
|
|
||||||
|
|
||||||
List<BakedQuad> quads;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
quads = MC.getModelManager().getBlockModelShaper().
|
|
||||||
getBlockModel(effectiveBlockState).getQuads(effectiveBlockState, direction, RANDOM);
|
|
||||||
#else
|
|
||||||
List<BlockModelPart> blockModelPartList = MC.getModelManager().getBlockModelShaper().
|
|
||||||
getBlockModel(effectiveBlockState).collectParts(RANDOM);
|
|
||||||
|
|
||||||
quads = new ArrayList<>();
|
|
||||||
if (blockModelPartList != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < blockModelPartList.size(); i++)
|
|
||||||
{
|
|
||||||
// if direction is null this will return the unculled quads
|
|
||||||
quads.addAll(blockModelPartList.get(i).getQuads(direction));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return quads;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Perhaps make this not just use the first frame?
|
|
||||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, EColorMode colorMode)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
int alpha = 0;
|
|
||||||
double red = 0;
|
|
||||||
double green = 0;
|
|
||||||
double blue = 0;
|
|
||||||
int tempColor;
|
|
||||||
|
|
||||||
// don't render Chiseled blocks.
|
|
||||||
// Since EColorMode is set per block, you only need to check this once.
|
|
||||||
if (colorMode != EColorMode.Chisel)
|
|
||||||
{
|
|
||||||
// textures normally use u and v instead of x and y
|
|
||||||
for (int v = 0; v < getTextureHeight(texture); v++)
|
|
||||||
{
|
|
||||||
for (int u = 0; u < getTextureWidth(texture); u++)
|
|
||||||
{
|
|
||||||
//note: Minecraft color format is: 0xAA BB GG RR
|
|
||||||
//________ DH mod color format is: 0xAA RR GG BB
|
|
||||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
|
||||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
|
||||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
|
|
||||||
|
|
||||||
int r = (tempColor & 0x000000FF);
|
|
||||||
int g = (tempColor & 0x0000FF00) >>> 8;
|
|
||||||
int b = (tempColor & 0x00FF0000) >>> 16;
|
|
||||||
int a = (tempColor & 0xFF000000) >>> 24;
|
|
||||||
int scale = 1;
|
|
||||||
if (colorMode == EColorMode.Leaves)
|
|
||||||
{
|
|
||||||
//switch (//FIXME add config option)
|
|
||||||
// case BLACK:
|
|
||||||
// a = 255; //simulate black background of fast leaves
|
|
||||||
// break;
|
|
||||||
// case IGNORE:
|
|
||||||
if (a == 0) {
|
|
||||||
continue; //same long grass
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
a = 255; //just in case there are semi transparent pixels
|
|
||||||
}
|
|
||||||
// break;
|
|
||||||
// case TRANSPARENT:
|
|
||||||
// break; //do nothing, let it count towards transparency
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (a == 0 && colorMode != EColorMode.Glass)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (colorMode == EColorMode.Flower && (g + 25 < b || g + 25 < r))
|
|
||||||
{
|
|
||||||
scale = FLOWER_COLOR_SCALE;
|
|
||||||
}
|
|
||||||
count += scale;
|
|
||||||
//apparently alpha is linear
|
|
||||||
alpha += a * scale;
|
|
||||||
//gamma correction is complicated
|
|
||||||
red += srgbToLinearTable[r] * a * scale;
|
|
||||||
green += srgbToLinearTable[g] * a * scale;
|
|
||||||
blue += srgbToLinearTable[b] * a * scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
// this block is entirely transparent
|
|
||||||
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// determine the average color
|
|
||||||
tempColor = ColorUtil.argbToInt(
|
|
||||||
alpha / count,
|
|
||||||
linearToSrgb((float) (red / (double) alpha)),
|
|
||||||
linearToSrgb((float) (green / (double) alpha)),
|
|
||||||
linearToSrgb((float) (blue / (double) alpha)));
|
|
||||||
}
|
|
||||||
|
|
||||||
//check if not missing texture
|
|
||||||
if (tempColor == ColorUtil.argbToInt(255, 182, 0, 182))
|
|
||||||
{
|
|
||||||
//make it not render at all
|
|
||||||
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
|
|
||||||
}
|
|
||||||
return tempColor;
|
|
||||||
}
|
|
||||||
private static int getTextureWidth(TextureAtlasSprite texture)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
return texture.getWidth();
|
|
||||||
#else
|
|
||||||
return texture.contents().width();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
private static int getTextureHeight(TextureAtlasSprite texture)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
return texture.getHeight();
|
|
||||||
#else
|
|
||||||
return texture.contents().height();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This method was suggested by IMS from the Iris/Sodium team.
|
|
||||||
* That's where the numbers and code are based.
|
|
||||||
*/
|
|
||||||
private static int linearToSrgb(float color)
|
|
||||||
{
|
|
||||||
if (!(color > MIN_SRGB_BOUND))
|
|
||||||
{
|
|
||||||
color = MIN_SRGB_BOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (color > MAX_SRGB_BOUND)
|
|
||||||
{
|
|
||||||
color = MAX_SRGB_BOUND;
|
|
||||||
}
|
|
||||||
int inputBits = Float.floatToRawIntBits(color);
|
|
||||||
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
|
|
||||||
|
|
||||||
int bias = (entry >>> 16) << 9;
|
|
||||||
int scale = entry & 0xffff;
|
|
||||||
int t = (inputBits >>> 12) & 0xff;
|
|
||||||
|
|
||||||
return (bias + (scale * t)) >>> 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getParticleIconColor()
|
|
||||||
{
|
|
||||||
return calculateColorFromTexture(
|
|
||||||
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
|
|
||||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===============//
|
|
||||||
// public getter //
|
|
||||||
//===============//
|
|
||||||
|
|
||||||
public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos)
|
|
||||||
{
|
|
||||||
// only get the tint if the block needs to be tinted
|
|
||||||
if (!this.needPostTinting)
|
|
||||||
{
|
|
||||||
return this.baseColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't try tinting blocks that don't support our method of tint getting
|
|
||||||
if (BROKEN_BLOCK_STATES.contains(this.blockState))
|
|
||||||
{
|
|
||||||
return this.baseColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// attempt to get the tint
|
|
||||||
int tintColor = -1;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// try to use the fast tint getter logic first
|
|
||||||
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get();
|
|
||||||
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
|
|
||||||
|
|
||||||
// try using DH's cached tint values first if possible
|
|
||||||
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
|
|
||||||
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
|
|
||||||
{
|
|
||||||
// one or more tint values weren't calculated,
|
|
||||||
// we need MC's color resolver
|
|
||||||
tintColor = Minecraft.getInstance()
|
|
||||||
.getBlockColors()
|
|
||||||
.getColor(this.blockState,
|
|
||||||
tintOverride,
|
|
||||||
McObjectConverter.Convert(blockPos),
|
|
||||||
this.tintIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (UnsupportedOperationException e)
|
|
||||||
{
|
|
||||||
// this exception generally occurs if the tint requires other blocks besides itself
|
|
||||||
LOGGER.debug("Unable to use ["+ TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
|
|
||||||
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the level logic only if requested
|
|
||||||
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
|
||||||
{
|
|
||||||
// the level shouldn't be used all the time due to it breaking some blocks tinting
|
|
||||||
// specifically oceans don't render correctly
|
|
||||||
|
|
||||||
TintGetterOverride tintOverride = TintOverrideGetter.get();
|
|
||||||
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
|
|
||||||
|
|
||||||
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
|
|
||||||
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
|
|
||||||
{
|
|
||||||
tintColor = Minecraft.getInstance()
|
|
||||||
.getBlockColors()
|
|
||||||
.getColor(this.blockState,
|
|
||||||
tintOverride,
|
|
||||||
McObjectConverter.Convert(blockPos),
|
|
||||||
this.tintIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// only display the error once per block/biome type to reduce log spam
|
|
||||||
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
|
|
||||||
{
|
|
||||||
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
|
|
||||||
BROKEN_BLOCK_STATES.add(this.blockState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (tintColor != -1)
|
|
||||||
{
|
|
||||||
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// unable to get the tinted color, use the base color instead
|
|
||||||
return this.baseColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper classes //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
private enum EColorMode
|
|
||||||
{
|
|
||||||
Default,
|
|
||||||
Flower,
|
|
||||||
Leaves,
|
|
||||||
Chisel,
|
|
||||||
Glass;
|
|
||||||
|
|
||||||
static EColorMode getColorMode(Block block)
|
|
||||||
{
|
|
||||||
if (block instanceof LeavesBlock)
|
|
||||||
{
|
|
||||||
return Leaves;
|
|
||||||
}
|
|
||||||
if (block instanceof FlowerBlock)
|
|
||||||
{
|
|
||||||
return Flower;
|
|
||||||
}
|
|
||||||
if (block.toString().contains("glass"))
|
|
||||||
{
|
|
||||||
return Glass;
|
|
||||||
}
|
|
||||||
if (block.toString().equals("Block{chiselsandbits:chiseled}"))
|
|
||||||
{
|
|
||||||
return Chisel;
|
|
||||||
}
|
|
||||||
return Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-78
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.block;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
#else
|
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
|
||||||
import net.minecraft.client.renderer.texture.SpriteContents;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For wrapping/utilizing around TextureAtlasSprite
|
|
||||||
*
|
|
||||||
* @author Ran
|
|
||||||
*/
|
|
||||||
public class TextureAtlasSpriteWrapper
|
|
||||||
{
|
|
||||||
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
return sprite.mainImage[0].getPixelRGBA(
|
|
||||||
x + sprite.framesX[frameIndex] * sprite.getWidth(),
|
|
||||||
y + sprite.framesY[frameIndex] * sprite.getHeight());
|
|
||||||
#elif MC_VER < MC_1_19_4
|
|
||||||
if (sprite.animatedTexture != null)
|
|
||||||
{
|
|
||||||
x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width;
|
|
||||||
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
|
|
||||||
}
|
|
||||||
return sprite.mainImage[0].getPixelRGBA(x, y);
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
if (sprite.contents().animatedTexture != null)
|
|
||||||
{
|
|
||||||
x += sprite.contents().animatedTexture.getFrameX(frameIndex) * sprite.contents().width();
|
|
||||||
y += sprite.contents().animatedTexture.getFrameY(frameIndex) * sprite.contents().width();
|
|
||||||
}
|
|
||||||
return sprite.contents().originalImage.getPixelRGBA(x, y);
|
|
||||||
#else
|
|
||||||
|
|
||||||
SpriteContents content = sprite.contents(); // don't close, otherwise MC will be corrupted and you won't be able to re-access the texture
|
|
||||||
if (content.animatedTexture != null)
|
|
||||||
{
|
|
||||||
x += content.animatedTexture.getFrameX(frameIndex) * content.width();
|
|
||||||
y += content.animatedTexture.getFrameY(frameIndex) * content.width();
|
|
||||||
}
|
|
||||||
|
|
||||||
int abgr = content.originalImage.getPixel(x, y);
|
|
||||||
// re-pack the color so we can access it normally
|
|
||||||
int a = (abgr & 0xFF000000) >>> 24;
|
|
||||||
int b = (abgr & 0x00FF0000) >>> 16;
|
|
||||||
int g = (abgr & 0x0000FF00) >>> 8;
|
|
||||||
int r = (abgr & 0x000000FF);
|
|
||||||
return ColorUtil.argbToInt(a, r, g, b);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-182
@@ -1,182 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.block;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.level.*;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
|
||||||
import net.minecraft.world.level.material.FluidState;
|
|
||||||
import net.minecraft.world.phys.AABB;
|
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class TintGetterOverride extends AbstractDhTintGetter
|
|
||||||
{
|
|
||||||
private LevelReader parent;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public TintGetterOverride() { }
|
|
||||||
|
|
||||||
public void update(BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
|
|
||||||
{
|
|
||||||
super.update(biomeWrapper, blockStateWrapper, fullDataSource, clientLevelWrapper);
|
|
||||||
this.parent = (LevelReader)this.clientLevelWrapper.getWrappedMcObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// methods //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
@Override
|
|
||||||
public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); }
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockHitResult clip(ClipContext clipContext) { return this.parent.clip(clipContext); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
|
|
||||||
{
|
|
||||||
return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) { return this.parent.getBlockFloorHeight(voxelShape, supplier); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
@Override
|
|
||||||
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public int getMaxY() { return this.parent.getMaxY(); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
@Override
|
|
||||||
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) { return this.parent.getBlockEntity(blockPos, blockEntityType); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) { return this.parent.isBlockInLine(clipBlockStateContext); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeight() { return this.parent.getHeight(); }
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
@Override
|
|
||||||
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public int getMinY() { return this.parent.getMinY(); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSectionsCount() { return this.parent.getSectionsCount(); }
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
@Override
|
|
||||||
public int getMinSection() { return this.parent.getMinSection(); }
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public int getMinSectionY() { return super.getMinSectionY(); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
@Override
|
|
||||||
public int getMaxSection() { return this.parent.getMaxSection(); }
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-90
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.block;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.level.LevelReader;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
|
||||||
import net.minecraft.world.level.material.FluidState;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class TintWithoutLevelOverrider extends AbstractDhTintGetter
|
|
||||||
{
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public TintWithoutLevelOverrider()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// methods //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getShade(Direction direction, boolean shade)
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
|
||||||
@Override
|
|
||||||
public LevelLightEngine getLightEngine()
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public BlockEntity getBlockEntity(BlockPos pos)
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getBlockState(BlockPos pos)
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
|
||||||
@Override
|
|
||||||
public FluidState getFluidState(BlockPos pos)
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// post MC 1.17 //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeight()
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
@Override
|
|
||||||
public int getMinBuildHeight()
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public int getMinY()
|
|
||||||
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
-641
@@ -1,641 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.chunk;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.DhApi;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
import net.minecraft.core.QuartPos;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_17_1
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_18_2
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_19_2 || MC_VER == MC_1_19_4
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_20_1
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_4
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
public class ChunkWrapper implements IChunkWrapper
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
|
|
||||||
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
|
|
||||||
private static final ThreadLocal<MutableBlockPosWrapper> MUTABLE_BLOCK_POS_WRAPPER_REF = ThreadLocal.withInitial(() -> new MutableBlockPosWrapper());
|
|
||||||
|
|
||||||
private static boolean heightmapThreadWarningLogged = false;
|
|
||||||
|
|
||||||
|
|
||||||
private final ChunkAccess chunk;
|
|
||||||
private final DhChunkPos chunkPos;
|
|
||||||
private final ILevelWrapper wrappedLevel;
|
|
||||||
|
|
||||||
private boolean isDhBlockLightCorrect = false;
|
|
||||||
private boolean isDhSkyLightCorrect = false;
|
|
||||||
|
|
||||||
private ChunkLightStorage blockLightStorage;
|
|
||||||
private ChunkLightStorage skyLightStorage;
|
|
||||||
|
|
||||||
private ArrayList<DhBlockPos> blockLightPosList = null;
|
|
||||||
|
|
||||||
private int minNonEmptyHeight = Integer.MIN_VALUE;
|
|
||||||
private int maxNonEmptyHeight = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
/** will be null if we are using MC heightmaps */
|
|
||||||
private int[][] solidHeightMap = null;
|
|
||||||
/** will be null if we are using MC heightmaps */
|
|
||||||
private int[][] lightBlockingHeightMap = null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: this constructor should be very
|
|
||||||
* fast since it will be called frequently on the MC
|
|
||||||
* server thread and a slow method will cause server lag.
|
|
||||||
*/
|
|
||||||
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
|
|
||||||
{
|
|
||||||
this.chunk = chunk;
|
|
||||||
this.wrappedLevel = wrappedLevel;
|
|
||||||
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkWrapper copy() { return new ChunkWrapper(this.chunk, this.wrappedLevel); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// getters //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeight() { return getHeight(this.chunk); }
|
|
||||||
public static int getHeight(ChunkAccess chunk)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
return 255;
|
|
||||||
#else
|
|
||||||
return chunk.getHeight();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getInclusiveMinBuildHeight() { return getInclusiveMinBuildHeight(this.chunk); }
|
|
||||||
public static int getInclusiveMinBuildHeight(ChunkAccess chunk)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
return 0;
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
return chunk.getMinBuildHeight();
|
|
||||||
#else
|
|
||||||
return chunk.getMinY();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getExclusiveMaxBuildHeight() { return getExclusiveMaxBuildHeight(this.chunk); }
|
|
||||||
public static int getExclusiveMaxBuildHeight(ChunkAccess chunk)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
return chunk.getMaxBuildHeight();
|
|
||||||
#else
|
|
||||||
// +1 since Minecraft made the max value inclusive
|
|
||||||
return chunk.getMaxY() + 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinNonEmptyHeight()
|
|
||||||
{
|
|
||||||
if (this.minNonEmptyHeight != Integer.MIN_VALUE)
|
|
||||||
{
|
|
||||||
return this.minNonEmptyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// default if every section is empty or missing
|
|
||||||
this.minNonEmptyHeight = this.getInclusiveMinBuildHeight();
|
|
||||||
|
|
||||||
// determine the lowest empty section (bottom up)
|
|
||||||
LevelChunkSection[] sections = this.chunk.getSections();
|
|
||||||
for (int index = 0; index < sections.length; index++)
|
|
||||||
{
|
|
||||||
if (sections[index] == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isChunkSectionEmpty(sections[index]))
|
|
||||||
{
|
|
||||||
this.minNonEmptyHeight = this.getChunkSectionMinHeight(index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.minNonEmptyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxNonEmptyHeight()
|
|
||||||
{
|
|
||||||
if (this.maxNonEmptyHeight != Integer.MAX_VALUE)
|
|
||||||
{
|
|
||||||
return this.maxNonEmptyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// default if every section is empty or missing
|
|
||||||
this.maxNonEmptyHeight = this.getExclusiveMaxBuildHeight();
|
|
||||||
|
|
||||||
// determine the highest empty section (top down)
|
|
||||||
LevelChunkSection[] sections = this.chunk.getSections();
|
|
||||||
for (int index = sections.length-1; index >= 0; index--)
|
|
||||||
{
|
|
||||||
// update at each position to fix using the max height if the chunk is empty
|
|
||||||
this.maxNonEmptyHeight = this.getChunkSectionMinHeight(index) + 16;
|
|
||||||
|
|
||||||
if (sections[index] == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isChunkSectionEmpty(sections[index]))
|
|
||||||
{
|
|
||||||
// non-empty section found
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.maxNonEmptyHeight;
|
|
||||||
}
|
|
||||||
private static boolean isChunkSectionEmpty(LevelChunkSection section)
|
|
||||||
{
|
|
||||||
#if MC_VER == MC_1_16_5
|
|
||||||
return section.isEmpty();
|
|
||||||
#elif MC_VER == MC_1_17_1
|
|
||||||
return section.isEmpty();
|
|
||||||
#else
|
|
||||||
return section.hasOnlyAir();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createDhHeightMaps()
|
|
||||||
{
|
|
||||||
if (heightmapThreadWarningLogged
|
|
||||||
&& !DhApi.isDhThread())
|
|
||||||
{
|
|
||||||
LOGGER.warn("ChunkWrapper Height maps created on non-DH thread ["+Thread.currentThread().getName()+"]. This may cause stuttering.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
|
||||||
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
|
||||||
|
|
||||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
|
||||||
{
|
|
||||||
int minInclusiveBuildHeight = this.getMinNonEmptyHeight();
|
|
||||||
// if no blocks are found the height map will be at the bottom of the world
|
|
||||||
int solidHeight = minInclusiveBuildHeight;
|
|
||||||
int lightBlockingHeight = minInclusiveBuildHeight;
|
|
||||||
|
|
||||||
|
|
||||||
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
|
|
||||||
IBlockStateWrapper block = this.getBlockState(x, y, z);
|
|
||||||
while (// go down until we reach the minimum build height
|
|
||||||
y > minInclusiveBuildHeight
|
|
||||||
// keep going until we find both height map values
|
|
||||||
&&
|
|
||||||
(
|
|
||||||
solidHeight == minInclusiveBuildHeight
|
|
||||||
|| lightBlockingHeight == minInclusiveBuildHeight
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// is this block solid?
|
|
||||||
if (solidHeight == minInclusiveBuildHeight
|
|
||||||
&& block.isSolid())
|
|
||||||
{
|
|
||||||
solidHeight = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// is this block light blocking?
|
|
||||||
if (lightBlockingHeight == minInclusiveBuildHeight
|
|
||||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
|
|
||||||
{
|
|
||||||
lightBlockingHeight = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the next block down
|
|
||||||
y--;
|
|
||||||
block = this.getBlockState(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.solidHeightMap[x][z] = solidHeight;
|
|
||||||
this.lightBlockingHeightMap[x][z] = lightBlockingHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSolidHeightMapValue(int xRel, int zRel)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
|
|
||||||
|
|
||||||
// will be null if we want to use MC heightmaps
|
|
||||||
if (this.solidHeightMap == null)
|
|
||||||
{
|
|
||||||
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return this.solidHeightMap[xRel][zRel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightBlockingHeightMapValue(int xRel, int zRel)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
|
|
||||||
|
|
||||||
if (this.lightBlockingHeightMap == null)
|
|
||||||
{
|
|
||||||
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return this.lightBlockingHeightMap[xRel][zRel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBiomeWrapper getBiome(int relX, int relY, int relZ)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
|
|
||||||
relX >> 2, relY >> 2, relZ >> 2),
|
|
||||||
this.wrappedLevel);
|
|
||||||
#elif MC_VER < MC_1_18_2
|
|
||||||
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
|
|
||||||
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
|
|
||||||
this.wrappedLevel);
|
|
||||||
#elif MC_VER < MC_1_18_2
|
|
||||||
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
|
|
||||||
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
|
|
||||||
this.wrappedLevel);
|
|
||||||
#else
|
|
||||||
//Now returns a Holder<Biome> instead of Biome
|
|
||||||
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
|
|
||||||
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
|
|
||||||
this.wrappedLevel);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
|
||||||
|
|
||||||
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
|
|
||||||
|
|
||||||
blockPos.setX(relX);
|
|
||||||
blockPos.setY(relY);
|
|
||||||
blockPos.setZ(relZ);
|
|
||||||
|
|
||||||
// TODO copy into pooled array, this isn't thread safe and can cause MC to throw errors if the chunk is loaded
|
|
||||||
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
|
||||||
|
|
||||||
BlockPos.MutableBlockPos pos = (BlockPos.MutableBlockPos)mcBlockPos.getWrappedMcObject();
|
|
||||||
pos.setX(relX);
|
|
||||||
pos.setY(relY);
|
|
||||||
pos.setZ(relZ);
|
|
||||||
|
|
||||||
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(pos), this.wrappedLevel, guess);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
// Commented out experimental LevelChunkSection cloning logic to fix extremely rare concurrency modification issue
|
|
||||||
// James has only ever seen a report relating to LevelSection concurrent modification once,
|
|
||||||
// the issue can cause DH lighting/LOD building to fail due to the chunk being modified on the server.
|
|
||||||
// James has only heard of this issue once, so it isn't a high priority issue.
|
|
||||||
// And from James' quick look at a few different MC versions it appears the LevelChunkSection object changes quite drastically between MC versions,
|
|
||||||
// meaning any cloning logic would have to either be a new wrapper or very MC version dependent, either way a lot of additional work.
|
|
||||||
// Due to the large time cost and extremely rare nature of the issue, this logic is commented out unless this issue pops up again in the future.
|
|
||||||
|
|
||||||
// instance variable to hold the cloned sections
|
|
||||||
private final LevelChunkSection[] levelChunkSections;
|
|
||||||
|
|
||||||
// new constructor logic to clone the sections
|
|
||||||
public constructor(...)
|
|
||||||
{
|
|
||||||
// other constructor logic //
|
|
||||||
|
|
||||||
LevelChunkSection[] sections = this.chunk.getSections();
|
|
||||||
this.levelChunkSections = new LevelChunkSection[sections.length];
|
|
||||||
for (int i = 0; i < sections.length; i++)
|
|
||||||
{
|
|
||||||
LevelChunkSection section = sections[i];
|
|
||||||
if (section != null)
|
|
||||||
{
|
|
||||||
// TODO implement section cloning for older MC versions, only 1.21.4 MC (and maybe other semi recent versions) have a clean way to handle this
|
|
||||||
// TODO we probably want a wrapper object instead
|
|
||||||
#if MC_VER < MC_1_21_4
|
|
||||||
this.levelChunkSections[i] = section;
|
|
||||||
#else
|
|
||||||
this.levelChunkSections[i] = section.copy();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replacement getters
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
|
||||||
return this.getBlockStateInternal(relX, relY, relZ, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
|
||||||
return this.getBlockStateInternal(relX, relY, relZ, guess);
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal getter logic
|
|
||||||
private IBlockStateWrapper getBlockStateInternal(int relX, int y, int relZ, @Nullable IBlockStateWrapper guess)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// attempt to get the section for this position
|
|
||||||
int i = (y - this.getInclusiveMinBuildHeight()) / 16;
|
|
||||||
if (i >= 0 && i < this.levelChunkSections.length)
|
|
||||||
{
|
|
||||||
LevelChunkSection section = this.levelChunkSections[i];
|
|
||||||
if (!section.hasOnlyAir())
|
|
||||||
{
|
|
||||||
if (guess != null)
|
|
||||||
{
|
|
||||||
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel, guess);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return BlockStateWrapper.AIR;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return BlockStateWrapper.AIR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IMutableBlockPosWrapper getMutableBlockPosWrapper() { return MUTABLE_BLOCK_POS_WRAPPER_REF.get(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DhChunkPos getChunkPos() { return this.chunkPos; }
|
|
||||||
|
|
||||||
public ChunkAccess getChunk() { return this.chunk; }
|
|
||||||
|
|
||||||
public void trySetStatus(ChunkStatus status) { trySetStatus(this.getChunk(), status); }
|
|
||||||
/** does nothing if the chunk object doesn't support setting it's status */
|
|
||||||
public static void trySetStatus(ChunkAccess chunk, ChunkStatus status)
|
|
||||||
{
|
|
||||||
if (chunk instanceof ProtoChunk)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_1
|
|
||||||
((ProtoChunk) chunk).setStatus(status);
|
|
||||||
#else
|
|
||||||
((ProtoChunk) chunk).setPersistedStatus(status);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
|
|
||||||
public static ChunkStatus getStatus(ChunkAccess chunk)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_1
|
|
||||||
return chunk.getStatus();
|
|
||||||
#else
|
|
||||||
return chunk.getPersistedStatus();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxBlockX() { return this.chunk.getPos().getMaxBlockX(); }
|
|
||||||
@Override
|
|
||||||
public int getMaxBlockZ() { return this.chunk.getPos().getMaxBlockZ(); }
|
|
||||||
@Override
|
|
||||||
public int getMinBlockX() { return this.chunk.getPos().getMinBlockX(); }
|
|
||||||
@Override
|
|
||||||
public int getMinBlockZ() { return this.chunk.getPos().getMinBlockZ(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========//
|
|
||||||
// lighting //
|
|
||||||
//==========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIsDhSkyLightCorrect(boolean isDhLightCorrect) { this.isDhSkyLightCorrect = isDhLightCorrect; }
|
|
||||||
@Override
|
|
||||||
public void setIsDhBlockLightCorrect(boolean isDhLightCorrect) { this.isDhBlockLightCorrect = isDhLightCorrect; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDhBlockLightingCorrect() { return this.isDhBlockLightCorrect; }
|
|
||||||
@Override
|
|
||||||
public boolean isDhSkyLightCorrect() { return this.isDhSkyLightCorrect; }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDhBlockLight(int relX, int y, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
return this.getBlockLightStorage().get(relX, y, relZ);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void setDhBlockLight(int relX, int y, int relZ, int lightValue)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
this.getBlockLightStorage().set(relX, y, relZ, lightValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChunkLightStorage getBlockLightStorage()
|
|
||||||
{
|
|
||||||
if (this.blockLightStorage == null)
|
|
||||||
{
|
|
||||||
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(this);
|
|
||||||
}
|
|
||||||
return this.blockLightStorage;
|
|
||||||
}
|
|
||||||
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
|
|
||||||
@Override
|
|
||||||
public void clearDhBlockLighting() { this.getBlockLightStorage().clear(); }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDhSkyLight(int relX, int y, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
return this.getSkyLightStorage().get(relX, y, relZ);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void setDhSkyLight(int relX, int y, int relZ, int lightValue)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
this.getSkyLightStorage().set(relX, y, relZ, lightValue);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void clearDhSkyLighting() { this.getSkyLightStorage().clear(); }
|
|
||||||
|
|
||||||
private ChunkLightStorage getSkyLightStorage()
|
|
||||||
{
|
|
||||||
if (this.skyLightStorage == null)
|
|
||||||
{
|
|
||||||
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(this);
|
|
||||||
}
|
|
||||||
return this.skyLightStorage;
|
|
||||||
}
|
|
||||||
public void setSkyLightStorage(ChunkLightStorage lightStorage) { this.skyLightStorage = lightStorage; }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FIXME synchronized is necessary for a rare issue where this method is called from two separate threads at the same time
|
|
||||||
* before the list has finished populating.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public synchronized ArrayList<DhBlockPos> getWorldBlockLightPosList()
|
|
||||||
{
|
|
||||||
// only populate the list once
|
|
||||||
if (this.blockLightPosList == null)
|
|
||||||
{
|
|
||||||
this.blockLightPosList = new ArrayList<>();
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
this.chunk.getLights().forEach((blockPos) ->
|
|
||||||
{
|
|
||||||
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
|
|
||||||
});
|
|
||||||
#else
|
|
||||||
this.chunk.findBlockLightSources((blockPos, blockState) ->
|
|
||||||
{
|
|
||||||
DhBlockPos pos = new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
|
||||||
|
|
||||||
// this can be uncommented if MC decides to return relative block positions in the future instead of world positions
|
|
||||||
//pos.mutateToChunkRelativePos(pos);
|
|
||||||
//pos.mutateOffset(this.chunkPos.getMinBlockX(), 0, this.chunkPos.getMinBlockZ(), pos);
|
|
||||||
|
|
||||||
this.blockLightPosList.add(pos);
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.blockLightPosList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
|
|
||||||
|
|
||||||
//@Override
|
|
||||||
//public int hashCode()
|
|
||||||
//{
|
|
||||||
// if (this.blockBiomeHashCode == 0)
|
|
||||||
// {
|
|
||||||
// this.blockBiomeHashCode = this.getBlockBiomeHashCode();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return this.blockBiomeHashCode;
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
-1080
File diff suppressed because it is too large
Load Diff
-98
@@ -1,98 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
// not supported for older MC versions
|
|
||||||
#else
|
|
||||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.components.debug.DebugScreenDisplayer;
|
|
||||||
import net.minecraft.client.gui.components.debug.DebugScreenEntries;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
// not supported for older MC versions
|
|
||||||
public class DhDebugScreenEntry
|
|
||||||
{}
|
|
||||||
#else
|
|
||||||
public class DhDebugScreenEntry implements net.minecraft.client.gui.components.debug.DebugScreenEntry
|
|
||||||
{
|
|
||||||
public static void register()
|
|
||||||
{
|
|
||||||
// This method is private, so its access will need to be widened
|
|
||||||
DebugScreenEntries.register(
|
|
||||||
// The id, this will be displayed on the options screen
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, "distant_horizons"),
|
|
||||||
#else
|
|
||||||
"distant_horizons",
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The screen entry
|
|
||||||
new DhDebugScreenEntry()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void display(@NotNull DebugScreenDisplayer displayer, @Nullable Level level, @Nullable LevelChunk clientChunk, @Nullable LevelChunk serverChunk)
|
|
||||||
{
|
|
||||||
List<String> messageList = new ArrayList<>();
|
|
||||||
F3Screen.addStringToDisplay(messageList);
|
|
||||||
|
|
||||||
for (String message : messageList)
|
|
||||||
{
|
|
||||||
displayer.addLine(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
//// The following will display like so if it is the only entry on the screen:
|
|
||||||
//// First left! First Right!
|
|
||||||
////
|
|
||||||
//// Hello world! Random text!
|
|
||||||
//// Lorem ipsum.
|
|
||||||
//// I am another group!
|
|
||||||
//// I am one group This will appear after with no line breaks!
|
|
||||||
//// All in a row
|
|
||||||
//// Provided in a list.
|
|
||||||
////
|
|
||||||
//
|
|
||||||
//displayer.addLine("Hello world!");
|
|
||||||
//displayer.addLine("Lorem ipsum.");
|
|
||||||
//displayer.addLine("Random text!");
|
|
||||||
//
|
|
||||||
//// These will be displayed first
|
|
||||||
//displayer.addPriorityLine("First left!");
|
|
||||||
//displayer.addPriorityLine("First right!");
|
|
||||||
//
|
|
||||||
//// These will be grouped separately based on the key
|
|
||||||
//displayer.addToGroup(GROUP_ONE, List.of(
|
|
||||||
// "I am one group",
|
|
||||||
// "All in a row",
|
|
||||||
// "Provided in a list."
|
|
||||||
//));
|
|
||||||
//
|
|
||||||
//displayer.addToGroup(GROUP_TWO, "I am another group!");
|
|
||||||
//displayer.addToGroup(GROUP_TWO, "This will appear after with no line breaks!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAllowed(boolean reducedDebugInfo)
|
|
||||||
{
|
|
||||||
// Always show regardless of accessibility option
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.Font;
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
#else
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
#endif
|
|
||||||
import net.minecraft.client.gui.components.Button;
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DhScreen extends Screen
|
|
||||||
{
|
|
||||||
|
|
||||||
protected DhScreen(Component $$0)
|
|
||||||
{
|
|
||||||
super($$0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// addRenderableWidget in 1.17 and over
|
|
||||||
// addButton in 1.16 and below
|
|
||||||
protected Button addBtn(Button button)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
return this.addButton(button);
|
|
||||||
#else
|
|
||||||
return this.addRenderableWidget(button);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
protected void DhDrawCenteredString(PoseStack guiStack, Font font, Component text, int x, int y, int color)
|
|
||||||
{
|
|
||||||
drawCenteredString(guiStack, font, text, x, y, color);
|
|
||||||
}
|
|
||||||
protected void DhDrawString(PoseStack guiStack, Font font, Component text, int x, int y, int color)
|
|
||||||
{
|
|
||||||
drawString(guiStack, font, text, x, y, color);
|
|
||||||
}
|
|
||||||
protected void DhRenderTooltip(PoseStack guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
|
|
||||||
{
|
|
||||||
renderTooltip(guiStack, text, x, y);
|
|
||||||
}
|
|
||||||
protected void DhRenderComponentTooltip(PoseStack guiStack, Font font, List<Component> comp, int x, int y)
|
|
||||||
{
|
|
||||||
renderComponentTooltip(guiStack, comp, x, y);
|
|
||||||
}
|
|
||||||
protected void DhRenderTooltip(PoseStack guiStack, Font font, Component comp, int x, int y)
|
|
||||||
{
|
|
||||||
renderTooltip(guiStack, comp, x, y);
|
|
||||||
}
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
protected void DhDrawCenteredString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
|
|
||||||
{
|
|
||||||
guiStack.drawCenteredString(font, text, x, y, color);
|
|
||||||
}
|
|
||||||
protected void DhDrawString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
|
|
||||||
{
|
|
||||||
guiStack.drawString(font, text, x, y, color);
|
|
||||||
}
|
|
||||||
//protected void DhRenderTooltip(GuiGraphics guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
|
|
||||||
//{
|
|
||||||
// guiStack.renderTooltip(font, text, x, y);
|
|
||||||
//}
|
|
||||||
protected void DhRenderComponentTooltip(GuiGraphics guiStack, Font font, List<Component> comp, int x, int y)
|
|
||||||
{
|
|
||||||
guiStack.renderComponentTooltip(font, comp, x, y);
|
|
||||||
}
|
|
||||||
protected void DhRenderTooltip(GuiGraphics guiStack, Font font, Component text, int x, int y)
|
|
||||||
{
|
|
||||||
guiStack.renderTooltip(font, text, x, y);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
protected void DhDrawCenteredString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
|
|
||||||
{
|
|
||||||
guiStack.drawCenteredString(font, text, x, y, color);
|
|
||||||
}
|
|
||||||
protected void DhDrawString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
|
|
||||||
{
|
|
||||||
guiStack.drawString(font, text, x, y, color);
|
|
||||||
}
|
|
||||||
//protected void DhRenderTooltip(GuiGraphics guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
|
|
||||||
//{
|
|
||||||
// //guiStack.renderTooltip(font, text, x, y);
|
|
||||||
//}
|
|
||||||
protected void DhRenderComponentTooltip(GuiGraphics guiStack, Font font, List<Component> comp, int x, int y)
|
|
||||||
{
|
|
||||||
guiStack.setComponentTooltipForNextFrame(font, comp, x, y);
|
|
||||||
}
|
|
||||||
protected void DhRenderTooltip(GuiGraphics guiStack, Font font, Component text, int x, int y)
|
|
||||||
{
|
|
||||||
guiStack.setTooltipForNextFrame(font, text, x, y);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.ConfigHandler;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import com.seibel.distanthorizons.core.config.gui.JavaScreenHandlerScreen;
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
public class GetConfigScreen
|
|
||||||
{
|
|
||||||
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
public static EType useScreen = EType.Classic;
|
|
||||||
|
|
||||||
public enum EType
|
|
||||||
{
|
|
||||||
Classic,
|
|
||||||
JavaSwing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Screen getScreen(Screen parent)
|
|
||||||
{
|
|
||||||
// TODO it'd be nice to have this run automatically on startup
|
|
||||||
// but this will only work once MC has added our lang file,
|
|
||||||
// which won't be for sure added until we request a GUI
|
|
||||||
if (ModInfo.IS_DEV_BUILD)
|
|
||||||
{
|
|
||||||
String missingLangEntries = ConfigHandler.INSTANCE.generateLang(true, true);
|
|
||||||
|
|
||||||
// trim to remove any newlines/spaces
|
|
||||||
// that may be present when no lang entries need changing
|
|
||||||
// then we can check length != 0 if any items are missing and need adding
|
|
||||||
String trimmedMissingEntries = missingLangEntries.trim();
|
|
||||||
if (!trimmedMissingEntries.isEmpty())
|
|
||||||
{
|
|
||||||
LOGGER.warn("One or more language entries is missing:");
|
|
||||||
LOGGER.warn(missingLangEntries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
switch (useScreen)
|
|
||||||
{
|
|
||||||
case Classic:
|
|
||||||
return ClassicConfigGUI.getScreen(parent, "client");
|
|
||||||
case JavaSwing:
|
|
||||||
//return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
|
|
||||||
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
|
||||||
import net.minecraft.client.gui.components.Button;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.network.chat.MutableComponent;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
import net.minecraft.network.chat.TextComponent;
|
|
||||||
import net.minecraft.network.chat.TranslatableComponent;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public class GuiHelper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Helper static methods for versional compat
|
|
||||||
*/
|
|
||||||
public static Button MakeBtn(Component base, int posX, int posZ, int width, int height, Button.OnPress action)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
return new Button(posX, posZ, width, height, base, action);
|
|
||||||
#else
|
|
||||||
return Button.builder(base, action).bounds(posX, posZ, width, height).build();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MutableComponent TextOrLiteral(String text)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
return new TextComponent(text);
|
|
||||||
#else
|
|
||||||
return Component.literal(text);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MutableComponent TextOrTranslatable(String text)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
return new TextComponent(text);
|
|
||||||
#else
|
|
||||||
return Component.translatable(text);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MutableComponent Translatable(String text, Object... args)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
return new TranslatableComponent(text, args);
|
|
||||||
#else
|
|
||||||
return Component.translatable(text, args);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetX(AbstractWidget w, int x)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
w.x = x;
|
|
||||||
#else
|
|
||||||
w.setX(x);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetY(AbstractWidget w, int y)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
w.y = y;
|
|
||||||
#else
|
|
||||||
w.setY(y);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
|
|
||||||
import net.minecraft.client.resources.language.I18n;
|
|
||||||
|
|
||||||
public class LangWrapper implements ILangWrapper
|
|
||||||
{
|
|
||||||
public static final LangWrapper INSTANCE = new LangWrapper();
|
|
||||||
@Override
|
|
||||||
public boolean langExists(String str)
|
|
||||||
{
|
|
||||||
return I18n.exists(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLang(String str)
|
|
||||||
{
|
|
||||||
return I18n.get(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-165
@@ -1,165 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.Window;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.seibel.distanthorizons.core.config.gui.AbstractScreen;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
#if MC_VER >= MC_1_20_1
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
#endif
|
|
||||||
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class MinecraftScreen
|
|
||||||
{
|
|
||||||
public static Screen getScreen(Screen parent, AbstractScreen screen, String translationName)
|
|
||||||
{
|
|
||||||
return new ConfigScreenRenderer(parent, screen, translationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ConfigScreenRenderer extends DhScreen
|
|
||||||
{
|
|
||||||
private final Screen parent;
|
|
||||||
private ConfigListWidget configListWidget;
|
|
||||||
private AbstractScreen screen;
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args)
|
|
||||||
{ return new net.minecraft.network.chat.TranslatableComponent(str, args); }
|
|
||||||
#else
|
|
||||||
public static net.minecraft.network.chat.MutableComponent translate(String str, Object... args)
|
|
||||||
{ return net.minecraft.network.chat.Component.translatable(str, args); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName)
|
|
||||||
{
|
|
||||||
super(translate(translationName));
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow();
|
|
||||||
#else
|
|
||||||
screen.minecraftWindow = Minecraft.getInstance().getWindow().handle();
|
|
||||||
#endif
|
|
||||||
this.parent = parent;
|
|
||||||
this.screen = screen;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init()
|
|
||||||
{
|
|
||||||
super.init(); // Init Minecraft's screen
|
|
||||||
Window mcWindow = this.minecraft.getWindow();
|
|
||||||
this.screen.width = mcWindow.getWidth();
|
|
||||||
this.screen.height = mcWindow.getHeight();
|
|
||||||
this.screen.scaledWidth = this.width;
|
|
||||||
this.screen.scaledHeight = this.height;
|
|
||||||
this.screen.init(); // Init our own config screen
|
|
||||||
|
|
||||||
this.configListWidget = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
|
|
||||||
if (this.minecraft != null && this.minecraft.level != null) // Check if in game
|
|
||||||
{
|
|
||||||
this.configListWidget.setRenderBackground(false); // Disable from rendering
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.addWidget(this.configListWidget); // Add the tint to the things to be rendered
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#else
|
|
||||||
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_2
|
|
||||||
this.renderBackground(matrices); // Render background
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
|
||||||
#else
|
|
||||||
// background blur is already being rendered, rendering again causes the game to crash
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.configListWidget.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
|
|
||||||
|
|
||||||
this.screen.mouseX = mouseX;
|
|
||||||
this.screen.mouseY = mouseY;
|
|
||||||
this.screen.render(delta); // Render everything on the main screen
|
|
||||||
|
|
||||||
super.render(matrices, mouseX, mouseY, delta); // Render the vanilla stuff (currently only used for the background and tint)
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
@Override
|
|
||||||
public void resize(Minecraft mc, int width, int height)
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public void resize(int width, int height)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// Resize Minecraft's screen
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
super.resize(mc, width, height);
|
|
||||||
#else
|
|
||||||
super.resize(width, height);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
Window mcWindow = this.minecraft.getWindow();
|
|
||||||
this.screen.width = mcWindow.getWidth();
|
|
||||||
this.screen.height = mcWindow.getHeight();
|
|
||||||
this.screen.scaledWidth = this.width;
|
|
||||||
this.screen.scaledHeight = this.height;
|
|
||||||
this.screen.onResize(); // Resize our screen
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tick()
|
|
||||||
{
|
|
||||||
super.tick(); // Tick Minecraft's screen
|
|
||||||
this.screen.tick(); // Tick our screen
|
|
||||||
if (this.screen.close) // If we decide to close the screen, then actually close the screen
|
|
||||||
{
|
|
||||||
this.onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClose()
|
|
||||||
{
|
|
||||||
this.screen.onClose(); // Close our screen
|
|
||||||
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFilesDrop(@NotNull List<Path> files)
|
|
||||||
{ this.screen.onFilesDrop(files); }
|
|
||||||
|
|
||||||
// For checking if it should close when you press the escape key
|
|
||||||
@Override
|
|
||||||
public boolean shouldCloseOnEsc()
|
|
||||||
{ return this.screen.shouldCloseOnEsc; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConfigListWidget extends ContainerObjectSelectionList
|
|
||||||
{
|
|
||||||
public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_4
|
|
||||||
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
|
|
||||||
#else
|
|
||||||
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
|
|
||||||
#endif
|
|
||||||
this.centerListVertically = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-257
@@ -1,257 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
|
||||||
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
import net.minecraft.client.gui.components.Button;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
import net.minecraft.client.gui.components.ImageButton;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
#elif MC_VER < MC_1_20_1
|
|
||||||
import net.minecraft.client.gui.components.ImageButton;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
|
||||||
#elif MC_VER < MC_1_20_2
|
|
||||||
import net.minecraft.client.gui.components.ImageButton;
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.renderer.RenderPipelines;
|
|
||||||
#else
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
import net.minecraft.client.renderer.RenderPipelines;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
#else
|
|
||||||
import net.minecraft.resources.Identifier;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a button with a texture on it (and a background) that works with all mc versions
|
|
||||||
*
|
|
||||||
* @author coolGi
|
|
||||||
* @version 2023-10-03
|
|
||||||
*/
|
|
||||||
#if MC_VER < MC_1_20_2
|
|
||||||
public class TexturedButtonWidget extends ImageButton
|
|
||||||
#else
|
|
||||||
public class TexturedButtonWidget extends Button
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
public final boolean renderBackground;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_20_2
|
|
||||||
private final int u;
|
|
||||||
private final int v;
|
|
||||||
private final int hoveredVOffset;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
private final ResourceLocation textureResourceLocation;
|
|
||||||
#else
|
|
||||||
private final Identifier textureResourceLocation;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private final int textureWidth;
|
|
||||||
private final int textureHeight;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
public TexturedButtonWidget(
|
|
||||||
int x, int y, int width, int height, int u, int v, int hoveredVOffset,
|
|
||||||
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
|
|
||||||
#else Identifier textureResourceLocation,
|
|
||||||
#endif
|
|
||||||
int textureWidth, int textureHeight, OnPress pressAction, Component text)
|
|
||||||
{
|
|
||||||
this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true);
|
|
||||||
}
|
|
||||||
public TexturedButtonWidget(
|
|
||||||
int x, int y, int width, int height, int u, int v, int hoveredVOffset,
|
|
||||||
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
|
|
||||||
#else Identifier textureResourceLocation,
|
|
||||||
#endif
|
|
||||||
int textureWidth, int textureHeight, OnPress pressAction, Component text,
|
|
||||||
boolean renderBackground)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_2
|
|
||||||
super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text);
|
|
||||||
#else
|
|
||||||
// We don't pass on the text option as otherwise it will render (we normally pass it for narration)
|
|
||||||
// TODO: Find a fix for it
|
|
||||||
super(x, y, width, height, Component.empty(), pressAction, DEFAULT_NARRATION);
|
|
||||||
|
|
||||||
this.u = u;
|
|
||||||
this.v = v;
|
|
||||||
this.hoveredVOffset = hoveredVOffset;
|
|
||||||
|
|
||||||
this.textureResourceLocation = textureResourceLocation;
|
|
||||||
|
|
||||||
this.textureWidth = textureWidth;
|
|
||||||
this.textureHeight = textureHeight;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.renderBackground = renderBackground;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_2
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
@Override
|
|
||||||
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta)
|
|
||||||
{
|
|
||||||
if (this.renderBackground) // Renders the background of the button
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
Minecraft.getInstance().getTextureManager().bind(WIDGETS_LOCATION);
|
|
||||||
RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
|
|
||||||
#else
|
|
||||||
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
|
||||||
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
|
|
||||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int i = this.getYImage(this.isHovered);
|
|
||||||
RenderSystem.enableBlend();
|
|
||||||
RenderSystem.defaultBlendFunc();
|
|
||||||
RenderSystem.enableDepthTest();
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
this.blit(matrices, this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
|
|
||||||
this.blit(matrices, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
|
|
||||||
#else
|
|
||||||
this.blit(matrices, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
|
|
||||||
this.blit(matrices, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
super.renderButton(matrices, mouseX, mouseY, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
@Override
|
|
||||||
public void renderWidget(PoseStack matrices, int mouseX, int mouseY, float delta)
|
|
||||||
{
|
|
||||||
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
|
||||||
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
|
|
||||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
if (this.renderBackground) // Renders the background of the button
|
|
||||||
{
|
|
||||||
int i = 1;
|
|
||||||
if (!this.active) i = 0;
|
|
||||||
else if (this.isHovered) i = 2;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
RenderSystem.enableBlend();
|
|
||||||
RenderSystem.defaultBlendFunc();
|
|
||||||
RenderSystem.enableDepthTest();
|
|
||||||
|
|
||||||
this.blit(matrices, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
|
|
||||||
this.blit(matrices, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
|
|
||||||
#else
|
|
||||||
matrices.blit(WIDGETS_LOCATION, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
|
|
||||||
matrices.blit(WIDGETS_LOCATION, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
super.renderWidget(matrices, mouseX, mouseY, delta);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
#if MC_VER < MC_1_21_11
|
|
||||||
@Override
|
|
||||||
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
protected void renderContents(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (this.renderBackground)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
matrices.blitSprite(SPRITES.get(this.active, this.isHoveredOrFocused()), this.getX(), this.getY(), this.getWidth(), this.getHeight());
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
matrices.blitSprite(
|
|
||||||
RenderType::guiTextured,
|
|
||||||
SPRITES.get(this.active, this.isHoveredOrFocused()),
|
|
||||||
this.getX(), this.getY(),
|
|
||||||
this.getWidth(), this.getHeight());
|
|
||||||
#else
|
|
||||||
matrices.blitSprite(
|
|
||||||
RenderPipelines.GUI_TEXTURED,
|
|
||||||
SPRITES.get(this.active, this.isHoveredOrFocused()),
|
|
||||||
this.getX(), this.getY(),
|
|
||||||
this.getWidth(), this.getHeight());
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Renders the sprite
|
|
||||||
int i = 0;
|
|
||||||
if (!this.active)
|
|
||||||
{
|
|
||||||
i = 2;
|
|
||||||
}
|
|
||||||
else if (this.isHovered)
|
|
||||||
{
|
|
||||||
i = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
matrices.blit(this.textureResourceLocation, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight);
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
matrices.blit(
|
|
||||||
RenderType::guiTextured,
|
|
||||||
this.textureResourceLocation,
|
|
||||||
this.getX(), this.getY(),
|
|
||||||
this.u, this.v + (this.hoveredVOffset * i),
|
|
||||||
this.width, this.height,
|
|
||||||
this.textureWidth, this.textureHeight);
|
|
||||||
#else
|
|
||||||
matrices.blit(
|
|
||||||
RenderPipelines.GUI_TEXTURED,
|
|
||||||
this.textureResourceLocation,
|
|
||||||
this.getX(), this.getY(),
|
|
||||||
this.u, this.v + (this.hoveredVOffset * i),
|
|
||||||
this.width, this.height,
|
|
||||||
this.textureWidth, this.textureHeight);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
-33
@@ -1,33 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui.config;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.gui.IConfigGuiInfo;
|
|
||||||
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
|
|
||||||
import net.minecraft.client.gui.components.Button;
|
|
||||||
import net.minecraft.client.gui.components.EditBox;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* holds information needed by the config GUI for rendering.
|
|
||||||
*
|
|
||||||
* @see AbstractConfigBase
|
|
||||||
*/
|
|
||||||
public class ConfigGuiInfo implements IConfigGuiInfo
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Used to display validation errors.
|
|
||||||
* Null if no error is present.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public Component errorMessage;
|
|
||||||
|
|
||||||
public BiFunction<EditBox, Button, Predicate<String>> tooltipFunction;
|
|
||||||
/** determines which options the button will show */
|
|
||||||
public AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>> buttonOptionMap;
|
|
||||||
|
|
||||||
}
|
|
||||||
-287
@@ -1,287 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui.updater;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import com.seibel.distanthorizons.core.jar.installer.MarkdownFormatter;
|
|
||||||
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.gui.Font;
|
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
|
||||||
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
|
||||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
import net.minecraft.client.gui.narration.NarratableEntry;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
import net.minecraft.client.gui.GuiComponent;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
#else
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that pops up if the mod has an update.
|
|
||||||
*
|
|
||||||
* @author coolGi
|
|
||||||
*/
|
|
||||||
// TODO: After finishing the config, rewrite this in openGL as well
|
|
||||||
// TODO: Make this
|
|
||||||
public class ChangelogScreen extends DhScreen
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
|
|
||||||
private Screen parent;
|
|
||||||
private String versionID;
|
|
||||||
private List<String> changelog;
|
|
||||||
private TextArea changelogArea;
|
|
||||||
|
|
||||||
public boolean usable = false;
|
|
||||||
|
|
||||||
public ChangelogScreen(Screen parent)
|
|
||||||
{
|
|
||||||
this(parent, null);
|
|
||||||
|
|
||||||
if (!ModrinthGetter.initted) // Make sure the modrinth stuff is initted
|
|
||||||
{
|
|
||||||
ModrinthGetter.init();
|
|
||||||
}
|
|
||||||
if (!ModrinthGetter.initted) // If its not initted, then this isnt usable
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ModrinthGetter.mcVersions.contains(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String versionID = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
|
|
||||||
if (versionID == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.setupChangelog(versionID);
|
|
||||||
this.usable = true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("failed to setup changelog, error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChangelogScreen(Screen parent, String versionID)
|
|
||||||
{
|
|
||||||
super(Translatable(ModInfo.ID + ".updater.title"));
|
|
||||||
this.parent = parent;
|
|
||||||
this.versionID = versionID;
|
|
||||||
|
|
||||||
|
|
||||||
if (versionID == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.setupChangelog(versionID);
|
|
||||||
this.usable = true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupChangelog(String versionID)
|
|
||||||
{
|
|
||||||
this.changelog = new ArrayList<>();
|
|
||||||
|
|
||||||
// Put the new version name at the very top of the change log
|
|
||||||
this.changelog.add("§lChangelog for " + ModrinthGetter.releaseNames.get(versionID) + "§r");
|
|
||||||
this.changelog.add("");
|
|
||||||
this.changelog.add("");
|
|
||||||
|
|
||||||
String changelog = ModrinthGetter.changeLogs.get(versionID);
|
|
||||||
if (changelog == null)
|
|
||||||
{
|
|
||||||
// in case something goes wrong this will prevent null pointers
|
|
||||||
changelog = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the release changelog and split it by the new lines
|
|
||||||
String[] unwrappedChangelog = // Arrays.asList could be used if a list object is desired here vs List.of which is only available for Java 9+
|
|
||||||
// This formats markdown to minecraft's "§" charactersnew MarkdownFormatter.MinecraftFormat().convertTo(
|
|
||||||
new MarkdownFormatter.MinecraftFormat().convertTo(changelog).split("\\n");
|
|
||||||
// Makes the words wrap around to not go off the screen
|
|
||||||
for (String str : unwrappedChangelog)
|
|
||||||
{
|
|
||||||
this.changelog.addAll(
|
|
||||||
MarkdownFormatter.splitString(str, 75)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Debugging
|
|
||||||
// System.out.println(this.changelog);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init()
|
|
||||||
{
|
|
||||||
super.init();
|
|
||||||
if (!this.usable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.addBtn( // Close
|
|
||||||
MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> {
|
|
||||||
this.onClose();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, 32, 10);
|
|
||||||
for (int i = 0; i < this.changelog.size(); i++)
|
|
||||||
{
|
|
||||||
this.changelogArea.addButton(TextOrLiteral(this.changelog.get(i)));
|
|
||||||
// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#else
|
|
||||||
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_2
|
|
||||||
this.renderBackground(matrices); // Render background
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
|
||||||
#else
|
|
||||||
// background blur is already being rendered, rendering again causes the game to crash
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!this.usable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxScroll;
|
|
||||||
#if MC_VER <= MC_1_21_3
|
|
||||||
maxScroll = this.changelogArea.getMaxScroll();
|
|
||||||
#else
|
|
||||||
maxScroll = this.changelogArea.maxScrollAmount();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set the scroll position to the mouse height relative to the screen
|
|
||||||
// This is a bit of a hack as we cannot scroll on this area
|
|
||||||
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * maxScroll;
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
this.changelogArea.setScrollAmount(scrollAmount);
|
|
||||||
#elif MC_VER <= MC_1_21_3
|
|
||||||
this.changelogArea.scrollAmount = scrollAmount;
|
|
||||||
#else
|
|
||||||
this.changelogArea.setScrollAmount(scrollAmount);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// render order matters, otherwise on 1.20.6+ the blurred background will render on top of the text
|
|
||||||
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
|
|
||||||
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
|
|
||||||
this.DhDrawCenteredString(matrices, this.font, this.title, this.width / 2, 15, 0xFFFFFF); // Render title
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClose()
|
|
||||||
{
|
|
||||||
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TextArea extends ContainerObjectSelectionList<ButtonEntry>
|
|
||||||
{
|
|
||||||
Font textRenderer;
|
|
||||||
|
|
||||||
public TextArea(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_4
|
|
||||||
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
|
|
||||||
#else
|
|
||||||
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
|
|
||||||
#endif
|
|
||||||
this.centerListVertically = false;
|
|
||||||
this.textRenderer = minecraftClient.font;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addButton(Component text)
|
|
||||||
{
|
|
||||||
this.addEntry(ButtonEntry.create(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRowWidth()
|
|
||||||
{
|
|
||||||
return 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry>
|
|
||||||
{
|
|
||||||
private static final Font textRenderer = Minecraft.getInstance().font;
|
|
||||||
private final Component text;
|
|
||||||
private final List<AbstractWidget> children = new ArrayList<>();
|
|
||||||
|
|
||||||
private ButtonEntry(Component text) { this.text = text; }
|
|
||||||
|
|
||||||
public static ButtonEntry create(Component text)
|
|
||||||
{ return new ButtonEntry(text); }
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
@Override
|
|
||||||
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
|
||||||
{ GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF); }
|
|
||||||
#elif MC_VER < MC_1_21_9
|
|
||||||
@Override
|
|
||||||
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
|
||||||
{ matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); }
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public void renderContent(GuiGraphics matrices, int y, int x, boolean hovered, float tickDelta)
|
|
||||||
{ matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<? extends GuiEventListener> children() { return this.children; }
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
@Override
|
|
||||||
public List<? extends NarratableEntry> narratables() { return this.children; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-220
@@ -1,220 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.gui.updater;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
|
|
||||||
import com.seibel.distanthorizons.core.jar.ModJarInfo;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
|
|
||||||
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_20_1
|
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
#else
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
#else
|
|
||||||
import net.minecraft.resources.Identifier;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The screen that pops up if the mod has an update.
|
|
||||||
*
|
|
||||||
* @author coolGi
|
|
||||||
*/
|
|
||||||
// TODO: After finishing the config, rewrite this in Java Swing as well
|
|
||||||
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
|
|
||||||
public class UpdateModScreen extends DhScreen
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
|
|
||||||
private Screen parent;
|
|
||||||
private String newVersionID;
|
|
||||||
|
|
||||||
private String currentVer;
|
|
||||||
private String nextVer;
|
|
||||||
|
|
||||||
|
|
||||||
public UpdateModScreen(Screen parent, String newVersionID) throws IllegalArgumentException
|
|
||||||
{
|
|
||||||
super(Translatable(ModInfo.ID + ".updater.title"));
|
|
||||||
this.parent = parent;
|
|
||||||
this.newVersionID = newVersionID;
|
|
||||||
|
|
||||||
|
|
||||||
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
|
|
||||||
if (updateBranch == EDhApiUpdateBranch.STABLE)
|
|
||||||
{
|
|
||||||
this.currentVer = ModInfo.VERSION;
|
|
||||||
this.nextVer = ModrinthGetter.releaseNames.get(this.newVersionID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.currentVer = ModJarInfo.Git_Commit.substring(0,7);
|
|
||||||
this.nextVer = this.newVersionID.substring(0,7);
|
|
||||||
}
|
|
||||||
|
|
||||||
// done to prevent trying to update to "null"
|
|
||||||
// (this can happen if no versions are available to check/download from modrinth/gitlab)
|
|
||||||
if (this.nextVer == null)
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("No new version found with the ID ["+newVersionID+"].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init()
|
|
||||||
{
|
|
||||||
super.init();
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
// Logo image
|
|
||||||
this.addBtn(new TexturedButtonWidget(
|
|
||||||
// Where the button is on the screen
|
|
||||||
this.width / 2 - 95, this.height / 2 - 110,
|
|
||||||
// Width and height of the button
|
|
||||||
195, 65,
|
|
||||||
// Offset
|
|
||||||
0, 0,
|
|
||||||
// Some textuary stuff
|
|
||||||
0,
|
|
||||||
#if MC_VER <= MC_1_20_6
|
|
||||||
new ResourceLocation(ModInfo.ID, "logo.png"),
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
|
|
||||||
#else
|
|
||||||
Identifier.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
|
|
||||||
#endif
|
|
||||||
195, 65,
|
|
||||||
// Create the button and tell it where to go
|
|
||||||
// For now it goes to the client option by default
|
|
||||||
(buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
|
|
||||||
// Add a title to the button
|
|
||||||
Translatable(ModInfo.ID + ".updater.title"),
|
|
||||||
// Dont render the background of the button
|
|
||||||
false
|
|
||||||
));
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Failed to setup update mod screen, error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ModInfo.IS_DEV_BUILD)
|
|
||||||
{
|
|
||||||
this.addBtn(new TexturedButtonWidget(
|
|
||||||
// Where the button is on the screen
|
|
||||||
this.width / 2 - 97, this.height / 2 + 8,
|
|
||||||
// Width and height of the button
|
|
||||||
20, 20,
|
|
||||||
// Offset
|
|
||||||
0, 0,
|
|
||||||
// Some textuary stuff
|
|
||||||
0,
|
|
||||||
#if MC_VER < MC_1_21_1
|
|
||||||
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
|
||||||
#else
|
|
||||||
Identifier.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
|
||||||
#endif
|
|
||||||
20, 20,
|
|
||||||
// Create the button and tell it where to go
|
|
||||||
(buttonWidget) -> Objects.requireNonNull(this.minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)),
|
|
||||||
// Add a title to the button
|
|
||||||
Translatable(ModInfo.ID + ".updater.title")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.addBtn( // Update
|
|
||||||
MakeBtn(Translatable(ModInfo.ID + ".updater.update"), this.width / 2 - 75, this.height / 2 + 8, 150, 20, (btn) -> {
|
|
||||||
SelfUpdater.updateMod();
|
|
||||||
this.onClose();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
this.addBtn( // Silent update
|
|
||||||
MakeBtn(Translatable(ModInfo.ID + ".updater.silent"), this.width / 2 - 75, this.height / 2 + 30, 150, 20, (btn) -> {
|
|
||||||
Config.Client.Advanced.AutoUpdater.enableSilentUpdates.set(true);
|
|
||||||
SelfUpdater.updateMod();
|
|
||||||
this.onClose();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
this.addBtn( // Later (not now)
|
|
||||||
MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> {
|
|
||||||
this.onClose();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
this.addBtn( // Never
|
|
||||||
MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> {
|
|
||||||
Config.Client.Advanced.AutoUpdater.enableAutoUpdater.set(false);
|
|
||||||
this.onClose();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#else
|
|
||||||
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_2
|
|
||||||
this.renderBackground(matrices); // Render background
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
|
||||||
#else
|
|
||||||
// background blur is already being rendered, rendering again causes the game to crash
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO: add the tooltips for the buttons
|
|
||||||
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
|
|
||||||
// TODO: Add tooltips
|
|
||||||
|
|
||||||
// Render the text's
|
|
||||||
this.DhDrawCenteredString(matrices, this.font,
|
|
||||||
Translatable(ModInfo.ID + ".updater.text1"),
|
|
||||||
this.width / 2, this.height / 2 - 35,
|
|
||||||
#if MC_VER < MC_1_21_6
|
|
||||||
0xFFFFFF // RGB
|
|
||||||
#else
|
|
||||||
0xFFFFFFFF // ARGB
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
this.DhDrawCenteredString(matrices, this.font,
|
|
||||||
Translatable(ModInfo.ID + ".updater.text2", this.currentVer, this.nextVer),
|
|
||||||
this.width / 2, this.height / 2 - 20,
|
|
||||||
#if MC_VER < MC_1_21_6
|
|
||||||
0x52FD52 // RGB
|
|
||||||
#else
|
|
||||||
0xFF52FD52 // ARGB
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClose()
|
|
||||||
{
|
|
||||||
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Go to the parent screen
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.level;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
|
||||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class KeyedClientLevelManager implements IKeyedClientLevelManager
|
|
||||||
{
|
|
||||||
public static final KeyedClientLevelManager INSTANCE = new KeyedClientLevelManager();
|
|
||||||
|
|
||||||
/** This is set and managed by the ClientApi for servers with support for DH. */
|
|
||||||
@Nullable
|
|
||||||
private IServerKeyedClientLevel serverKeyedLevel = null;
|
|
||||||
/** Allows to keep level manager enabled between loading different keyed levels */
|
|
||||||
private boolean enabled = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
private KeyedClientLevelManager() { }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//======================//
|
|
||||||
// level override logic //
|
|
||||||
//======================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String serverKey, String levelKey)
|
|
||||||
{
|
|
||||||
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevelWrapper((ClientLevel) clientLevel.getWrappedMcObject(), serverKey, levelKey);
|
|
||||||
this.serverKeyedLevel = keyedLevel;
|
|
||||||
this.enabled = true;
|
|
||||||
return keyedLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearKeyedLevel() { this.serverKeyedLevel = null; }
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() { return this.enabled; }
|
|
||||||
@Override
|
|
||||||
public void disable() { this.enabled = false; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.level;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
|
|
||||||
public class ServerKeyedClientLevelWrapper extends ClientLevelWrapper implements IServerKeyedClientLevel
|
|
||||||
{
|
|
||||||
/** Returns the folder name the server wants the client to use. */
|
|
||||||
private final String serverKey;
|
|
||||||
|
|
||||||
/** A unique identifier (generally the level's name) for differentiating multiverse levels */
|
|
||||||
private final String serverLevelKey;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public ServerKeyedClientLevelWrapper(ClientLevel level, String serverKey, String serverLevelKey)
|
|
||||||
{
|
|
||||||
super(level);
|
|
||||||
this.serverKey = serverKey;
|
|
||||||
this.serverLevelKey = serverLevelKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServerKey() { return this.serverKey; }
|
|
||||||
|
|
||||||
//======================//
|
|
||||||
// level identification //
|
|
||||||
//======================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServerLevelKey() { return this.serverLevelKey; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDhIdentifier() { return this.getServerLevelKey(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-355
@@ -1,355 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.Window;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
|
||||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
import net.minecraft.CrashReport;
|
|
||||||
import net.minecraft.client.CloudStatus;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.client.multiplayer.ServerData;
|
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
import net.minecraft.network.chat.TextComponent;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
#else
|
|
||||||
import net.minecraft.util.profiling.Profiler;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A singleton that wraps the Minecraft object.
|
|
||||||
*
|
|
||||||
* @author James Seibel
|
|
||||||
*/
|
|
||||||
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
|
||||||
|
|
||||||
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
|
|
||||||
|
|
||||||
|
|
||||||
private ProfilerWrapper profilerWrapper;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//======================//
|
|
||||||
// multiplayer handling //
|
|
||||||
//======================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
|
|
||||||
@Override
|
|
||||||
public boolean clientConnectedToDedicatedServer()
|
|
||||||
{
|
|
||||||
return MINECRAFT.getCurrentServer() != null
|
|
||||||
&& !this.hasSinglePlayerServer();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean connectedToReplay()
|
|
||||||
{
|
|
||||||
return MINECRAFT.getCurrentServer() == null
|
|
||||||
&& !this.hasSinglePlayerServer() ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentServerName()
|
|
||||||
{
|
|
||||||
if (this.connectedToReplay())
|
|
||||||
{
|
|
||||||
return ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ServerData server = MINECRAFT.getCurrentServer();
|
|
||||||
return (server != null) ? server.name : "NULL";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public String getCurrentServerIp()
|
|
||||||
{
|
|
||||||
if (this.connectedToReplay())
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ServerData server = MINECRAFT.getCurrentServer();
|
|
||||||
return (server != null) ? server.ip : "NA";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public String getCurrentServerVersion()
|
|
||||||
{
|
|
||||||
ServerData server = MINECRAFT.getCurrentServer();
|
|
||||||
return (server != null) ? server.version.getString() : "UNKOWN";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// player handling //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
public LocalPlayer getPlayer() { return MINECRAFT.player; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean playerExists() { return MINECRAFT.player != null; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DhBlockPos getPlayerBlockPos()
|
|
||||||
{
|
|
||||||
LocalPlayer player = this.getPlayer();
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
return new DhBlockPos(0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockPos playerPos = player.blockPosition();
|
|
||||||
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DhChunkPos getPlayerChunkPos()
|
|
||||||
{
|
|
||||||
LocalPlayer player = this.getPlayer();
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
return new DhChunkPos(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
ChunkPos playerPos = new ChunkPos(player.blockPosition());
|
|
||||||
#else
|
|
||||||
ChunkPos playerPos = player.chunkPosition();
|
|
||||||
#endif
|
|
||||||
return new DhChunkPos(playerPos.x, playerPos.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// level handling //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public IClientLevelWrapper getWrappedClientLevel() { return this.getWrappedClientLevel(false); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager)
|
|
||||||
{
|
|
||||||
ClientLevel level = MINECRAFT.level;
|
|
||||||
if (level == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ClientLevelWrapper.getWrapper(level, bypassLevelKeyManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
|
||||||
// messaging //
|
|
||||||
//===========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendChatMessage(String string)
|
|
||||||
{
|
|
||||||
LocalPlayer player = this.getPlayer();
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
|
|
||||||
#elif MC_VER < MC_1_21_9
|
|
||||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
|
|
||||||
#else
|
|
||||||
|
|
||||||
GLProxy.queueRunningOnRenderThread(() ->
|
|
||||||
{
|
|
||||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendOverlayMessage(String string)
|
|
||||||
{
|
|
||||||
LocalPlayer player = this.getPlayer();
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
player.displayClientMessage(new TextComponent(string), /*isOverlay*/true);
|
|
||||||
#else
|
|
||||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========================//
|
|
||||||
// vanilla option overrides //
|
|
||||||
//==========================//
|
|
||||||
|
|
||||||
public void disableVanillaClouds()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_18_2
|
|
||||||
MINECRAFT.options.renderClouds = CloudStatus.OFF;
|
|
||||||
#else
|
|
||||||
MINECRAFT.options.cloudStatus().set(CloudStatus.OFF);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disableVanillaChunkFadeIn()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
// chunk fade in was added MC 1.21.11
|
|
||||||
#else
|
|
||||||
MINECRAFT.options.chunkSectionFadeInTime().set(0.0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//======//
|
|
||||||
// misc //
|
|
||||||
//======//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* no override and not included in {@link IMinecraftClientWrapper}
|
|
||||||
* since this would only be used in common/client, not core.
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
#if MC_VER < MC_1_21_9 long
|
|
||||||
#else Window
|
|
||||||
#endif
|
|
||||||
getGlfwWindowId()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
long glfwWindowId = MINECRAFT.getWindow().getWindow();
|
|
||||||
return glfwWindowId;
|
|
||||||
#else
|
|
||||||
return MINECRAFT.getWindow();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IProfilerWrapper getProfiler()
|
|
||||||
{
|
|
||||||
ProfilerFiller profiler;
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
profiler = MINECRAFT.getProfiler();
|
|
||||||
#else
|
|
||||||
profiler = Profiler.get();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (this.profilerWrapper == null)
|
|
||||||
{
|
|
||||||
this.profilerWrapper = new ProfilerWrapper(profiler);
|
|
||||||
}
|
|
||||||
else if (profiler != this.profilerWrapper.profiler)
|
|
||||||
{
|
|
||||||
this.profilerWrapper.profiler = profiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.profilerWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void crashMinecraft(String errorMessage, Throwable exception)
|
|
||||||
{
|
|
||||||
LOGGER.fatal(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
|
|
||||||
CrashReport report = new CrashReport(errorMessage, exception);
|
|
||||||
#if MC_VER < MC_1_20_4
|
|
||||||
Minecraft.crash(report);
|
|
||||||
#else
|
|
||||||
MINECRAFT.delayCrash(report);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// mod support //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getOptionsObject() { return MINECRAFT.options; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//========//
|
|
||||||
// shared //
|
|
||||||
//========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDedicatedServer() { return false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPlayerCount()
|
|
||||||
{
|
|
||||||
// can be null if the server hasn't finished booting up yet
|
|
||||||
if (MINECRAFT.getSingleplayerServer() == null)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MINECRAFT.getSingleplayerServer().getPlayerCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-252
@@ -1,252 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
|
||||||
#else
|
|
||||||
import com.mojang.blaze3d.opengl.GlStateManager;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import org.lwjgl.opengl.GL32;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>Why does DH often call GL methods twice? </b><br>
|
|
||||||
* Once using the base {@link GL32} function and a second time using
|
|
||||||
* Minecraft's {@link GlStateManager}?<br><br>
|
|
||||||
*
|
|
||||||
* <b>Answer: </b><br>
|
|
||||||
* Compatibility and robustness<br>
|
|
||||||
* In general all MC rendering should go through MC's {@link GlStateManager},
|
|
||||||
* however that isn't always the case.
|
|
||||||
* So, to prevent issues if a mod (or MC itself) calls a direct GL function
|
|
||||||
* instead of the {@link GlStateManager} wrapper, we need to be sure about what the actual
|
|
||||||
* set value is (whether setting or getting) and that MC knows what DH has done.
|
|
||||||
* This way whether a mod (or MC) is using the {@link GlStateManager} or direct GL calls,
|
|
||||||
* they should always have the correct value for anything DH has modified.
|
|
||||||
* <br><br>
|
|
||||||
* This may slow down some low end GPUs that are driver limited,
|
|
||||||
* however James would rather have slow correct rendering vs fast broken rendering.
|
|
||||||
*/
|
|
||||||
public class MinecraftGLWrapper implements IMinecraftGLWrapper
|
|
||||||
{
|
|
||||||
public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper();
|
|
||||||
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
private static final StencilState STENCIL;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// scissor //
|
|
||||||
|
|
||||||
/** @see GL32#GL_SCISSOR_TEST */
|
|
||||||
@Override
|
|
||||||
public void enableScissorTest()
|
|
||||||
{
|
|
||||||
GL32.glEnable(GL32.GL_SCISSOR_TEST);
|
|
||||||
GlStateManager._enableScissorTest();
|
|
||||||
}
|
|
||||||
/** @see GL32#GL_SCISSOR_TEST */
|
|
||||||
@Override
|
|
||||||
public void disableScissorTest()
|
|
||||||
{
|
|
||||||
GL32.glDisable(GL32.GL_SCISSOR_TEST);
|
|
||||||
GlStateManager._disableScissorTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// stencil //
|
|
||||||
//
|
|
||||||
// /** @see GL32#GL_SCISSOR_TEST */
|
|
||||||
// public void enableScissorTest() { GlStateManager._stencilFunc(); }
|
|
||||||
// /** @see GL32#GL_SCISSOR_TEST */
|
|
||||||
// public void disableScissorTest() { GlStateManager._disableScissorTest(); }
|
|
||||||
|
|
||||||
|
|
||||||
// depth //
|
|
||||||
|
|
||||||
/** @see GL32#GL_DEPTH_TEST */
|
|
||||||
@Override
|
|
||||||
public void enableDepthTest()
|
|
||||||
{
|
|
||||||
GL32.glEnable(GL32.GL_DEPTH_TEST);
|
|
||||||
GlStateManager._enableDepthTest();
|
|
||||||
}
|
|
||||||
/** @see GL32#GL_DEPTH_TEST */
|
|
||||||
@Override
|
|
||||||
public void disableDepthTest()
|
|
||||||
{
|
|
||||||
GL32.glDisable(GL32.GL_DEPTH_TEST);
|
|
||||||
GlStateManager._disableDepthTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see GL32#glDepthFunc(int) */
|
|
||||||
@Override
|
|
||||||
public void glDepthFunc(int func)
|
|
||||||
{
|
|
||||||
GL32.glDepthFunc(func);
|
|
||||||
GlStateManager._depthFunc(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see GL32#glDepthMask(boolean) */
|
|
||||||
@Override
|
|
||||||
public void enableDepthMask()
|
|
||||||
{
|
|
||||||
GL32.glDepthMask(true);
|
|
||||||
GlStateManager._depthMask(true);
|
|
||||||
}
|
|
||||||
/** @see GL32#glDepthMask(boolean) */
|
|
||||||
@Override
|
|
||||||
public void disableDepthMask()
|
|
||||||
{
|
|
||||||
GL32.glDepthMask(false);
|
|
||||||
GlStateManager._depthMask(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// blending //
|
|
||||||
|
|
||||||
/** @see GL32#GL_BLEND */
|
|
||||||
@Override
|
|
||||||
public void enableBlend()
|
|
||||||
{
|
|
||||||
GL32.glEnable(GL32.GL_BLEND);
|
|
||||||
GlStateManager._enableBlend();
|
|
||||||
}
|
|
||||||
/** @see GL32#GL_BLEND */
|
|
||||||
@Override
|
|
||||||
public void disableBlend()
|
|
||||||
{
|
|
||||||
GL32.glDisable(GL32.GL_BLEND);
|
|
||||||
GlStateManager._disableBlend();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see GL32#glBlendFunc */
|
|
||||||
@Override
|
|
||||||
public void glBlendFunc(int sfactor, int dfactor)
|
|
||||||
{
|
|
||||||
GL32.glBlendFunc(sfactor, dfactor);
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
GlStateManager._blendFunc(sfactor, dfactor);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/** @see GL32#glBlendFuncSeparate */
|
|
||||||
@Override
|
|
||||||
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha)
|
|
||||||
{
|
|
||||||
GL32.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
|
||||||
GlStateManager._blendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// frame buffers //
|
|
||||||
|
|
||||||
/** @see GL32#glBindFramebuffer */
|
|
||||||
@Override
|
|
||||||
public void glBindFramebuffer(int target, int framebuffer)
|
|
||||||
{
|
|
||||||
GL32.glBindFramebuffer(target, framebuffer);
|
|
||||||
GlStateManager._glBindFramebuffer(target, framebuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// buffers //
|
|
||||||
|
|
||||||
/** @see GL32#glGenBuffers() */
|
|
||||||
@Override
|
|
||||||
public int glGenBuffers()
|
|
||||||
{ return GlStateManager._glGenBuffers(); }
|
|
||||||
|
|
||||||
/** @see GL32#glDeleteBuffers(int) */
|
|
||||||
@Override
|
|
||||||
public void glDeleteBuffers(int buffer)
|
|
||||||
{
|
|
||||||
GL32.glDeleteBuffers(buffer);
|
|
||||||
|
|
||||||
// MC's implementation has a bug where it will throw:
|
|
||||||
// GL_INVALID_OPERATION in glBufferData(immutable)
|
|
||||||
// when attempting to delete Storage Buffers
|
|
||||||
// So we need to manually delete the buffers ourselves
|
|
||||||
//GlStateManager._glDeleteBuffers(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// culling //
|
|
||||||
|
|
||||||
/** @see GL32#GL_CULL_FACE */
|
|
||||||
@Override
|
|
||||||
public void enableFaceCulling()
|
|
||||||
{
|
|
||||||
GL32.glEnable(GL32.GL_CULL_FACE);
|
|
||||||
GlStateManager._enableCull();
|
|
||||||
}
|
|
||||||
/** @see GL32#GL_CULL_FACE */
|
|
||||||
@Override
|
|
||||||
public void disableFaceCulling()
|
|
||||||
{
|
|
||||||
GL32.glDisable(GL32.GL_CULL_FACE);
|
|
||||||
GlStateManager._disableCull();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// textures //
|
|
||||||
|
|
||||||
/** @see GL32#glGenTextures() */
|
|
||||||
@Override
|
|
||||||
public int glGenTextures() { return GlStateManager._genTexture(); }
|
|
||||||
/** @see GL32#glDeleteTextures(int) */
|
|
||||||
@Override
|
|
||||||
public void glDeleteTextures(int texture) { GlStateManager._deleteTexture(texture); }
|
|
||||||
|
|
||||||
/** @see GL32#glActiveTexture(int) */
|
|
||||||
@Override
|
|
||||||
public void glActiveTexture(int textureId)
|
|
||||||
{
|
|
||||||
GL32.glActiveTexture(textureId);
|
|
||||||
GlStateManager._activeTexture(textureId);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Always binds to {@link GL32#GL_TEXTURE_2D}
|
|
||||||
* @see GL32#glBindTexture(int, int)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void glBindTexture(int texture)
|
|
||||||
{
|
|
||||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, texture);
|
|
||||||
GlStateManager._bindTexture(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-519
@@ -1,519 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
|
||||||
import com.mojang.blaze3d.platform.NativeImage;
|
|
||||||
import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
import net.minecraft.client.renderer.FogRenderer;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
#else
|
|
||||||
import net.minecraft.client.renderer.fog.FogRenderer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_4
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3f;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.world.effect.MobEffects;
|
|
||||||
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.joml.Vector4f;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
import net.minecraft.tags.FluidTags;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.material.FluidState;
|
|
||||||
import org.lwjgl.opengl.GL15;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.material.FogType;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_21_5
|
|
||||||
import com.mojang.blaze3d.opengl.GlTexture;
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.attribute.EnvironmentAttributes;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A singleton that contains everything
|
|
||||||
* related to rendering in Minecraft.
|
|
||||||
*/
|
|
||||||
public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
|
||||||
{
|
|
||||||
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
|
|
||||||
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
private static final Minecraft MC = Minecraft.getInstance();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
|
|
||||||
* Requiring the use of multiple {@link LightMapWrapper}.
|
|
||||||
*/
|
|
||||||
public ConcurrentHashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the render buffer that should be used when displaying levels to the screen.
|
|
||||||
* This is used for Optifine shader support so we can render directly to Optifine's level frame buffer.
|
|
||||||
*/
|
|
||||||
public int finalLevelFrameBufferId = -1;
|
|
||||||
|
|
||||||
public boolean colorTextureCastFailLogged = false;
|
|
||||||
public boolean depthTextureCastFailLogged = false;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_6
|
|
||||||
#else
|
|
||||||
private static FogRenderer mcFogRenderer = null;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// methods //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3f getLookAtVector()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
Camera camera = MC.gameRenderer.getMainCamera();
|
|
||||||
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
|
|
||||||
#else
|
|
||||||
Camera camera = MC.gameRenderer.getMainCamera();
|
|
||||||
return new Vec3f(camera.forwardVector().x(), camera.forwardVector().y(), camera.forwardVector().z());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unless you really need to know if the player is blind,
|
|
||||||
* use {@link MinecraftRenderWrapper#isFogStateSpecial()} or {@link IMinecraftRenderWrapper#isFogStateSpecial()} instead
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean playerHasBlindingEffect()
|
|
||||||
{
|
|
||||||
if (MC.player == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (MC.player.getActiveEffectsMap() == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null
|
|
||||||
#if MC_VER >= MC_1_19_2
|
|
||||||
|| MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3d getCameraExactPosition()
|
|
||||||
{
|
|
||||||
Camera camera = MC.gameRenderer.getMainCamera();
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
Vec3 projectedView = camera.getPosition();
|
|
||||||
#else
|
|
||||||
Vec3 projectedView = camera.position();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getFogColor(float partialTicks)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
float[] colorValues = new float[4];
|
|
||||||
GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues);
|
|
||||||
return new Color(
|
|
||||||
Math.max(0f, Math.min(colorValues[0], 1f)), // r
|
|
||||||
Math.max(0f, Math.min(colorValues[1], 1f)), // g
|
|
||||||
Math.max(0f, Math.min(colorValues[2], 1f)), // b
|
|
||||||
Math.max(0f, Math.min(colorValues[3], 1f)) // a
|
|
||||||
);
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
FogRenderer.setupColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
|
|
||||||
float[] colorValues = RenderSystem.getShaderFogColor();
|
|
||||||
return new Color(
|
|
||||||
Math.max(0f, Math.min(colorValues[0], 1f)), // r
|
|
||||||
Math.max(0f, Math.min(colorValues[1], 1f)), // g
|
|
||||||
Math.max(0f, Math.min(colorValues[2], 1f)), // b
|
|
||||||
Math.max(0f, Math.min(colorValues[3], 1f)) // a
|
|
||||||
);
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
Vector4f colorValues = FogRenderer.computeFogColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
|
|
||||||
return new Color(
|
|
||||||
Math.max(0f, Math.min(colorValues.x, 1f)), // r
|
|
||||||
Math.max(0f, Math.min(colorValues.y, 1f)), // g
|
|
||||||
Math.max(0f, Math.min(colorValues.z, 1f)), // b
|
|
||||||
Math.max(0f, Math.min(colorValues.w, 1f)) // a
|
|
||||||
);
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
if (mcFogRenderer == null)
|
|
||||||
{
|
|
||||||
mcFogRenderer = new FogRenderer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MC.level == null)
|
|
||||||
{
|
|
||||||
// shouldn't happen, but just in case
|
|
||||||
return Color.white;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isFoggy =
|
|
||||||
MC.level.effects().isFoggyAt(
|
|
||||||
MC.gameRenderer.getMainCamera().getBlockPosition().getX(),
|
|
||||||
MC.gameRenderer.getMainCamera().getBlockPosition().getZ())
|
|
||||||
|| MC.gui.getBossOverlay().shouldCreateWorldFog();
|
|
||||||
Vector4f colorValues = mcFogRenderer.setupFog(MC.gameRenderer.getMainCamera(), MC.options.getEffectiveRenderDistance(), isFoggy, MC.deltaTracker, MC.gameRenderer.getDarkenWorldAmount(MC.deltaTracker.getGameTimeDeltaPartialTick(true)), MC.level);
|
|
||||||
return new Color(
|
|
||||||
Math.max(0f, Math.min(colorValues.x, 1f)), // r
|
|
||||||
Math.max(0f, Math.min(colorValues.y, 1f)), // g
|
|
||||||
Math.max(0f, Math.min(colorValues.z, 1f)), // b
|
|
||||||
Math.max(0f, Math.min(colorValues.w, 1f)) // a
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
|
|
||||||
if (mcFogRenderer == null)
|
|
||||||
{
|
|
||||||
mcFogRenderer = new FogRenderer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MC.level == null)
|
|
||||||
{
|
|
||||||
// shouldn't happen, but just in case
|
|
||||||
return Color.white;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector4f colorValues = mcFogRenderer.setupFog(
|
|
||||||
MC.gameRenderer.getMainCamera(),
|
|
||||||
MC.options.getEffectiveRenderDistance(),
|
|
||||||
MC.deltaTracker,
|
|
||||||
MC.gameRenderer.getDarkenWorldAmount(MC.deltaTracker.getGameTimeDeltaPartialTick(true)),
|
|
||||||
MC.level);
|
|
||||||
return new Color(
|
|
||||||
Math.max(0f, Math.min(colorValues.x, 1f)), // r
|
|
||||||
Math.max(0f, Math.min(colorValues.y, 1f)), // g
|
|
||||||
Math.max(0f, Math.min(colorValues.z, 1f)), // b
|
|
||||||
Math.max(0f, Math.min(colorValues.w, 1f)) // a
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getSkyColor()
|
|
||||||
{
|
|
||||||
if (MC.level.dimensionType().hasSkyLight())
|
|
||||||
{
|
|
||||||
float frameTime;
|
|
||||||
#if MC_VER < MC_1_21_1
|
|
||||||
frameTime = MC.getFrameTime();
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
frameTime = MC.getTimer().getRealtimeDeltaTicks();
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
frameTime = MC.deltaTracker.getGameTimeDeltaTicks();
|
|
||||||
#else
|
|
||||||
frameTime = 0f; // unused
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime);
|
|
||||||
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
|
|
||||||
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
int argbColorInt = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
|
|
||||||
return ColorUtil.toColorObjARGB(argbColorInt);
|
|
||||||
#else
|
|
||||||
int argbColor = MC.level.environmentAttributes().getValue(EnvironmentAttributes.SKY_COLOR, BlockPos.ZERO);
|
|
||||||
return new Color(ColorUtil.getRed(argbColor), ColorUtil.getGreen(argbColor), ColorUtil.getBlue(argbColor), 255 /* ignore alpha since DH clouds don't render correctly with transparency */);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new Color(0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getFov(float partialTicks) { return MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true); }
|
|
||||||
|
|
||||||
/** Measured in chunks */
|
|
||||||
@Override
|
|
||||||
public int getRenderDistance()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
//FIXME: How to resolve this?
|
|
||||||
return MC.options.renderDistance;
|
|
||||||
#else
|
|
||||||
return MC.options.getEffectiveRenderDistance();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
protected RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean mcRendersToFrameBuffer()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean runningLegacyOpenGL()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_16_5
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTargetFramebuffer()
|
|
||||||
{
|
|
||||||
// used so we can access the framebuffer shaders end up rendering to
|
|
||||||
if (AbstractOptifineAccessor.optifinePresent())
|
|
||||||
{
|
|
||||||
return this.finalLevelFrameBufferId;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return this.getRenderTarget().frameBufferId;
|
|
||||||
#else
|
|
||||||
// MC renders to a texture and then directly to the default FBO now
|
|
||||||
// we need to draw to their texture instead of the FBO
|
|
||||||
return 0; // 0 is the ID for the default frame buffer
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearTargetFrameBuffer() { this.finalLevelFrameBufferId = -1; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDepthTextureId()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return this.getRenderTarget().getDepthTextureId();
|
|
||||||
#else
|
|
||||||
try
|
|
||||||
{
|
|
||||||
GlTexture glTexture = (GlTexture) this.getRenderTarget().getDepthTexture();
|
|
||||||
if (glTexture == null)
|
|
||||||
{
|
|
||||||
// shouldn't happen, but just in case
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return glTexture.glId();
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// only log this error once per session
|
|
||||||
if (!this.depthTextureCastFailLogged)
|
|
||||||
{
|
|
||||||
this.depthTextureCastFailLogged = true;
|
|
||||||
LOGGER.error("Unable to cast render Target depth texture to GlTexture. MC or a rendering mod may have changed the object type.", e);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int getColorTextureId()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return this.getRenderTarget().getColorTextureId();
|
|
||||||
#else
|
|
||||||
try
|
|
||||||
{
|
|
||||||
GlTexture glTexture = (GlTexture) this.getRenderTarget().getColorTexture();
|
|
||||||
if (glTexture == null)
|
|
||||||
{
|
|
||||||
// shouldn't happen, but just in case
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return glTexture.glId();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// only log this error once per session
|
|
||||||
if (!this.colorTextureCastFailLogged)
|
|
||||||
{
|
|
||||||
this.colorTextureCastFailLogged = true;
|
|
||||||
LOGGER.error("Unable to cast render Target color texture to GlTexture. MC or a rendering mod may have changed the object type.", e);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTargetFramebufferViewportWidth()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
return this.getRenderTarget().viewWidth;
|
|
||||||
#else
|
|
||||||
return this.getRenderTarget().width;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTargetFramebufferViewportHeight()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
return this.getRenderTarget().viewHeight;
|
|
||||||
#else
|
|
||||||
return this.getRenderTarget().height;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFogStateSpecial()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
|
|
||||||
FluidState fluidState = camera.getFluidInCamera();
|
|
||||||
Entity entity = camera.getEntity();
|
|
||||||
boolean isBlind = this.playerHasBlindingEffect();
|
|
||||||
isBlind |= fluidState.is(FluidTags.WATER);
|
|
||||||
isBlind |= fluidState.is(FluidTags.LAVA);
|
|
||||||
return isBlind;
|
|
||||||
#else
|
|
||||||
boolean isBlind = this.playerHasBlindingEffect();
|
|
||||||
return MC.gameRenderer.getMainCamera().getFluidInCamera() != FogType.NONE || isBlind;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* It's better to use {@link MinecraftRenderWrapper#setLightmapId(int, IClientLevelWrapper)} if possible,
|
|
||||||
* however old MC versions don't support it.
|
|
||||||
*/
|
|
||||||
public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level)
|
|
||||||
{
|
|
||||||
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
|
|
||||||
// object for the same MC level and/or the same hash,
|
|
||||||
// so this will have to do for now
|
|
||||||
IDimensionTypeWrapper dimensionType = level.getDimensionType();
|
|
||||||
|
|
||||||
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
|
|
||||||
wrapper.uploadLightmap(lightPixels);
|
|
||||||
}
|
|
||||||
public void setLightmapId(int tetxureId, IClientLevelWrapper level)
|
|
||||||
{
|
|
||||||
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
|
|
||||||
// object for the same MC level and/or the same hash,
|
|
||||||
// so this will have to do for now
|
|
||||||
IDimensionTypeWrapper dimensionType = level.getDimensionType();
|
|
||||||
|
|
||||||
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
|
|
||||||
wrapper.setLightmapId(tetxureId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getShade(EDhDirection lodDirection)
|
|
||||||
{
|
|
||||||
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
|
|
||||||
switch (lodShading)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case AUTO:
|
|
||||||
if (MC.level != null)
|
|
||||||
{
|
|
||||||
Direction mcDir = McObjectConverter.Convert(lodDirection);
|
|
||||||
return MC.level.getShade(mcDir, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ENABLED:
|
|
||||||
switch (lodDirection)
|
|
||||||
{
|
|
||||||
case DOWN:
|
|
||||||
return 0.5F;
|
|
||||||
default:
|
|
||||||
case UP:
|
|
||||||
return 1.0F;
|
|
||||||
case NORTH:
|
|
||||||
case SOUTH:
|
|
||||||
return 0.8F;
|
|
||||||
case WEST:
|
|
||||||
case EAST:
|
|
||||||
return 0.6F;
|
|
||||||
}
|
|
||||||
|
|
||||||
case DISABLED:
|
|
||||||
return 1.0F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-62
@@ -1,62 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
|
||||||
import net.minecraft.server.dedicated.DedicatedServer;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class MinecraftServerWrapper implements IMinecraftSharedWrapper
|
|
||||||
{
|
|
||||||
public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper();
|
|
||||||
|
|
||||||
/** set during server startup */
|
|
||||||
@Nullable
|
|
||||||
public DedicatedServer dedicatedServer = null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
private MinecraftServerWrapper() { }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// methods //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDedicatedServer() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getInstallationDirectory()
|
|
||||||
{
|
|
||||||
if (this.dedicatedServer == null)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Trying to get Installation Direction before dedicated server completed initialization!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_1
|
|
||||||
return this.dedicatedServer.getServerDirectory();
|
|
||||||
#else
|
|
||||||
return this.dedicatedServer.getServerDirectory().toFile();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPlayerCount()
|
|
||||||
{
|
|
||||||
if (this.dedicatedServer == null)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Trying to get player count before dedicated server completed initialization!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dedicatedServer.getPlayerCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
|
||||||
|
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author James Seibel
|
|
||||||
* @version 11-20-2021
|
|
||||||
*/
|
|
||||||
public class ProfilerWrapper implements IProfilerWrapper
|
|
||||||
{
|
|
||||||
public ProfilerFiller profiler;
|
|
||||||
|
|
||||||
public ProfilerWrapper(ProfilerFiller newProfiler) { this.profiler = newProfiler; }
|
|
||||||
|
|
||||||
|
|
||||||
/** starts a new section inside the currently running section */
|
|
||||||
@Override
|
|
||||||
public void push(String newSection) { this.profiler.push(newSection); }
|
|
||||||
|
|
||||||
/** ends the currently running section and starts a new one */
|
|
||||||
@Override
|
|
||||||
public void popPush(String newSection) { this.profiler.popPush(newSection); }
|
|
||||||
|
|
||||||
/** ends the currently running section */
|
|
||||||
@Override
|
|
||||||
public void pop() { this.profiler.pop(); }
|
|
||||||
|
|
||||||
}
|
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
|
||||||
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public interface IMixinServerPlayer
|
|
||||||
{
|
|
||||||
@Nullable
|
|
||||||
ServerLevel distantHorizons$getDimensionChangeDestination();
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5
|
|
||||||
void distantHorizons$setDimensionChangeDestination(ServerLevel dimensionChangeDestination);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
-112
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.NativeImage;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import org.lwjgl.opengl.GL32;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
public class LightMapWrapper implements ILightMapWrapper
|
|
||||||
{
|
|
||||||
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
private int textureId = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// constructors //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
public LightMapWrapper() { }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
|
||||||
// lightmap syncing //
|
|
||||||
//==================//
|
|
||||||
|
|
||||||
public void uploadLightmap(NativeImage image)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
int currentTexture = GLMC.getActiveTexture();
|
|
||||||
if (this.textureId == 0)
|
|
||||||
{
|
|
||||||
this.createLightmap(image);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GLMC.glBindTexture(this.textureId);
|
|
||||||
}
|
|
||||||
image.upload(0, 0, 0, false);
|
|
||||||
|
|
||||||
// getActiveTexture() may return textures that aren't valid and attempting to bind them will
|
|
||||||
// throw a GL error in MC 1.21.1
|
|
||||||
if (GL32.glIsTexture(currentTexture))
|
|
||||||
{
|
|
||||||
GLMC.glBindTexture(currentTexture);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
throw new UnsupportedOperationException("setLightmapId should be used for MC versions after 1.21.5"); // TODO that MC version number is wrong, when did we actually start using setLightmapId()?
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
private void createLightmap(NativeImage image)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
this.textureId = GLMC.glGenTextures();
|
|
||||||
GLMC.glBindTexture(this.textureId);
|
|
||||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
|
|
||||||
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
|
||||||
#else
|
|
||||||
throw new UnsupportedOperationException("setLightmapId should be used for MC versions after 1.21.5"); // TODO that MC version number is wrong, when did we actually start using setLightmapId()?
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLightmapId(int minecraftLightmapTetxureId)
|
|
||||||
{
|
|
||||||
// just use the MC texture ID
|
|
||||||
this.textureId = minecraftLightmapTetxureId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// lightmap use //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bind()
|
|
||||||
{
|
|
||||||
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
|
|
||||||
GLMC.glBindTexture(this.textureId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unbind() { GLMC.glBindTexture(0); }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
|
|
||||||
public class MutableBlockPosWrapper implements IMutableBlockPosWrapper
|
|
||||||
{
|
|
||||||
public final BlockPos.MutableBlockPos pos;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public MutableBlockPosWrapper()
|
|
||||||
{
|
|
||||||
this.pos = new BlockPos.MutableBlockPos();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
|
||||||
// overrides //
|
|
||||||
//===========//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getWrappedMcObject() { return this.pos; }
|
|
||||||
|
|
||||||
}
|
|
||||||
-104
@@ -1,104 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.misc;
|
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
import com.google.common.collect.MapMaker;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This wrapper transparently ensures that the underlying {@link ServerPlayer} is always valid,
|
|
||||||
* unless the player has disconnected.
|
|
||||||
*/
|
|
||||||
public class ServerPlayerWrapper implements IServerPlayerWrapper
|
|
||||||
{
|
|
||||||
private static final ConcurrentMap<ServerGamePacketListenerImpl, ServerPlayerWrapper> serverPlayerWrapperMap = new MapMaker().weakKeys().weakValues().makeMap();
|
|
||||||
|
|
||||||
private final ServerGamePacketListenerImpl connection;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
|
|
||||||
{ return serverPlayerWrapperMap.computeIfAbsent(serverPlayer.connection, ignored -> new ServerPlayerWrapper(serverPlayer.connection)); }
|
|
||||||
|
|
||||||
private ServerPlayerWrapper(ServerGamePacketListenerImpl connection) { this.connection = connection; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// getters //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
private ServerPlayer getServerPlayer() { return this.connection.player; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() { return this.getServerPlayer().getName().getString(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IServerLevelWrapper getLevel()
|
|
||||||
{
|
|
||||||
ServerLevel level = ((IMixinServerPlayer) this.getServerPlayer()).distantHorizons$getDimensionChangeDestination();
|
|
||||||
if (level == null)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
level = this.getServerPlayer().getLevel();
|
|
||||||
#elif MC_VER < MC_1_21_6
|
|
||||||
level = this.getServerPlayer().serverLevel();
|
|
||||||
#else
|
|
||||||
level = this.getServerPlayer().level();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return ServerLevelWrapper.getWrapper(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3d getPosition()
|
|
||||||
{
|
|
||||||
Vec3 position = this.getServerPlayer().position();
|
|
||||||
return new Vec3d(position.x, position.y, position.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getWrappedMcObject() { return this.getServerPlayer(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return "Wrapped{" + this.getServerPlayer() + "}"; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj)
|
|
||||||
{
|
|
||||||
if (this == obj)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!(obj instanceof ServerPlayerWrapper))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ServerPlayerWrapper that = (ServerPlayerWrapper) obj;
|
|
||||||
return Objects.equal(this.connection, that.connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() { return Objects.hashCode(this.connection); }
|
|
||||||
|
|
||||||
}
|
|
||||||
-430
@@ -1,430 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.world;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|
||||||
import com.seibel.distanthorizons.core.level.*;
|
|
||||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_4
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
#else
|
|
||||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.attribute.EnvironmentAttributes;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
public class ClientLevelWrapper implements IClientLevelWrapper
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
/**
|
|
||||||
* weak references are to prevent rare issues
|
|
||||||
* where, upon world closure, some levels aren't shutdown/removed properly
|
|
||||||
* and/or for servers were the level object isn't consistent
|
|
||||||
*/
|
|
||||||
private static final Map<ClientLevel, WeakReference<ClientLevelWrapper>> LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
|
|
||||||
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
|
||||||
|
|
||||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
|
||||||
|
|
||||||
private final ClientLevel level;
|
|
||||||
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockColorCacheByBlockState = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/** cached method reference to reduce GC overhead */
|
|
||||||
private final Function<BlockState, ClientBlockStateColorCache> createCachedBlockColorCacheFunc = (blockState) -> new ClientBlockStateColorCache(blockState, this);
|
|
||||||
|
|
||||||
|
|
||||||
private boolean cloudColorFailLogged = false;
|
|
||||||
|
|
||||||
private BlockStateWrapper dirtBlockWrapper;
|
|
||||||
private IDhLevel dhLevel;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
protected ClientLevelWrapper(ClientLevel level) { this.level = level; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
|
||||||
// instance methods //
|
|
||||||
//==================//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* can be used when speed is important and the same level is likely to be passed in,
|
|
||||||
* IE rendering.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static IClientLevelWrapper getWrapperIfDifferent(@Nullable IClientLevelWrapper levelWrapper, @NotNull ClientLevel level) // TODO handle null level
|
|
||||||
{
|
|
||||||
if (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != levelWrapper)
|
|
||||||
{
|
|
||||||
return getWrapper(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientLevelWrapper clientLevelWrapper = (ClientLevelWrapper)levelWrapper;
|
|
||||||
if (clientLevelWrapper == null
|
|
||||||
|| clientLevelWrapper.level != level)
|
|
||||||
{
|
|
||||||
return getWrapper(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientLevelWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level, boolean bypassLevelKeyManager)
|
|
||||||
{
|
|
||||||
if (!bypassLevelKeyManager)
|
|
||||||
{
|
|
||||||
if (level == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// used if the client is connected to a server that defines the currently loaded level
|
|
||||||
IServerKeyedClientLevel overrideLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
|
|
||||||
if (overrideLevel != null)
|
|
||||||
{
|
|
||||||
return overrideLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WeakReference<ClientLevelWrapper> levelRef = LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.get(level);
|
|
||||||
if (levelRef != null)
|
|
||||||
{
|
|
||||||
ClientLevelWrapper levelWrapper = levelRef.get();
|
|
||||||
if (levelWrapper != null)
|
|
||||||
{
|
|
||||||
return levelWrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.compute(level, (newLevel, newLevelRef) ->
|
|
||||||
{
|
|
||||||
if (newLevelRef != null)
|
|
||||||
{
|
|
||||||
ClientLevelWrapper oldLevelWrapper = newLevelRef.get();
|
|
||||||
if (oldLevelWrapper != null)
|
|
||||||
{
|
|
||||||
return newLevelRef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new WeakReference<>(new ClientLevelWrapper(newLevel));
|
|
||||||
}).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public IServerLevelWrapper tryGetServerSideWrapper()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// this method only makes sense if we are running a single-player server
|
|
||||||
if (MINECRAFT.getSingleplayerServer() == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
|
|
||||||
|
|
||||||
// attempt to find the server level with the same dimension type
|
|
||||||
// Note: this assumes only one level per dimension type, multiverse servers may not behave correctly
|
|
||||||
ServerLevelWrapper foundLevelWrapper = null;
|
|
||||||
for (ServerLevel serverLevel : serverLevels)
|
|
||||||
{
|
|
||||||
if (serverLevel.dimension() == this.level.dimension())
|
|
||||||
{
|
|
||||||
foundLevelWrapper = ServerLevelWrapper.getWrapper(serverLevel);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return foundLevelWrapper;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Failed to get server side wrapper for client level: " + this.level);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//====================//
|
|
||||||
// base level methods //
|
|
||||||
//====================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockColor(DhBlockPos blockPos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockWrapper)
|
|
||||||
{
|
|
||||||
ClientBlockStateColorCache blockColorCache = this.blockColorCacheByBlockState.get(((BlockStateWrapper) blockWrapper).blockState);
|
|
||||||
if (blockColorCache == null)
|
|
||||||
{
|
|
||||||
blockColorCache = this.blockColorCacheByBlockState.computeIfAbsent(
|
|
||||||
((BlockStateWrapper) blockWrapper).blockState,
|
|
||||||
this.createCachedBlockColorCacheFunc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return blockColorCache.getColor((BiomeWrapper) biome, fullDataSource, blockPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDirtBlockColor()
|
|
||||||
{
|
|
||||||
if (this.dirtBlockWrapper == null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.dirtBlockWrapper = (BlockStateWrapper) BlockStateWrapper.deserialize(BlockStateWrapper.DIRT_RESOURCE_LOCATION_STRING, this);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
// shouldn't happen, but just in case
|
|
||||||
LOGGER.warn("Unable to get dirt color with resource location ["+BlockStateWrapper.DIRT_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.dirtBlockWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearBlockColorCache() { this.blockColorCacheByBlockState.clear(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IDimensionTypeWrapper getDimensionType()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
|
|
||||||
#else
|
|
||||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDimensionName()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
return this.level.dimension().location().toString();
|
|
||||||
#else
|
|
||||||
return this.level.dimension().identifier().toString();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDhIdentifier() { return this.getHashedSeedEncoded() + "@" + this.getDimensionName(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
|
|
||||||
|
|
||||||
public ClientLevel getLevel() { return this.level; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxHeight() { return this.level.getHeight(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinHeight()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
return 0;
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
return this.level.getMinBuildHeight();
|
|
||||||
#else
|
|
||||||
return this.level.getMinY();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientLevel getWrappedMcObject() { return this.level; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUnload()
|
|
||||||
{
|
|
||||||
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level);
|
|
||||||
this.dhLevel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getDhSaveFolder()
|
|
||||||
{
|
|
||||||
if (this.dhLevel == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dhLevel.getSaveStructure().getSaveFolder(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
|
||||||
// generic rendering //
|
|
||||||
//===================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
|
|
||||||
@Override
|
|
||||||
public IDhLevel getDhLevel() { return this.dhLevel; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IDhApiCustomRenderRegister getRenderRegister()
|
|
||||||
{
|
|
||||||
if (this.dhLevel == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dhLevel.getGenericRenderer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getCloudColor(float tickDelta)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
Vec3 colorVec3 = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
colorVec3 = this.level.getCloudColor(tickDelta);
|
|
||||||
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// extra logging is due to some mods returning weird values, this way we can track down the issue better
|
|
||||||
if (!this.cloudColorFailLogged)
|
|
||||||
{
|
|
||||||
this.cloudColorFailLogged = true;
|
|
||||||
|
|
||||||
String colorString = "NULL";
|
|
||||||
if (colorVec3 != null)
|
|
||||||
{
|
|
||||||
colorString = "r["+(float)colorVec3.x+"] g["+(float)colorVec3.y+"] b["+(float)colorVec3.z+"]";
|
|
||||||
}
|
|
||||||
LOGGER.warn("Failed to get cloud color for ["+this.getDhIdentifier()+"]. vec3 ["+colorString+"], error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// default to white if there's an issue
|
|
||||||
return Color.WHITE;
|
|
||||||
}
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
int argbColor = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
argbColor = this.level.getCloudColor(tickDelta);
|
|
||||||
return ColorUtil.toColorObjARGB(argbColor);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// extra logging is due to some mods returning weird values, this way we can track down the issue better
|
|
||||||
if (!this.cloudColorFailLogged)
|
|
||||||
{
|
|
||||||
this.cloudColorFailLogged = true;
|
|
||||||
LOGGER.warn("Failed to get cloud color for ["+this.getDhIdentifier()+"]. Int ["+argbColor+"], col ["+ColorUtil.toString(argbColor)+"], error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// default to white if there's an issue
|
|
||||||
return Color.WHITE;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int argbColor = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
argbColor = this.level.environmentAttributes().getValue(EnvironmentAttributes.CLOUD_COLOR, BlockPos.ZERO);
|
|
||||||
return new Color(ColorUtil.getRed(argbColor), ColorUtil.getGreen(argbColor), ColorUtil.getBlue(argbColor), 255 /* ignore alpha since DH clouds don't render correctly with transparency */);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// extra logging is due to some mods returning weird values, this way we can track down the issue better
|
|
||||||
if (!this.cloudColorFailLogged)
|
|
||||||
{
|
|
||||||
this.cloudColorFailLogged = true;
|
|
||||||
LOGGER.warn("Failed to get cloud color for ["+this.getDhIdentifier()+"]. Int ["+argbColor+"], col ["+ColorUtil.toString(argbColor)+"], error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// default to white if there's an issue
|
|
||||||
return Color.WHITE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
if (this.level == null)
|
|
||||||
{
|
|
||||||
return "Wrapped{null}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-147
@@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.world;
|
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
|
||||||
|
|
||||||
public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
|
||||||
{
|
|
||||||
private static final ConcurrentMap<String, DimensionTypeWrapper> DIMENSION_WRAPPER_BY_NAME = new ConcurrentHashMap<>();
|
|
||||||
private final DimensionType dimensionType;
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// Constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
public DimensionTypeWrapper(DimensionType dimensionType)
|
|
||||||
#else
|
|
||||||
public DimensionTypeWrapper(DimensionType dimensionType, String name)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
this.dimensionType = dimensionType;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
this.name = determineName(dimensionType);
|
|
||||||
#else
|
|
||||||
this.name = name;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
|
|
||||||
#else
|
|
||||||
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType, String name)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
String dimName = determineName(dimensionType);
|
|
||||||
#else
|
|
||||||
String dimName = name;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// check if the dimension has already been wrapped
|
|
||||||
if (DIMENSION_WRAPPER_BY_NAME.containsKey(dimName)
|
|
||||||
&& DIMENSION_WRAPPER_BY_NAME.get(dimName) != null)
|
|
||||||
{
|
|
||||||
return DIMENSION_WRAPPER_BY_NAME.get(dimName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// create the missing wrapper
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
|
|
||||||
#else
|
|
||||||
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType, dimName);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper);
|
|
||||||
return dimensionTypeWrapper;
|
|
||||||
}
|
|
||||||
private static String determineName(DimensionType dimensionType)
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_16_5
|
|
||||||
// effectsLocation() is marked as client only, so using the backing field directly
|
|
||||||
return dimensionType.effectsLocation.getPath();
|
|
||||||
#elif MC_VER <= MC_1_21_10
|
|
||||||
return dimensionType.effectsLocation().getPath();
|
|
||||||
#else
|
|
||||||
throw new UnsupportedOperationException("As of MC 1.21.11 the dimension type no longer stores it's name and must be determined from the level.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// wrapper methods //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() { return this.name; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasCeiling() { return this.dimensionType.hasCeiling(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSkyLight() { return this.dimensionType.hasSkyLight(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getWrappedMcObject() { return this.dimensionType; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getCoordinateScale() { return this.dimensionType.coordinateScale(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj)
|
|
||||||
{
|
|
||||||
if (obj.getClass() != DimensionTypeWrapper.class)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DimensionTypeWrapper other = (DimensionTypeWrapper) obj;
|
|
||||||
return other.getName().equals(this.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-271
@@ -1,271 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.world;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
|
||||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkSource;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_4
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class ServerLevelWrapper implements IServerLevelWrapper
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
/**
|
|
||||||
* weak references are to prevent rare issues
|
|
||||||
* where, upon world closure, some levels aren't shutdown/removed properly
|
|
||||||
*/
|
|
||||||
private static final Map<ServerLevel, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
|
|
||||||
|
|
||||||
private final ServerLevel level;
|
|
||||||
private IDhLevel dhLevel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this name is cached to prevent issues during shutdown where
|
|
||||||
* the server variables needed may no longer be available.
|
|
||||||
*/
|
|
||||||
private final String KeyedLevelDimensionName;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// constructors //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
public static ServerLevelWrapper getWrapper(ServerLevel level)
|
|
||||||
{
|
|
||||||
return LEVEL_WRAPPER_REF_BY_SERVER_LEVEL.compute(level, (newLevel, levelRef) ->
|
|
||||||
{
|
|
||||||
if (levelRef != null)
|
|
||||||
{
|
|
||||||
ServerLevelWrapper oldLevelWrapper = levelRef.get();
|
|
||||||
if (oldLevelWrapper != null)
|
|
||||||
{
|
|
||||||
return levelRef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new WeakReference<>(new ServerLevelWrapper(newLevel));
|
|
||||||
}).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServerLevelWrapper(ServerLevel level)
|
|
||||||
{
|
|
||||||
this.level = level;
|
|
||||||
this.KeyedLevelDimensionName = this.createKeyedLevelDimensionName();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
|
||||||
// instance methods //
|
|
||||||
//==================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getMcSaveFolder()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
return this.level.getChunkSource().getDataStorage().dataFolder;
|
|
||||||
#else
|
|
||||||
return this.level.getChunkSource().getDataStorage().dataFolder.toFile();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getKeyedLevelDimensionName() { return this.KeyedLevelDimensionName; }
|
|
||||||
|
|
||||||
private String createKeyedLevelDimensionName()
|
|
||||||
{
|
|
||||||
String dimensionName = this.getDhIdentifier();
|
|
||||||
|
|
||||||
if (Config.Server.sendLevelKeys.get())
|
|
||||||
{
|
|
||||||
String levelKeyPrefix = Config.Server.levelKeyPrefix.get();
|
|
||||||
|
|
||||||
if (SharedApi.getEnvironment() == EWorldEnvironment.CLIENT_SERVER)
|
|
||||||
{
|
|
||||||
String cleanWorldFolderName = this.getWorldFolderName()
|
|
||||||
.replaceAll("[^" + LevelInitMessage.ALLOWED_CHARS_REGEX + " ]", "")
|
|
||||||
.replaceAll(" ", "_");
|
|
||||||
|
|
||||||
levelKeyPrefix += (!levelKeyPrefix.isEmpty() ? "_" : "") + cleanWorldFolderName
|
|
||||||
+ "_" + this.getHashedSeedEncoded();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (levelKeyPrefix.isEmpty())
|
|
||||||
{
|
|
||||||
levelKeyPrefix = this.getHashedSeedEncoded();
|
|
||||||
}
|
|
||||||
|
|
||||||
String mainPart = "@" + dimensionName;
|
|
||||||
|
|
||||||
return levelKeyPrefix.substring(0, Math.min(
|
|
||||||
LevelInitMessage.MAX_LENGTH - mainPart.length(),
|
|
||||||
levelKeyPrefix.length()
|
|
||||||
)) + mainPart;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dimensionName;
|
|
||||||
}
|
|
||||||
private String getWorldFolderName()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// We use the overworld since it's the only dimension that is stored in the server root folder
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_21_3
|
|
||||||
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParent().getFileName().toString();
|
|
||||||
#else // <= 1.21.3
|
|
||||||
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParentFile().getName();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Unable to get world folder name. LODs may not load or save correctly. Error: ["+e.getMessage()+"].", e);
|
|
||||||
return "unknown_world";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DimensionTypeWrapper getDimensionType()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
|
|
||||||
#else
|
|
||||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDimensionName()
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_21_10
|
|
||||||
return this.level.dimension().location().toString();
|
|
||||||
#else
|
|
||||||
return this.level.dimension().identifier().toString();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDhIdentifier() { return this.getDimensionName(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; }
|
|
||||||
|
|
||||||
public ServerLevel getLevel() { return this.level; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxHeight() { return this.level.getHeight(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinHeight()
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
return 0;
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
return this.level.getMinBuildHeight();
|
|
||||||
#else
|
|
||||||
return this.level.getMinY();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServerLevel getWrappedMcObject() { return this.level; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUnload() { LEVEL_WRAPPER_REF_BY_SERVER_LEVEL.remove(this.level); }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public IDhLevel getDhLevel() { return this.dhLevel; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IDhApiCustomRenderRegister getRenderRegister()
|
|
||||||
{
|
|
||||||
if (this.dhLevel == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dhLevel.getGenericRenderer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getDhSaveFolder()
|
|
||||||
{
|
|
||||||
if (this.dhLevel == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dhLevel.getSaveStructure().getSaveFolder(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}"; }
|
|
||||||
|
|
||||||
}
|
|
||||||
-804
@@ -1,804 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 Tom Lee (TomTheFurry)
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.seibel.distanthorizons.api.DhApi;
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.*;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
|
||||||
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
|
||||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
|
||||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
|
||||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepBiomes;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepFeatures;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepNoise;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStructureReference;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStructureStart;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepSurface;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.*;
|
|
||||||
import net.minecraft.world.level.levelgen.DebugLevelSource;
|
|
||||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
|
||||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_17_1
|
|
||||||
#elif MC_VER <= MC_1_19_2
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
#elif MC_VER <= MC_1_19_4
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
#elif MC_VER < MC_1_21_9
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_4
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironmentWrapper
|
|
||||||
{
|
|
||||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
|
||||||
.name("LOD World Gen")
|
|
||||||
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
|
|
||||||
.name("LOD World Gen")
|
|
||||||
.maxCountPerSecond(1)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static final ImmutableMap<EDhApiWorldGenerationStep, Integer> WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP;
|
|
||||||
public static final int MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
|
|
||||||
|
|
||||||
public static final long EXCEPTION_TIMER_RESET_TIME = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
|
|
||||||
public static final int EXCEPTION_COUNTER_TRIGGER = 20;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to revert the ignore logic in {@link SharedApi} so
|
|
||||||
* that a given chunk pos can be handled again.
|
|
||||||
* A timer is used so we don't have to inject into MC's code and it works sell enough
|
|
||||||
* most of the time.
|
|
||||||
* If a chunk does get through due the timeout not being long enough that isn't the end of the world.
|
|
||||||
*/
|
|
||||||
private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final IDhServerLevel dhServerLevel;
|
|
||||||
|
|
||||||
public final InternalServerGenerator internalServerGenerator;
|
|
||||||
public final ChunkFileReader chunkFileReader;
|
|
||||||
|
|
||||||
private final Timer chunkSaveIgnoreTimer = TimerUtil.CreateTimer("ChunkSaveIgnoreTimer");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public final LinkedBlockingQueue<GenerationEvent> generationEventQueue = new LinkedBlockingQueue<>();
|
|
||||||
public final GlobalWorldGenParams globalParams;
|
|
||||||
|
|
||||||
public final StepStructureStart stepStructureStart = new StepStructureStart(this);
|
|
||||||
public final StepStructureReference stepStructureReference = new StepStructureReference(this);
|
|
||||||
public final StepBiomes stepBiomes = new StepBiomes(this);
|
|
||||||
public final StepNoise stepNoise = new StepNoise(this);
|
|
||||||
public final StepSurface stepSurface = new StepSurface(this);
|
|
||||||
public final StepFeatures stepFeatures = new StepFeatures(this);
|
|
||||||
|
|
||||||
public boolean unsafeThreadingRecorded = false;
|
|
||||||
public boolean generatedChunkWithoutBiomeWarningLogged = false;
|
|
||||||
public int unknownExceptionCount = 0;
|
|
||||||
public long lastExceptionTriggerTime = 0;
|
|
||||||
|
|
||||||
public static ThreadLocal<Boolean> isDhWorldGenThreadRef = new ThreadLocal<>();
|
|
||||||
public static boolean isThisDhWorldGenThread() { return (isDhWorldGenThreadRef.get() != null); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
|
||||||
// constructors //
|
|
||||||
//==============//
|
|
||||||
|
|
||||||
static
|
|
||||||
{
|
|
||||||
boolean isTerraFirmaCraftPresent = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Class.forName("net.dries007.tfc.world.TFCChunkGenerator");
|
|
||||||
isTerraFirmaCraftPresent = true;
|
|
||||||
LOGGER.info("TerraFirmaCraft detected.");
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ignore) { }
|
|
||||||
|
|
||||||
|
|
||||||
ImmutableMap.Builder<EDhApiWorldGenerationStep, Integer> builder = ImmutableMap.builder();
|
|
||||||
builder.put(EDhApiWorldGenerationStep.EMPTY, 1);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.STRUCTURE_START, 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.STRUCTURE_REFERENCE, 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.BIOMES, isTerraFirmaCraftPresent ? 1 : 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.NOISE, isTerraFirmaCraftPresent ? 1 : 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.SURFACE, 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.CARVERS, 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.FEATURES, 0);
|
|
||||||
builder.put(EDhApiWorldGenerationStep.LIGHT, 0);
|
|
||||||
WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP = builder.build();
|
|
||||||
|
|
||||||
// in James' testing as of 2025-09-13 a border here of 2
|
|
||||||
// and a getChunkPosToGenerateStream() radius of 14 provided more accurate
|
|
||||||
// structure generation, however it also caused extreme server lag
|
|
||||||
// a border of 0 here and a getChunkPosToGenerateStream() radius of 8 provided
|
|
||||||
// good-enough structure generation while not lagging the server
|
|
||||||
MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BatchGenerationEnvironment(IDhServerLevel dhServerLevel)
|
|
||||||
{
|
|
||||||
this.dhServerLevel = dhServerLevel;
|
|
||||||
this.globalParams = new GlobalWorldGenParams(dhServerLevel);
|
|
||||||
this.internalServerGenerator = new InternalServerGenerator(this.globalParams, this.dhServerLevel);
|
|
||||||
this.chunkFileReader = new ChunkFileReader(this.globalParams);
|
|
||||||
|
|
||||||
ChunkGenerator generator = ((ServerLevelWrapper) (dhServerLevel.getServerLevelWrapper())).getLevel().getChunkSource().getGenerator();
|
|
||||||
boolean isMcGenerator =
|
|
||||||
generator instanceof NoiseBasedChunkGenerator
|
|
||||||
|| generator instanceof DebugLevelSource
|
|
||||||
|| generator instanceof FlatLevelSource;
|
|
||||||
if (!isMcGenerator)
|
|
||||||
{
|
|
||||||
if (generator.getClass().toString().equals("class com.terraforged.mod.chunk.TFChunkGenerator"))
|
|
||||||
{
|
|
||||||
LOGGER.info("TerraForge Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
|
|
||||||
LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
|
|
||||||
}
|
|
||||||
else if (generator.getClass().toString().equals("class net.dries007.tfc.world.TFCChunkGenerator"))
|
|
||||||
{
|
|
||||||
LOGGER.info("TerraFirmaCraft Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
|
|
||||||
LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.warn("Unknown Chunk Generator detected: [" + generator.getClass() + "], Distant Generation May Fail!");
|
|
||||||
LOGGER.warn("If it does crash, disable Distant Generation or set the Generation Mode to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// synchronization //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method checks to make sure that all world gen is being
|
|
||||||
* run on DH threads instead of leaking out to other MC threads.
|
|
||||||
* This is done to prevent putting undue stress on MC threads
|
|
||||||
* and prevent potential issues with concurrent processing.
|
|
||||||
*/
|
|
||||||
public <T> T confirmFutureWasRunSynchronously(CompletableFuture<T> future)
|
|
||||||
{
|
|
||||||
// this operation should be done since DH wants the
|
|
||||||
// operation to be done synchronously
|
|
||||||
if (!this.unsafeThreadingRecorded && !future.isDone())
|
|
||||||
{
|
|
||||||
LOGGER.warn(
|
|
||||||
"Unsafe MultiThreading in Distant Horizons Chunk Generator. \n" +
|
|
||||||
"This can happen if world generation is run on one of Minecraft's thread pools " +
|
|
||||||
"instead of the thread DH provided. \n" +
|
|
||||||
"This can likely be ignored, however if world generator crashes occur " +
|
|
||||||
"setting DH's world generation thread count to 1 may improve stability. ",
|
|
||||||
new RuntimeException("Incorrect thread pool use"));
|
|
||||||
this.unsafeThreadingRecorded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the future wasn't done synchronously,
|
|
||||||
// wait for it to finish so we can continue the world gen
|
|
||||||
// lifecycle like normal
|
|
||||||
return future.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateAllFutures()
|
|
||||||
{
|
|
||||||
if (this.unknownExceptionCount > 0)
|
|
||||||
{
|
|
||||||
if (System.nanoTime() - this.lastExceptionTriggerTime >= EXCEPTION_TIMER_RESET_TIME)
|
|
||||||
{
|
|
||||||
this.unknownExceptionCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Update all current out standing jobs
|
|
||||||
Iterator<GenerationEvent> iter = this.generationEventQueue.iterator();
|
|
||||||
while (iter.hasNext())
|
|
||||||
{
|
|
||||||
GenerationEvent event = iter.next();
|
|
||||||
if (event.future.isDone())
|
|
||||||
{
|
|
||||||
if (event.future.isCompletedExceptionally() && !event.future.isCancelled())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
event.future.get(); // Should throw exception
|
|
||||||
LodUtil.assertNotReach("Exceptionally completed world gen Future should have thrown an exception.");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
this.unknownExceptionCount++;
|
|
||||||
this.lastExceptionTriggerTime = System.nanoTime();
|
|
||||||
LOGGER.error("Batching World Generator event ["+event+"] threw an exception: "+e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iter.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.unknownExceptionCount > EXCEPTION_COUNTER_TRIGGER)
|
|
||||||
{
|
|
||||||
LOGGER.error("Too many exceptions in Batching World Generator! Disabling the generator.");
|
|
||||||
this.unknownExceptionCount = 0;
|
|
||||||
Config.Common.WorldGenerator.enableDistantGeneration.set(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
|
||||||
// world generation //
|
|
||||||
//==================//
|
|
||||||
|
|
||||||
/** @throws RejectedExecutionException if the given {@link Executor} is cancelled. */
|
|
||||||
public void generateEvent(GenerationEvent genEvent) throws RejectedExecutionException
|
|
||||||
{
|
|
||||||
// Minecraft's generation events expect odd chunk width areas (3x3, 7x7, or 11x11),
|
|
||||||
// but DH submits square generation events (4x4).
|
|
||||||
// We handle this later, although that handling would need to change if the gen size ever changes.
|
|
||||||
LodUtil.assertTrue(genEvent.widthInChunks % 2 == 0, "Generation events are expected to be an evan number of chunks wide.");
|
|
||||||
|
|
||||||
if (!DhApi.isDhThread()
|
|
||||||
&& ModInfo.IS_DEV_BUILD)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Batch world generation should be called from one of DH's world gen thread. Current thread: ["+Thread.currentThread().getName()+"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// variable setup //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
int borderSize = MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
|
|
||||||
// genEvent.size - 1 converts the even width size to an odd number for MC compatability
|
|
||||||
int refSize = (genEvent.widthInChunks - 1) + (borderSize * 2);
|
|
||||||
int refPosX = genEvent.minPos.getX() - borderSize;
|
|
||||||
int refPosZ = genEvent.minPos.getZ() - borderSize;
|
|
||||||
|
|
||||||
LightGetterAdaptor lightGetterAdaptor = new LightGetterAdaptor(this.globalParams.mcServerLevel);
|
|
||||||
DummyLightEngine dummyLightEngine = new DummyLightEngine(lightGetterAdaptor);
|
|
||||||
|
|
||||||
// reused data between each offset
|
|
||||||
Map<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
Map<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
Map<DhChunkPos, ChunkWrapper> chunkWrappersByDhPos = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================================//
|
|
||||||
// read existing chunks from file //
|
|
||||||
//================================//
|
|
||||||
|
|
||||||
HashMap<DhChunkPos, CompletableFuture<ChunkWrapper>> readFutureByDhChunkPos = new HashMap<>();
|
|
||||||
|
|
||||||
Iterator<ChunkPos> existingChunkPosIterator = ChunkPosGenStream.getIterator(
|
|
||||||
genEvent.minPos.getX(), genEvent.minPos.getZ(),
|
|
||||||
genEvent.widthInChunks,
|
|
||||||
// 0 radius -> only pull existing chunks from disk
|
|
||||||
0);
|
|
||||||
while (existingChunkPosIterator.hasNext())
|
|
||||||
{
|
|
||||||
ChunkPos chunkPos = existingChunkPosIterator.next();
|
|
||||||
DhChunkPos dhChunkPos = new DhChunkPos(chunkPos.x, chunkPos.z);
|
|
||||||
|
|
||||||
CompletableFuture<ChunkWrapper> getExistingChunkFuture
|
|
||||||
// running async allows file IO to run in parallel when C2ME is present
|
|
||||||
= this.chunkFileReader.createEmptyOrPreExistingChunkWrapperAsync(
|
|
||||||
chunkPos.x, chunkPos.z,
|
|
||||||
chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, chunkWrappersByDhPos);
|
|
||||||
|
|
||||||
readFutureByDhChunkPos.put(dhChunkPos, getExistingChunkFuture);
|
|
||||||
}
|
|
||||||
|
|
||||||
// normally DH will handle each of these futures serially
|
|
||||||
// but if C2ME is present these will be completed in parallel
|
|
||||||
for (CompletableFuture<ChunkWrapper> readChunkFuture : readFutureByDhChunkPos.values())
|
|
||||||
{
|
|
||||||
readChunkFuture.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================================//
|
|
||||||
// create empty chunks for world gen //
|
|
||||||
//===================================//
|
|
||||||
|
|
||||||
Iterator<ChunkPos> emptyChunkPosIterator = ChunkPosGenStream.getIterator(
|
|
||||||
genEvent.minPos.getX(), genEvent.minPos.getZ(),
|
|
||||||
genEvent.widthInChunks,
|
|
||||||
// the extra radius of 8 is to account for structure references which need a chunk radius of 8
|
|
||||||
8);
|
|
||||||
while (emptyChunkPosIterator.hasNext())
|
|
||||||
{
|
|
||||||
ChunkPos chunkPos = emptyChunkPosIterator.next();
|
|
||||||
DhChunkPos dhChunkPos = new DhChunkPos(chunkPos.x, chunkPos.z);
|
|
||||||
|
|
||||||
// create empty chunks outside the generation radius
|
|
||||||
if (!readFutureByDhChunkPos.containsKey(dhChunkPos))
|
|
||||||
{
|
|
||||||
ChunkWrapper chunkWrapper = this.chunkFileReader.CreateProtoChunkWrapper(this.globalParams.mcServerLevel, chunkPos);
|
|
||||||
chunkWrappersByDhPos.put(dhChunkPos, chunkWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// generate chunks //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// offset 1 chunk in both X and Z direction so we can generate an even number of chunks wide
|
|
||||||
// while still submitting an odd number width to MC's internal generators
|
|
||||||
for (int xOffset = 0; xOffset < 2; xOffset++)
|
|
||||||
{
|
|
||||||
// final is so the offset can be used in lambdas
|
|
||||||
final int xOffsetFinal = xOffset;
|
|
||||||
for (int zOffset = 0; zOffset < 2; zOffset++)
|
|
||||||
{
|
|
||||||
final int zOffsetFinal = zOffset;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// variable setup //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
int radius = refSize / 2;
|
|
||||||
int centerX = refPosX + radius + xOffset;
|
|
||||||
int centerZ = refPosZ + radius + zOffset;
|
|
||||||
|
|
||||||
// get/create the list of chunks we're going to generate
|
|
||||||
IEmptyChunkRetrievalFunc fallbackChunkGetterFunc =
|
|
||||||
(chunkPosX, chunkPosZ) -> Objects.requireNonNull(
|
|
||||||
chunkWrappersByDhPos.get(new DhChunkPos(chunkPosX, chunkPosZ)).getChunk(),
|
|
||||||
() -> String.format("Requested chunk [%d, %d] unavailable during world generation", chunkPosX, chunkPosZ));
|
|
||||||
|
|
||||||
ArrayGridList<ChunkAccess> regionChunks = new ArrayGridList<>(
|
|
||||||
refSize,
|
|
||||||
(relX, relZ) -> fallbackChunkGetterFunc.getChunk(
|
|
||||||
relX + refPosX + xOffsetFinal,
|
|
||||||
relZ + refPosZ + zOffsetFinal));
|
|
||||||
|
|
||||||
ChunkAccess centerChunk = regionChunks.stream()
|
|
||||||
.filter((chunk) -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ)
|
|
||||||
.findFirst()
|
|
||||||
.orElseGet(() -> regionChunks.getFirst());
|
|
||||||
|
|
||||||
DhLitWorldGenRegion region = new DhLitWorldGenRegion(
|
|
||||||
centerX, centerZ,
|
|
||||||
centerChunk,
|
|
||||||
this.globalParams.mcServerLevel, dummyLightEngine, regionChunks,
|
|
||||||
ChunkStatus.STRUCTURE_STARTS, radius,
|
|
||||||
// this method shouldn't be necessary since we're passing in a pre-populated
|
|
||||||
// list of chunks, but just in case
|
|
||||||
fallbackChunkGetterFunc
|
|
||||||
);
|
|
||||||
lightGetterAdaptor.setRegion(region);
|
|
||||||
genEvent.threadedParam.makeStructFeatManager(region, this.globalParams);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========================//
|
|
||||||
// process existing chunks //
|
|
||||||
//=========================//
|
|
||||||
|
|
||||||
ArrayGridList<ChunkWrapper> chunkWrapperList = new ArrayGridList<>(regionChunks.gridSize);
|
|
||||||
regionChunks.forEachPos((relX, relZ) ->
|
|
||||||
{
|
|
||||||
// ArrayGridList's use relative positions and don't have a center position
|
|
||||||
// so we need to use the offsetFinal to select the correct position
|
|
||||||
DhChunkPos chunkPos = new DhChunkPos(relX + refPosX + xOffsetFinal, relZ + refPosZ + zOffsetFinal);
|
|
||||||
ChunkAccess chunk = regionChunks.get(relX, relZ);
|
|
||||||
|
|
||||||
if (chunkWrappersByDhPos.containsKey(chunkPos))
|
|
||||||
{
|
|
||||||
chunkWrapperList.set(relX, relZ, chunkWrappersByDhPos.get(chunkPos));
|
|
||||||
}
|
|
||||||
else if (chunk != null)
|
|
||||||
{
|
|
||||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
|
|
||||||
chunkWrapper.createDhHeightMaps();
|
|
||||||
chunkWrapperList.set(relX, relZ, chunkWrapper);
|
|
||||||
|
|
||||||
// try setting the wrapper's lighting
|
|
||||||
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
|
|
||||||
{
|
|
||||||
// block
|
|
||||||
ChunkLightStorage blockLightStorage = chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos());
|
|
||||||
// if the light storage is empty then we should try generating the lighting
|
|
||||||
// ourselves, the light data is probably missing
|
|
||||||
if (blockLightStorage != null
|
|
||||||
&& !blockLightStorage.isEmpty())
|
|
||||||
{
|
|
||||||
chunkWrapper.setBlockLightStorage(blockLightStorage);
|
|
||||||
chunkWrapper.setIsDhBlockLightCorrect(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sky
|
|
||||||
ChunkLightStorage skyLightStorage = chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos());
|
|
||||||
if (skyLightStorage != null
|
|
||||||
&& !skyLightStorage.isEmpty())
|
|
||||||
{
|
|
||||||
chunkWrapper.setSkyLightStorage(skyLightStorage);
|
|
||||||
chunkWrapper.setIsDhSkyLightCorrect(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkWrappersByDhPos.put(chunkPos, chunkWrapper);
|
|
||||||
}
|
|
||||||
else //if (chunk == null)
|
|
||||||
{
|
|
||||||
LodUtil.assertNotReach("Programmer Error: No chunk found in grid list, position offset is likely wrong.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// generate chunks //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.generateDirect(genEvent, chunkWrapperList, region);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e)
|
|
||||||
{
|
|
||||||
throw new CompletionException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========================//
|
|
||||||
// submit generated chunks //
|
|
||||||
//=========================//
|
|
||||||
|
|
||||||
Iterator<ChunkPos> iterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
|
||||||
while (iterator.hasNext())
|
|
||||||
{
|
|
||||||
ChunkPos pos = iterator.next();
|
|
||||||
DhChunkPos dhPos = new DhChunkPos(pos.x, pos.z);
|
|
||||||
ChunkWrapper wrappedChunk = chunkWrappersByDhPos.get(dhPos);
|
|
||||||
|
|
||||||
// only pass along chunks that have been generated up to BIOMES
|
|
||||||
// this is to prevent issues with generating existing
|
|
||||||
if (wrappedChunk.getStatus().isOrAfter(ChunkStatus.BIOMES))
|
|
||||||
{
|
|
||||||
genEvent.resultConsumer.accept(wrappedChunk);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// this shouldn't happen, but if it does log it
|
|
||||||
if (!this.generatedChunkWithoutBiomeWarningLogged)
|
|
||||||
{
|
|
||||||
this.generatedChunkWithoutBiomeWarningLogged = true;
|
|
||||||
LOGGER.warn("Chunk [" + dhPos + "] wasn't generated up to BIOMES, world gen may appear empty.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (CompletionException | UncheckedInterruptedException e)
|
|
||||||
{
|
|
||||||
// interrupts mean the world generator is being shut down, no need to log that
|
|
||||||
boolean isShutdownException = ExceptionUtil.isShutdownException(e);
|
|
||||||
if (!isShutdownException)
|
|
||||||
{
|
|
||||||
LOGGER.error("Completion error during world gen for min chunk pos ["+genEvent.minPos+"], error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Unexpected error during world gen for min chunk pos ["+genEvent.minPos+"], error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// direct generation //
|
|
||||||
|
|
||||||
public void generateDirect(
|
|
||||||
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunkWrappersToGenerate,
|
|
||||||
DhLitWorldGenRegion region) throws InterruptedException
|
|
||||||
{
|
|
||||||
if (Thread.interrupted())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
chunkWrappersToGenerate.forEach((chunkWrapper) ->
|
|
||||||
{
|
|
||||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
|
||||||
if (chunk instanceof ProtoChunk)
|
|
||||||
{
|
|
||||||
ProtoChunk protoChunk = ((ProtoChunk) chunk);
|
|
||||||
protoChunk.setLightEngine(region.getLightEngine());
|
|
||||||
}
|
|
||||||
|
|
||||||
// usually ignoring the chunk's position is unnecessary,
|
|
||||||
// but this improves performance if a chunk update event does sneak through
|
|
||||||
SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.addPosToIgnore(chunkWrapper.getChunkPos());
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// if we're only working with pre-existing chunks,
|
|
||||||
// biomes need to be initialized but no other steps should be done
|
|
||||||
if (genEvent.generatorMode == EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY)
|
|
||||||
{
|
|
||||||
this.stepBiomes.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.BIOMES));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EDhApiWorldGenerationStep step = genEvent.targetGenerationStep;
|
|
||||||
if (step == EDhApiWorldGenerationStep.EMPTY)
|
|
||||||
{
|
|
||||||
// shouldn't normally happen but is here for consistency with the other world gen steps
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throwIfThreadInterrupted();
|
|
||||||
this.stepStructureStart.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.STRUCTURE_START));
|
|
||||||
if (step == EDhApiWorldGenerationStep.STRUCTURE_START)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throwIfThreadInterrupted();
|
|
||||||
this.stepStructureReference.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.STRUCTURE_REFERENCE));
|
|
||||||
if (step == EDhApiWorldGenerationStep.STRUCTURE_REFERENCE)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throwIfThreadInterrupted();
|
|
||||||
this.stepBiomes.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.BIOMES));
|
|
||||||
if (step == EDhApiWorldGenerationStep.BIOMES)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throwIfThreadInterrupted();
|
|
||||||
this.stepNoise.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.NOISE));
|
|
||||||
if (step == EDhApiWorldGenerationStep.NOISE)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throwIfThreadInterrupted();
|
|
||||||
this.stepSurface.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.SURFACE));
|
|
||||||
if (step == EDhApiWorldGenerationStep.SURFACE)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// throwIfThreadInterrupted();
|
|
||||||
// // caves can generally be ignored since they aren't generally visible from far away
|
|
||||||
// if (step == EDhApiWorldGenerationStep.CARVERS)
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
throwIfThreadInterrupted();
|
|
||||||
this.stepFeatures.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.FEATURES));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// generate lighting using DH's lighting engine
|
|
||||||
|
|
||||||
int maxSkyLight = this.dhServerLevel.getServerLevelWrapper().hasSkyLight() ? 15 : 0;
|
|
||||||
|
|
||||||
// only light generated chunks,
|
|
||||||
// attempting to light un-generated chunks will cause lighting issues on bordering generated chunks
|
|
||||||
ArrayList<IChunkWrapper> iChunkWrapperList = new ArrayList<>();
|
|
||||||
for (int i = 0; i < chunkWrappersToGenerate.size(); i++) // regular for loop since enhanced for loops increase GC pressure slightly
|
|
||||||
{
|
|
||||||
ChunkWrapper chunkWrapper = chunkWrappersToGenerate.get(i);
|
|
||||||
if (chunkWrapper.getStatus() != ChunkStatus.EMPTY)
|
|
||||||
{
|
|
||||||
iChunkWrapperList.add(chunkWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// light each chunk in the list
|
|
||||||
for (int i = 0; i < iChunkWrapperList.size(); i++)
|
|
||||||
{
|
|
||||||
ChunkWrapper centerChunkWrapper = (ChunkWrapper) iChunkWrapperList.get(i);
|
|
||||||
if (centerChunkWrapper == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throwIfThreadInterrupted();
|
|
||||||
|
|
||||||
// not always necessary, but sometimes MC heightmap is wrong
|
|
||||||
// and can cause LODs to generate incorrectly
|
|
||||||
centerChunkWrapper.createDhHeightMaps();
|
|
||||||
|
|
||||||
// pre-generated chunks should have lighting but new ones won't
|
|
||||||
if (!centerChunkWrapper.isDhBlockLightingCorrect())
|
|
||||||
{
|
|
||||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(centerChunkWrapper, iChunkWrapperList, maxSkyLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<BeaconBeamDTO> activeBeamList = centerChunkWrapper.getAllActiveBeacons(iChunkWrapperList);
|
|
||||||
if (!activeBeamList.isEmpty())
|
|
||||||
{
|
|
||||||
this.dhServerLevel.updateBeaconBeamsForChunkPos(centerChunkWrapper.getChunkPos(), activeBeamList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < iChunkWrapperList.size(); i++)
|
|
||||||
{
|
|
||||||
ChunkWrapper chunkWrapper = (ChunkWrapper) iChunkWrapperList.get(i);
|
|
||||||
if (chunkWrapper == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// give MC a few seconds to save the chunk before
|
|
||||||
// we can process update events there again
|
|
||||||
this.chunkSaveIgnoreTimer.schedule(new TimerTask()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run() { SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.removePosToIgnore(chunkWrapper.getChunkPos()); }
|
|
||||||
}, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
|
|
||||||
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// queue task //
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<Void> queueGenEvent(
|
|
||||||
int minX, int minZ, int chunkWidthCount,
|
|
||||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetStep,
|
|
||||||
ExecutorService worldGeneratorThreadPool, Consumer<IChunkWrapper> resultConsumer)
|
|
||||||
{
|
|
||||||
GenerationEvent genEvent = GenerationEvent.start(
|
|
||||||
new DhChunkPos(minX, minZ), chunkWidthCount, this,
|
|
||||||
generatorMode, targetStep, resultConsumer,
|
|
||||||
worldGeneratorThreadPool);
|
|
||||||
this.generationEventQueue.add(genEvent);
|
|
||||||
return genEvent.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close()
|
|
||||||
{
|
|
||||||
LOGGER.info("Closing [" +BatchGenerationEnvironment.class.getSimpleName() + "]");
|
|
||||||
|
|
||||||
|
|
||||||
// cancel in-progress tasks
|
|
||||||
Iterator<GenerationEvent> genEventIter = this.generationEventQueue.iterator();
|
|
||||||
while (genEventIter.hasNext())
|
|
||||||
{
|
|
||||||
GenerationEvent event = genEventIter.next();
|
|
||||||
event.future.cancel(true);
|
|
||||||
genEventIter.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.chunkFileReader.close();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper methods //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called before code that may run for an extended period of time. <br>
|
|
||||||
* This is necessary to allow canceling world gen since waiting
|
|
||||||
* for some world gen requests to finish can take a while.
|
|
||||||
*/
|
|
||||||
public static void throwIfThreadInterrupted() throws InterruptedException
|
|
||||||
{
|
|
||||||
if (Thread.interrupted())
|
|
||||||
{
|
|
||||||
throw new InterruptedException("["+BatchGenerationEnvironment.class.getSimpleName()+"] task interrupted.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper classes //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface IEmptyChunkRetrievalFunc
|
|
||||||
{
|
|
||||||
ChunkAccess getChunk(int chunkPosX, int chunkPosZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-92
@@ -1,92 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Spliterator;
|
|
||||||
import java.util.Spliterators;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
public class ChunkPosGenStream
|
|
||||||
{
|
|
||||||
|
|
||||||
public static Iterator<ChunkPos> getIterator(int genMinX, int genMinZ, int width, int extraRadius)
|
|
||||||
{ return getStream(genMinX, genMinZ, width, extraRadius).iterator(); }
|
|
||||||
/** @param extraRadius in both the positive and negative directions */
|
|
||||||
public static Stream<ChunkPos> getStream(int genMinX, int genMinZ, int width, int extraRadius)
|
|
||||||
{ return StreamSupport.stream(new InclusiveChunkPosIterator(genMinX, genMinZ, width, extraRadius), false); }
|
|
||||||
|
|
||||||
private static class InclusiveChunkPosIterator extends Spliterators.AbstractSpliterator<ChunkPos>
|
|
||||||
{
|
|
||||||
private final int minX;
|
|
||||||
private final int minZ;
|
|
||||||
|
|
||||||
private final int maxX;
|
|
||||||
private final int maxZ;
|
|
||||||
|
|
||||||
|
|
||||||
/** current X pos */
|
|
||||||
int x;
|
|
||||||
/** current Z pos */
|
|
||||||
private int z;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
protected InclusiveChunkPosIterator(int genMinX, int genMinZ, int width, int extraRadius)
|
|
||||||
{
|
|
||||||
super(getCount(width, extraRadius), Spliterator.SIZED);
|
|
||||||
|
|
||||||
this.minX = genMinX - extraRadius;
|
|
||||||
this.minZ = genMinZ - extraRadius;
|
|
||||||
|
|
||||||
this.maxX = genMinX + (width - 1) + extraRadius;
|
|
||||||
this.maxZ = genMinZ + (width - 1) + extraRadius;
|
|
||||||
|
|
||||||
// X starts at 1 minus the minX so we can immediately re-add 1 in the tryAdvance() loop
|
|
||||||
this.x = this.minX - 1;
|
|
||||||
this.z = this.minZ;
|
|
||||||
}
|
|
||||||
private static int getCount(int width, int extraRadius)
|
|
||||||
{
|
|
||||||
int widthPlusExtra = width + (extraRadius * 2);
|
|
||||||
return widthPlusExtra * widthPlusExtra;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// iterator method //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryAdvance(Consumer<? super ChunkPos> consumer)
|
|
||||||
{
|
|
||||||
if (this.x == this.maxX && this.z == this.maxZ)
|
|
||||||
{
|
|
||||||
// the last returned position was the final valid position
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.x == this.maxX)
|
|
||||||
{
|
|
||||||
// we reached the max X position, loop back around in the next Z row
|
|
||||||
this.x = this.minX;
|
|
||||||
this.z++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.x++;
|
|
||||||
}
|
|
||||||
|
|
||||||
consumer.accept(new ChunkPos(this.x, this.z));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-171
@@ -1,171 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
|
||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
|
||||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
|
|
||||||
public final class GenerationEvent
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();;
|
|
||||||
|
|
||||||
private static final AtomicInteger DEBUG_ID_REF = new AtomicInteger(0);
|
|
||||||
|
|
||||||
|
|
||||||
/** can be used for troubleshooting */
|
|
||||||
public final int id;
|
|
||||||
|
|
||||||
public final ThreadWorldGenParams threadedParam;
|
|
||||||
public final DhChunkPos minPos;
|
|
||||||
public final int widthInChunks;
|
|
||||||
public final EDhApiWorldGenerationStep targetGenerationStep;
|
|
||||||
public final EDhApiDistantGeneratorMode generatorMode;
|
|
||||||
public final CompletableFuture<Void> future;
|
|
||||||
public final Consumer<IChunkWrapper> resultConsumer;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
private GenerationEvent(
|
|
||||||
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment generationGroup,
|
|
||||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
|
|
||||||
{
|
|
||||||
this.id = DEBUG_ID_REF.getAndIncrement();
|
|
||||||
|
|
||||||
this.minPos = minPos;
|
|
||||||
this.widthInChunks = widthInChunks;
|
|
||||||
this.targetGenerationStep = targetGenerationStep;
|
|
||||||
this.generatorMode = generatorMode;
|
|
||||||
this.threadedParam = ThreadWorldGenParams.getOrMake(generationGroup.globalParams);
|
|
||||||
this.future = new CompletableFuture<>();
|
|
||||||
this.resultConsumer = resultConsumer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======//
|
|
||||||
// start //
|
|
||||||
//=======//
|
|
||||||
|
|
||||||
public static GenerationEvent start(
|
|
||||||
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment genEnvironment,
|
|
||||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
|
|
||||||
ExecutorService worldGeneratorThreadPool)
|
|
||||||
{
|
|
||||||
GenerationEvent genEvent = new GenerationEvent(minPos, widthInChunks, genEnvironment, generatorMode, target, resultConsumer);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
worldGeneratorThreadPool.execute(() ->
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
BatchGenerationEnvironment.isDhWorldGenThreadRef.set(true);
|
|
||||||
|
|
||||||
|
|
||||||
if (genEvent.generatorMode == EDhApiDistantGeneratorMode.INTERNAL_SERVER)
|
|
||||||
{
|
|
||||||
genEnvironment.internalServerGenerator.generateChunksViaInternalServer(genEvent);
|
|
||||||
genEvent.future.complete(null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
genEnvironment.generateEvent(genEvent);
|
|
||||||
}
|
|
||||||
catch (Throwable throwable)
|
|
||||||
{
|
|
||||||
handleWorldGenThrowable(genEvent, throwable);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
genEvent.future.complete(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Throwable initialThrowable)
|
|
||||||
{
|
|
||||||
handleWorldGenThrowable(genEvent, initialThrowable);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
BatchGenerationEnvironment.isDhWorldGenThreadRef.remove();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (RejectedExecutionException e)
|
|
||||||
{
|
|
||||||
genEvent.future.completeExceptionally(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return genEvent;
|
|
||||||
}
|
|
||||||
/** There's probably a better way to handle this, but it'll work for now */
|
|
||||||
private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable)
|
|
||||||
{
|
|
||||||
Throwable throwable = initialThrowable;
|
|
||||||
while (throwable instanceof CompletionException)
|
|
||||||
{
|
|
||||||
throwable = throwable.getCause();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isShutdownException = ExceptionUtil.isShutdownException(throwable);
|
|
||||||
if (isShutdownException)
|
|
||||||
{
|
|
||||||
// these exceptions can be ignored, generally they just mean
|
|
||||||
// the thread is busy so it'll need to try again later.
|
|
||||||
// FIXME this should cause the world gen task to be re-queued so we can try again later
|
|
||||||
// however, currently it can cause large gaps in the world gen instead.
|
|
||||||
// These gaps will generate correctly if the level is reloaded and the world gen is re-queued,
|
|
||||||
// however this is makes it look like the generator isn't working or skipped something.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
generationEvent.future.completeExceptionally(throwable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return this.id + ":" + this.widthInChunks + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-311
@@ -1,311 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.DhApi;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
|
||||||
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
|
||||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
|
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_4
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class InternalServerGenerator
|
|
||||||
{
|
|
||||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
|
||||||
.name("LOD World Gen - Internal Server")
|
|
||||||
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
|
|
||||||
.name("LOD Chunk Loading")
|
|
||||||
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final IC2meAccessor C2ME_ACCESSOR = ModAccessorInjector.INSTANCE.get(IC2meAccessor.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to revert the ignore logic in {@link SharedApi} so
|
|
||||||
* that a given chunk pos can be handled again.
|
|
||||||
* A timer is used so we don't have to inject into MC's code and it works sell enough
|
|
||||||
* most of the time.
|
|
||||||
* If a chunk does get through due the timeout not being long enough that isn't the end of the world.
|
|
||||||
*/
|
|
||||||
private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
private static final TicketType<ChunkPos> DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong));
|
|
||||||
#elif MC_VER < MC_1_21_9
|
|
||||||
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* persist */ false, TicketType.TicketUse.LOADING);
|
|
||||||
#else
|
|
||||||
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* flags */TicketType.FLAG_LOADING);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private static boolean c2meMissingWarningLogged = false;
|
|
||||||
|
|
||||||
|
|
||||||
private final GlobalWorldGenParams params;
|
|
||||||
private final IDhServerLevel dhServerLevel;
|
|
||||||
private final Timer chunkSaveIgnoreTimer = TimerUtil.CreateTimer("ChunkSaveIgnoreTimer");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public InternalServerGenerator(GlobalWorldGenParams params, IDhServerLevel dhServerLevel)
|
|
||||||
{
|
|
||||||
this.params = params;
|
|
||||||
this.dhServerLevel = dhServerLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============//
|
|
||||||
// generation //
|
|
||||||
//============//
|
|
||||||
|
|
||||||
public void generateChunksViaInternalServer(GenerationEvent genEvent)
|
|
||||||
{
|
|
||||||
this.runValidation();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//=====================//
|
|
||||||
// create gen requests //
|
|
||||||
//=====================//
|
|
||||||
|
|
||||||
ArrayList<CompletableFuture<ChunkAccess>> getChunkFutureList = new ArrayList<>();
|
|
||||||
{
|
|
||||||
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
|
||||||
while (chunkPosIterator.hasNext())
|
|
||||||
{
|
|
||||||
ChunkPos chunkPos = chunkPosIterator.next();
|
|
||||||
|
|
||||||
CompletableFuture<ChunkAccess> requestChunkFuture =
|
|
||||||
this.requestChunkFromServerAsync(chunkPos)
|
|
||||||
// log errors if necessary
|
|
||||||
.whenCompleteAsync(
|
|
||||||
(chunk, throwable) ->
|
|
||||||
{
|
|
||||||
// unwrap the CompletionException if necessary
|
|
||||||
Throwable actualThrowable = throwable;
|
|
||||||
while (actualThrowable instanceof CompletionException)
|
|
||||||
{
|
|
||||||
actualThrowable = actualThrowable.getCause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actualThrowable != null)
|
|
||||||
{
|
|
||||||
// ignore expected shutdown exceptions
|
|
||||||
boolean isShutdownException =
|
|
||||||
ExceptionUtil.isShutdownException(actualThrowable)
|
|
||||||
|| actualThrowable.getMessage().contains("Unloaded chunk");
|
|
||||||
if (!isShutdownException)
|
|
||||||
{
|
|
||||||
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getChunkFutureList.add(requestChunkFuture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============================//
|
|
||||||
// wait for generation requests //
|
|
||||||
//==============================//
|
|
||||||
|
|
||||||
// Join-ing each thread will prevent DH from working on anything else
|
|
||||||
// but will also prevent over-queuing world gen tasks.
|
|
||||||
// If C2ME is present the CPU will still be well utilized.
|
|
||||||
|
|
||||||
ArrayList<IChunkWrapper> chunkWrappers = new ArrayList<>();
|
|
||||||
for (int i = 0; i < getChunkFutureList.size(); i++)
|
|
||||||
{
|
|
||||||
CompletableFuture<ChunkAccess> getChunkFuture = getChunkFutureList.get(i);
|
|
||||||
ChunkAccess chunk = getChunkFuture.join();
|
|
||||||
if (chunk != null)
|
|
||||||
{
|
|
||||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
|
|
||||||
chunkWrapper.createDhHeightMaps();
|
|
||||||
chunkWrappers.add(chunkWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========================//
|
|
||||||
// process generated chunks //
|
|
||||||
//==========================//
|
|
||||||
|
|
||||||
int maxSkyLight = this.dhServerLevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
|
|
||||||
for (int i = 0; i < chunkWrappers.size(); i++)
|
|
||||||
{
|
|
||||||
ChunkWrapper chunkWrapper = (ChunkWrapper)chunkWrappers.get(i);
|
|
||||||
|
|
||||||
// pre-generated chunks should have lighting but new ones won't
|
|
||||||
if (!chunkWrapper.isDhBlockLightingCorrect())
|
|
||||||
{
|
|
||||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, chunkWrappers, maxSkyLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dhServerLevel.updateBeaconBeamsForChunk(chunkWrapper, chunkWrappers);
|
|
||||||
genEvent.resultConsumer.accept(chunkWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// release all chunks from the server to prevent out of memory issues
|
|
||||||
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
|
||||||
while (chunkPosIterator.hasNext())
|
|
||||||
{
|
|
||||||
ChunkPos chunkPos = chunkPosIterator.next();
|
|
||||||
this.releaseChunkFromServer(this.params.mcServerLevel, chunkPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void runValidation()
|
|
||||||
{
|
|
||||||
// DH thread check
|
|
||||||
if (!DhApi.isDhThread()
|
|
||||||
&& ModInfo.IS_DEV_BUILD)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Internal server generation should be called from one of DH's world gen thread. Current thread: ["+Thread.currentThread().getName()+"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// C2ME present?
|
|
||||||
if (C2ME_ACCESSOR == null
|
|
||||||
&& !c2meMissingWarningLogged)
|
|
||||||
{
|
|
||||||
c2meMissingWarningLogged = true;
|
|
||||||
|
|
||||||
String c2meWarning = "C2ME missing, \n" +
|
|
||||||
"low CPU usage and slow world gen speeds expected. \n" +
|
|
||||||
"DH is set to use MC's internal server for world gen \n" +
|
|
||||||
"this mode is less efficient unless a mod like C2ME is present."
|
|
||||||
;
|
|
||||||
|
|
||||||
if (Config.Common.Logging.Warning.showSlowWorldGenSettingWarnings.get())
|
|
||||||
{
|
|
||||||
String message =
|
|
||||||
MinecraftTextFormat.ORANGE + "Distant Horizons: slow world gen." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
|
|
||||||
c2meWarning;
|
|
||||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.warn(c2meWarning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private CompletableFuture<ChunkAccess> requestChunkFromServerAsync(ChunkPos chunkPos)
|
|
||||||
{
|
|
||||||
return CompletableFuture.supplyAsync(() ->
|
|
||||||
{
|
|
||||||
ServerLevel level = this.params.mcServerLevel;
|
|
||||||
|
|
||||||
// ignore chunk update events for this position
|
|
||||||
SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.addPosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z));
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
|
|
||||||
level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
|
|
||||||
#else
|
|
||||||
level.getChunkSource().addTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// probably not the most optimal to run updates here, but fast enough
|
|
||||||
level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap);
|
|
||||||
|
|
||||||
ChunkHolder chunkHolder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong());
|
|
||||||
if (chunkHolder == null)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("No chunk chunkHolder for pos ["+chunkPos+"] after ticket has been added.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_4
|
|
||||||
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
|
||||||
.thenApply(result -> result.left().orElseThrow(() -> new RuntimeException(result.right().get().toString()))); // can throw if the server is shutting down
|
|
||||||
#elif MC_VER <= MC_1_20_6
|
|
||||||
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
|
||||||
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.toString()))); // can throw if the server is shutting down
|
|
||||||
#else
|
|
||||||
return chunkHolder.scheduleChunkGenerationTask(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
|
||||||
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.getError()))); // can throw if the server is shutting down
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}, this.params.mcServerLevel.getChunkSource().chunkMap.mainThreadExecutor)
|
|
||||||
.thenCompose(Function.identity());
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* mitigates out of memory issues in the vanilla chunk system. <br>
|
|
||||||
* See: https://github.com/pop4959/Chunky/pull/383
|
|
||||||
*/
|
|
||||||
private void releaseChunkFromServer(ServerLevel level, ChunkPos chunkPos)
|
|
||||||
{
|
|
||||||
level.getChunkSource().chunkMap.mainThreadExecutor.execute(() ->
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
|
|
||||||
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
|
|
||||||
#else
|
|
||||||
level.getChunkSource().removeTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
level.getChunkSource().chunkMap.tick(() -> false);
|
|
||||||
|
|
||||||
#if MC_VER > MC_1_16_5
|
|
||||||
level.entityManager.tick();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// give MC a few seconds to save the chunk before
|
|
||||||
// we can process update events there again
|
|
||||||
this.chunkSaveIgnoreTimer.schedule(new TimerTask()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run() { SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.removePosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); }
|
|
||||||
}, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-745
@@ -1,745 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU GPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
|
|
||||||
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
#if MC_VER >= MC_1_19_4
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import net.minecraft.nbt.NbtOps;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.world.level.*;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.*;
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
import net.minecraft.world.level.block.Blocks;
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
|
||||||
#endif
|
|
||||||
import net.minecraft.world.ticks.LevelChunkTicks;
|
|
||||||
#endif
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
|
||||||
import net.minecraft.world.level.material.Fluids;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER == MC_1_20_6
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkType;
|
|
||||||
#elif MC_VER >= MC_1_21_1
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import net.minecraft.world.level.material.Fluid;
|
|
||||||
|
|
||||||
|
|
||||||
public class ChunkCompoundTagParser
|
|
||||||
{
|
|
||||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
|
||||||
.name("LOD Chunk Reader")
|
|
||||||
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(false);
|
|
||||||
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private static boolean lightingSectionErrorLogged = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============//
|
|
||||||
// read chunk //
|
|
||||||
//============//
|
|
||||||
|
|
||||||
public static ChunkWrapper createFromTag(
|
|
||||||
WorldGenLevel mcWorldGenLevel, IDhServerLevel dhServerLevel,
|
|
||||||
ChunkPos chunkPos, CompoundTag chunkData)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
CompoundTag tagLevel = chunkData.getCompound("Level");
|
|
||||||
#else
|
|
||||||
CompoundTag tagLevel = chunkData;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======================//
|
|
||||||
// validate the chunkPos //
|
|
||||||
//=======================//
|
|
||||||
|
|
||||||
int chunkX = CompoundTagUtil.getInt(tagLevel,"xPos");
|
|
||||||
int chunkZ = CompoundTagUtil.getInt(tagLevel, "zPos");
|
|
||||||
ChunkPos actualChunkPos = new ChunkPos(chunkX, chunkZ);
|
|
||||||
|
|
||||||
// confirm chunk pos is correct
|
|
||||||
if (!Objects.equals(chunkPos, actualChunkPos))
|
|
||||||
{
|
|
||||||
if (chunkX == 0 && chunkZ == 0)
|
|
||||||
{
|
|
||||||
if (!ZERO_CHUNK_POS_ERROR_LOGGED_REF.getAndSet(true))
|
|
||||||
{
|
|
||||||
// explicit chunkPos toString is necessary otherwise the JDK 17 compiler breaks
|
|
||||||
LOGGER.warn("Chunk file at ["+chunkPos.toString()+"] doesn't have a chunk pos. \n" +
|
|
||||||
"This might happen if the world was created using an external program. \n" +
|
|
||||||
"DH will attempt to parse the chunk anyway and won't log this message again.\n" +
|
|
||||||
"If issues arise please try optimizing your world to fix this issue. \n" +
|
|
||||||
"World optimization can be done from the singleplayer world selection screen." +
|
|
||||||
" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \n" +
|
|
||||||
"Please try optimizing your world to fix this issue. \n" +
|
|
||||||
"World optimization can be done from the singleplayer world selection screen. \n" +
|
|
||||||
"(Expected pos: ["+chunkPos.toString()+"], actual ["+actualChunkPos.toString()+"])" +
|
|
||||||
" ");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
|
||||||
// get ticks //
|
|
||||||
//===========//
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
|
|
||||||
mcWorldGenLevel.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), #if MC_VER >= MC_1_17_1 mcWorldGenLevel, #endif
|
|
||||||
chunkPos, mcWorldGenLevel.getLevel().getChunkSource().getGenerator().getBiomeSource(),
|
|
||||||
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
|
|
||||||
|
|
||||||
String BLOCK_TICKS_TAG_PRE18 = "TileTicks";
|
|
||||||
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
|
|
||||||
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
|
|
||||||
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
|
|
||||||
tagLevel.getList("ToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
|
|
||||||
|
|
||||||
String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
|
|
||||||
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
|
|
||||||
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
|
|
||||||
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
|
|
||||||
tagLevel.getList("LiquidsToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
|
|
||||||
#else
|
|
||||||
// ticks shouldn't be needed so ignore them for MC versions after 1.18.2
|
|
||||||
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
|
|
||||||
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=====================//
|
|
||||||
// get misc properties //
|
|
||||||
//=====================//
|
|
||||||
|
|
||||||
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else mcWorldGenLevel.getSectionsCount(); #endif
|
|
||||||
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYCount];
|
|
||||||
boolean hasBlocks = readAndPopulateSections(mcWorldGenLevel, chunkPos, tagLevel, chunkSections);
|
|
||||||
if (!hasBlocks)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
long inhabitedTime = CompoundTagUtil.getLong(tagLevel, "InhabitedTime");
|
|
||||||
boolean isLightOn = CompoundTagUtil.getBoolean(tagLevel, "isLightOn");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============//
|
|
||||||
// make chunk //
|
|
||||||
//============//
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel.getLevel(), chunkPos, chunkBiomeContainer, UpgradeData.EMPTY, blockTicks,
|
|
||||||
fluidTicks, inhabitedTime, chunkSections, null);
|
|
||||||
#else
|
|
||||||
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel, chunkPos, UpgradeData.EMPTY, blockTicks,
|
|
||||||
fluidTicks, inhabitedTime, chunkSections, null, null);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set some states after object creation
|
|
||||||
chunk.setLightCorrect(isLightOn);
|
|
||||||
boolean hasHeightmapData = readHeightmaps(chunk, chunkData);
|
|
||||||
|
|
||||||
// chunk wrapper so we can pass along extra data more easily
|
|
||||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, dhServerLevel.getServerLevelWrapper());
|
|
||||||
chunkWrapper.createDhHeightMaps();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========================//
|
|
||||||
// check if chunk has blocks //
|
|
||||||
//===========================//
|
|
||||||
|
|
||||||
// in some MC versions all the NBT data will be there
|
|
||||||
// but the chunk will be totally empty,
|
|
||||||
// usually this means the chunk was only partially generated.
|
|
||||||
// If that happens we should try to generate the chunk from scratch
|
|
||||||
// otherwise we can end up with large empty holes in the world.
|
|
||||||
|
|
||||||
// walking through the heightmap (recreated by DH if missing)
|
|
||||||
// is a fast way to check if there are any blocks in the chunk
|
|
||||||
boolean chunkHasBlocks = false;
|
|
||||||
int serverMinHeight = dhServerLevel.getServerLevelWrapper().getMinHeight();
|
|
||||||
for (int x = 0; x < 16 && !chunkHasBlocks; x++)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < 16 && !chunkHasBlocks; z++)
|
|
||||||
{
|
|
||||||
int heightMap = Math.max(
|
|
||||||
// max between both heightmaps just in case there's a discrepancy
|
|
||||||
chunkWrapper.getLightBlockingHeightMapValue(x, z),
|
|
||||||
chunkWrapper.getSolidHeightMapValue(x, z)
|
|
||||||
);
|
|
||||||
if (heightMap != serverMinHeight)
|
|
||||||
{
|
|
||||||
chunkHasBlocks = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (chunkHasBlocks)
|
|
||||||
{
|
|
||||||
return chunkWrapper;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// no blocks detected, this chunk should be generated from scratch
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// chunk sections //
|
|
||||||
// (Blocks/biomes) //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
/** handles both blocks and biomes */
|
|
||||||
private static boolean readAndPopulateSections(
|
|
||||||
LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData,
|
|
||||||
LevelChunkSection[] chunkSections)
|
|
||||||
{
|
|
||||||
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
|
|
||||||
|
|
||||||
ListTag tagSections = CompoundTagUtil.getListTag(chunkData, "Sections", 10);
|
|
||||||
// try lower-case "sections" if capital "Sections" is missing
|
|
||||||
if (tagSections == null
|
|
||||||
|| tagSections.isEmpty())
|
|
||||||
{
|
|
||||||
tagSections = CompoundTagUtil.getListTag(chunkData, "sections", 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boolean blocksFound = false;
|
|
||||||
if (tagSections != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < tagSections.size(); ++i)
|
|
||||||
{
|
|
||||||
CompoundTag tagSection = CompoundTagUtil.getCompoundTag(tagSections, i);
|
|
||||||
if (tagSection == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int sectionYPos = CompoundTagUtil.getByte(tagSection, "Y");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
|
||||||
// get blocks/biomes //
|
|
||||||
//===================//
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
if (tagSection.contains("Palette", 9)
|
|
||||||
&& tagSection.contains("BlockStates", 12))
|
|
||||||
{
|
|
||||||
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
|
|
||||||
levelChunkSection.getStates().read(tagSection.getList("Palette", 10), tagSection.getLongArray("BlockStates"));
|
|
||||||
levelChunkSection.recalcBlockCounts();
|
|
||||||
if (!levelChunkSection.isEmpty())
|
|
||||||
{
|
|
||||||
int sectionIndex;
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
sectionIndex = sectionYPos;
|
|
||||||
#else
|
|
||||||
sectionIndex = level.getSectionIndexFromSectionY(sectionYPos);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
chunkSections[sectionIndex] = levelChunkSection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
|
|
||||||
if (sectionId >= 0
|
|
||||||
&& sectionId < chunkSections.length)
|
|
||||||
{
|
|
||||||
//========//
|
|
||||||
// blocks //
|
|
||||||
//========//
|
|
||||||
|
|
||||||
PalettedContainer<BlockState> blockStateContainer;
|
|
||||||
|
|
||||||
boolean containsBlockStates = CompoundTagUtil.contains(tagSection, "block_states", 10);
|
|
||||||
if (containsBlockStates)
|
|
||||||
{
|
|
||||||
Codec<PalettedContainer<BlockState>> blockStateCodec = getBlockStateCodec(level);
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_6
|
|
||||||
blockStateContainer = blockStateCodec
|
|
||||||
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
|
|
||||||
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
|
||||||
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
|
|
||||||
#else
|
|
||||||
blockStateContainer = blockStateCodec
|
|
||||||
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
|
|
||||||
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
|
||||||
.getOrThrow((message) -> logErrorAndReturnException(message));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
blocksFound = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_9
|
|
||||||
blockStateContainer = new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
|
||||||
#else
|
|
||||||
blockStateContainer = PalettedContainerFactory.create(level.registryAccess()).createForBlockStates();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//========//
|
|
||||||
// biomes //
|
|
||||||
//========//
|
|
||||||
|
|
||||||
Registry<Biome> biomeRegistry = getBiomeRegistry(level);
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
Codec<PalettedContainer<Biome>> biomeCodec;
|
|
||||||
#else
|
|
||||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec;
|
|
||||||
#endif
|
|
||||||
biomeCodec = getBiomeCodec(level, biomeRegistry);
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
PalettedContainer<Biome> biomeContainer;
|
|
||||||
#else
|
|
||||||
PalettedContainer<Holder<Biome>> biomeContainer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
biomeContainer = tagSection.contains("biomeRegistry", 10)
|
|
||||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomeRegistry")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, (message) -> logWarningOnce(message))
|
|
||||||
: new PalettedContainer<Biome>(biomeRegistry, biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
|
||||||
#else
|
|
||||||
{
|
|
||||||
CompoundTag biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomeRegistry");
|
|
||||||
if (biomeTag == null)
|
|
||||||
{
|
|
||||||
biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomes");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (biomeTag != null
|
|
||||||
&& !biomeTag.isEmpty())
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_20_6
|
|
||||||
biomeContainer = new PalettedContainer<Holder<Biome>>(
|
|
||||||
biomeRegistry.asHolderIdMap(),
|
|
||||||
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
|
||||||
#else
|
|
||||||
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, biomeTag)
|
|
||||||
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYCount, (String) string))
|
|
||||||
.getOrThrow((message) -> logErrorAndReturnException(message));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// no biomes found, use the default (probably plains)
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
biomeContainer = new PalettedContainer<Holder<Biome>>(
|
|
||||||
biomeRegistry.asHolderIdMap(),
|
|
||||||
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
|
||||||
#elif MC_VER < MC_1_21_9
|
|
||||||
biomeContainer = new PalettedContainer<Holder<Biome>>(biomeRegistry.asHolderIdMap(),
|
|
||||||
biomeRegistry.getOrThrow(Biomes.PLAINS),
|
|
||||||
PalettedContainer.Strategy.SECTION_BIOMES);
|
|
||||||
#else
|
|
||||||
biomeContainer = PalettedContainerFactory.create(level.registryAccess()).createForBiomes();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
|
|
||||||
#else
|
|
||||||
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return blocksFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Codec<PalettedContainer<BlockState>> getBlockStateCodec(LevelAccessor level)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
return null; // unused for older MC versions
|
|
||||||
#elif MC_VER < MC_1_19_2
|
|
||||||
return PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
|
||||||
#elif MC_VER <= MC_1_21_8
|
|
||||||
return PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
|
||||||
#else
|
|
||||||
return PalettedContainerFactory.create(level.registryAccess()).blockStatesContainerCodec();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Registry<Biome> getBiomeRegistry(LevelAccessor level)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
// not needed
|
|
||||||
return null;
|
|
||||||
#elif MC_VER < MC_1_19_4
|
|
||||||
return level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
return level.registryAccess().registryOrThrow(Registries.BIOME);
|
|
||||||
#else
|
|
||||||
return level.registryAccess().lookupOrThrow(Registries.BIOME);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
private static
|
|
||||||
#if MC_VER < MC_1_19_2 Codec<PalettedContainer<Biome>>
|
|
||||||
#else Codec<PalettedContainer<Holder<Biome>>>
|
|
||||||
#endif
|
|
||||||
getBiomeCodec(LevelAccessor level, Registry<Biome> biomeRegistry)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
return null; // unused for older MC versions
|
|
||||||
#elif MC_VER < MC_1_19_2
|
|
||||||
return PalettedContainer.codec(
|
|
||||||
biomeRegistry, biomeRegistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
|
|
||||||
#elif MC_VER < MC_1_19_2
|
|
||||||
return PalettedContainer.codec(
|
|
||||||
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
return PalettedContainer.codecRW(
|
|
||||||
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
|
|
||||||
#elif MC_VER < MC_1_21_9
|
|
||||||
return PalettedContainer.codecRW(
|
|
||||||
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
|
|
||||||
#else
|
|
||||||
return PalettedContainer.codecRW(
|
|
||||||
biomeRegistry.holderByNameCodec(), PalettedContainerFactory.create(level.registryAccess()).biomeStrategy(), biomeRegistry.getOrThrow(Biomes.PLAINS));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============//
|
|
||||||
// heightmaps //
|
|
||||||
//============//
|
|
||||||
|
|
||||||
private static boolean readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
|
|
||||||
{
|
|
||||||
CompoundTag tagHeightmaps = CompoundTagUtil.getCompoundTag(chunkData, "Heightmaps");
|
|
||||||
if (tagHeightmaps == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
|
|
||||||
{
|
|
||||||
String heightmapKey = type.getSerializationKey();
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
if (tagHeightmaps.contains(heightmapKey, 12))
|
|
||||||
{
|
|
||||||
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmapKey));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (tagHeightmaps.contains(heightmapKey))
|
|
||||||
{
|
|
||||||
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmapKey);
|
|
||||||
if (optionalHeightmap.isPresent())
|
|
||||||
{
|
|
||||||
chunk.setHeightmap(type, optionalHeightmap.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// chunk lighting //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
/** source: https://minecraft.wiki/w/Chunk_format */
|
|
||||||
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_17_1
|
|
||||||
// MC 1.16 and 1.17 doesn't have the necessary NBT info
|
|
||||||
return null;
|
|
||||||
#else
|
|
||||||
|
|
||||||
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(chunk));
|
|
||||||
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
|
|
||||||
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
|
|
||||||
|
|
||||||
boolean foundSkyLight = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
|
||||||
// get NBT tags info //
|
|
||||||
//===================//
|
|
||||||
|
|
||||||
Tag chunkSectionTags = chunkData.get("sections");
|
|
||||||
if (chunkSectionTags == null)
|
|
||||||
{
|
|
||||||
if (!lightingSectionErrorLogged)
|
|
||||||
{
|
|
||||||
lightingSectionErrorLogged = true;
|
|
||||||
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else if (!(chunkSectionTags instanceof ListTag))
|
|
||||||
{
|
|
||||||
if (!lightingSectionErrorLogged)
|
|
||||||
{
|
|
||||||
lightingSectionErrorLogged = true;
|
|
||||||
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===================//
|
|
||||||
// get lighting info //
|
|
||||||
//===================//
|
|
||||||
|
|
||||||
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
|
|
||||||
{
|
|
||||||
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
|
|
||||||
if (!(chunkSectionTag instanceof CompoundTag))
|
|
||||||
{
|
|
||||||
if (!lightingSectionErrorLogged)
|
|
||||||
{
|
|
||||||
lightingSectionErrorLogged = true;
|
|
||||||
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
|
|
||||||
|
|
||||||
|
|
||||||
// if null all lights = 0
|
|
||||||
byte[] blockLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "BlockLight");
|
|
||||||
byte[] skyLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "SkyLight");
|
|
||||||
|
|
||||||
if (blockLightNibbleArray != null
|
|
||||||
&& skyLightNibbleArray != null)
|
|
||||||
{
|
|
||||||
// if any sky light was found then all lights above will be max brightness
|
|
||||||
if (skyLightNibbleArray.length != 0)
|
|
||||||
{
|
|
||||||
foundSkyLight = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
|
|
||||||
{
|
|
||||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
|
||||||
{
|
|
||||||
// chunk sections are also 16 blocks tall
|
|
||||||
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
|
|
||||||
{
|
|
||||||
int blockPosIndex = relY*16*16 + relZ*16 + relX;
|
|
||||||
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
|
|
||||||
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
|
|
||||||
if (skyLightNibbleArray.length == 0 && foundSkyLight)
|
|
||||||
{
|
|
||||||
skyLight = LodUtil.MAX_MC_LIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
|
|
||||||
blockLightStorage.set(relX, y, relZ, blockLight);
|
|
||||||
skyLightStorage.set(relX, y, relZ, skyLight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinedStorage;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
|
|
||||||
private static byte getNibbleAtIndex(byte[] arr, int index)
|
|
||||||
{
|
|
||||||
if (index % 2 == 0)
|
|
||||||
{
|
|
||||||
return (byte)(arr[index/2] & 0x0F);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (byte)((arr[index/2]>>4) & 0x0F);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
|
||||||
// logging //
|
|
||||||
//=========//
|
|
||||||
|
|
||||||
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
|
||||||
{
|
|
||||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
|
||||||
{
|
|
||||||
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
|
||||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
|
||||||
|
|
||||||
return newMessage;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
|
||||||
{
|
|
||||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
|
||||||
{
|
|
||||||
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
|
||||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
|
||||||
|
|
||||||
return newMessage;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); }
|
|
||||||
private static void logParsingWarningOnce(String message, Exception e)
|
|
||||||
{
|
|
||||||
if (message == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
|
||||||
{
|
|
||||||
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
|
||||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.",
|
|
||||||
e);
|
|
||||||
|
|
||||||
return newMessage;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RuntimeException logErrorAndReturnException(String message)
|
|
||||||
{
|
|
||||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
|
||||||
{
|
|
||||||
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
|
||||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
|
||||||
|
|
||||||
return newMessage;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
|
|
||||||
return null; //new RuntimeException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// helper classes //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
public static class CombinedChunkLightStorage
|
|
||||||
{
|
|
||||||
public ChunkLightStorage blockLightStorage;
|
|
||||||
public ChunkLightStorage skyLightStorage;
|
|
||||||
|
|
||||||
public CombinedChunkLightStorage(int minY, int maxY)
|
|
||||||
{
|
|
||||||
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
|
|
||||||
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-343
@@ -1,343 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.RegionFileStorageExternalCache;
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
|
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
|
||||||
import net.minecraft.world.level.chunk.UpgradeData;
|
|
||||||
import net.minecraft.world.level.chunk.storage.IOWorker;
|
|
||||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.ClosedByInterruptException;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_17_1
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#elif MC_VER <= MC_1_19_2
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
#elif MC_VER <= MC_1_19_4
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#elif MC_VER <= MC_1_20_6
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
#elif MC_VER <= MC_1_21_3
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#elif MC_VER <= MC_1_21_8
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
||||||
#elif MC_VER <= MC_1_21_9
|
|
||||||
import net.minecraft.world.level.chunk.PalettedContainerFactory;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.chunk.PalettedContainerFactory;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public class ChunkFileReader implements AutoCloseable
|
|
||||||
{
|
|
||||||
|
|
||||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
|
||||||
.name("LOD World Gen")
|
|
||||||
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
|
|
||||||
.name("LOD Chunk Loading")
|
|
||||||
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final IModChecker MOD_CHECKER = SingletonInjector.INSTANCE.get(IModChecker.class);
|
|
||||||
|
|
||||||
public final GlobalWorldGenParams params;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* will be true if C2ME is installed (since they require us to
|
|
||||||
* pull chunks using their async method), or if there
|
|
||||||
* was an issue with the sync pulling method.
|
|
||||||
*/
|
|
||||||
private boolean pullExistingChunkUsingMcAsyncMethod = false;
|
|
||||||
|
|
||||||
private final AtomicReference<RegionFileStorageExternalCache> regionFileStorageCacheRef = new AtomicReference<>();
|
|
||||||
public RegionFileStorageExternalCache getOrCreateRegionFileCache(RegionFileStorage storage)
|
|
||||||
{
|
|
||||||
RegionFileStorageExternalCache cache = this.regionFileStorageCacheRef.get();
|
|
||||||
if (cache == null)
|
|
||||||
{
|
|
||||||
cache = new RegionFileStorageExternalCache(storage);
|
|
||||||
if (!this.regionFileStorageCacheRef.compareAndSet(null, cache))
|
|
||||||
{
|
|
||||||
cache = this.regionFileStorageCacheRef.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public ChunkFileReader(GlobalWorldGenParams params)
|
|
||||||
{
|
|
||||||
this.params = params;
|
|
||||||
|
|
||||||
if (MOD_CHECKER.isModLoaded("c2me"))
|
|
||||||
{
|
|
||||||
LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use methods handled by C2ME.");
|
|
||||||
this.pullExistingChunkUsingMcAsyncMethod = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the given chunk pos already exists in the world, that chunk will be returned,
|
|
||||||
* otherwise this will return an empty chunk.
|
|
||||||
*/
|
|
||||||
public CompletableFuture<ChunkWrapper> createEmptyOrPreExistingChunkWrapperAsync(
|
|
||||||
int chunkX, int chunkZ,
|
|
||||||
Map<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
|
|
||||||
Map<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
|
|
||||||
Map<DhChunkPos, ChunkWrapper> generatedChunkWrapperByDhPos)
|
|
||||||
{
|
|
||||||
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
|
|
||||||
DhChunkPos dhChunkPos = new DhChunkPos(chunkX, chunkZ);
|
|
||||||
|
|
||||||
if (generatedChunkWrapperByDhPos.containsKey(dhChunkPos))
|
|
||||||
{
|
|
||||||
return CompletableFuture.completedFuture(generatedChunkWrapperByDhPos.get(dhChunkPos));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getChunkNbtDataAsync(chunkPos)
|
|
||||||
.thenApply((CompoundTag chunkData) ->
|
|
||||||
{
|
|
||||||
ChunkWrapper newChunkWrapper = this.loadOrMakeChunkWrapper(chunkPos, chunkData);
|
|
||||||
|
|
||||||
// attempt to get chunk lighting
|
|
||||||
ChunkCompoundTagParser.CombinedChunkLightStorage combinedLights = ChunkCompoundTagParser.readLight(newChunkWrapper.getChunk(), chunkData);
|
|
||||||
if (combinedLights != null)
|
|
||||||
{
|
|
||||||
// may be empty, empty checks are handled later
|
|
||||||
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
|
|
||||||
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newChunkWrapper;
|
|
||||||
})
|
|
||||||
// separate handle so we can cleanly handle missing chunks and/or thrown errors
|
|
||||||
.handle((ChunkWrapper newChunkWrapper, Throwable throwable) ->
|
|
||||||
{
|
|
||||||
if (newChunkWrapper != null)
|
|
||||||
{
|
|
||||||
return newChunkWrapper;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return this.CreateProtoChunkWrapper(this.params.mcServerLevel, chunkPos);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.thenApply((ChunkWrapper newChunkWrapper) ->
|
|
||||||
{
|
|
||||||
generatedChunkWrapperByDhPos.put(dhChunkPos, newChunkWrapper);
|
|
||||||
return newChunkWrapper;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private CompletableFuture<CompoundTag> getChunkNbtDataAsync(ChunkPos chunkPos)
|
|
||||||
{
|
|
||||||
ServerLevel level = this.params.mcServerLevel;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
IOWorker ioWorker = level.getChunkSource().chunkMap.worker;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_18_2
|
|
||||||
return CompletableFuture.completedFuture(ioWorker.load(chunkPos));
|
|
||||||
#else
|
|
||||||
|
|
||||||
// storage will be null if C2ME is installed
|
|
||||||
if (!this.pullExistingChunkUsingMcAsyncMethod
|
|
||||||
&& ioWorker.storage != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RegionFileStorage storage = this.params.mcServerLevel.getChunkSource().chunkMap.worker.storage;
|
|
||||||
RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage);
|
|
||||||
return CompletableFuture.completedFuture(cache.read(chunkPos));
|
|
||||||
}
|
|
||||||
catch (NullPointerException e)
|
|
||||||
{
|
|
||||||
// this shouldn't happen, if anything is null it should be
|
|
||||||
// ioWorker.storage
|
|
||||||
// but just in case
|
|
||||||
LOGGER.error("Unexpected issue pulling pre-existing chunk ["+chunkPos+"], falling back to async chunk pulling. This may cause server-tick lag.", e);
|
|
||||||
this.pullExistingChunkUsingMcAsyncMethod = true;
|
|
||||||
|
|
||||||
// try again now using the async method
|
|
||||||
return this.getChunkNbtDataAsync(chunkPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// log if we unexpectedly weren't able to run the sync chunk pulling
|
|
||||||
if (!this.pullExistingChunkUsingMcAsyncMethod)
|
|
||||||
{
|
|
||||||
// this shouldn't happen, but just in case
|
|
||||||
LOGGER.info("Unable to pull pre-existing chunk using synchronous method. Falling back to async method. this may cause server-tick lag.");
|
|
||||||
this.pullExistingChunkUsingMcAsyncMethod = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//GET_CHUNK_COUNT_REF.incrementAndGet();
|
|
||||||
|
|
||||||
// When running in vanilla MC on versions before 1.21.4,
|
|
||||||
// DH would attempt to run loadAsync on this same thread via a threading mixin,
|
|
||||||
// to prevent causing lag on the server thread.
|
|
||||||
// However, if a mod like C2ME is installed this will run on a C2ME thread instead.
|
|
||||||
return ioWorker.loadAsync(chunkPos)
|
|
||||||
.thenApply(optional ->
|
|
||||||
{
|
|
||||||
// Debugging note:
|
|
||||||
// If there are reports of extreme memory use when C2ME is installed, that probably means
|
|
||||||
// this method is queuing a lot of tasks (1,000+), which causes C2ME to explode.
|
|
||||||
|
|
||||||
//GET_CHUNK_COUNT_REF.decrementAndGet();
|
|
||||||
//PREF_LOGGER.info("chunk getter count ["+F3Screen.NUMBER_FORMAT.format(GET_CHUNK_COUNT_REF.get())+"]");
|
|
||||||
return optional.orElse(null);
|
|
||||||
})
|
|
||||||
.exceptionally((throwable) ->
|
|
||||||
{
|
|
||||||
// unwrap the CompletionException if necessary
|
|
||||||
Throwable actualThrowable = throwable;
|
|
||||||
while (actualThrowable instanceof CompletionException completionException)
|
|
||||||
{
|
|
||||||
actualThrowable = completionException.getCause();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isShutdownException = ExceptionUtil.isShutdownException(actualThrowable);
|
|
||||||
if (!isShutdownException)
|
|
||||||
{
|
|
||||||
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (ClosedByInterruptException ignore)
|
|
||||||
{
|
|
||||||
// this just means the world generator is being shut down
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
CHUNK_LOAD_LOGGER.warn("Couldn't load or make chunk [" + chunkPos + "]. Error: [" + e.getMessage() + "].", e);
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private ChunkWrapper loadOrMakeChunkWrapper(ChunkPos chunkPos, CompoundTag chunkTagData)
|
|
||||||
{
|
|
||||||
ServerLevel mcServerLevel = this.params.mcServerLevel;
|
|
||||||
|
|
||||||
if (chunkTagData == null)
|
|
||||||
{
|
|
||||||
return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ChunkWrapper chunkWrapper = ChunkCompoundTagParser.createFromTag(mcServerLevel, this.params.dhServerLevel, chunkPos, chunkTagData);
|
|
||||||
if (chunkWrapper == null)
|
|
||||||
{
|
|
||||||
chunkWrapper = this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
|
|
||||||
}
|
|
||||||
return chunkWrapper;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
CHUNK_LOAD_LOGGER.error(
|
|
||||||
"DistantHorizons: couldn't load or make chunk at [" + chunkPos + "]." +
|
|
||||||
"Please try optimizing your world to fix this issue. \n" +
|
|
||||||
"World optimization can be done from the singleplayer world selection screen.\n" +
|
|
||||||
"Error: [" + e.getMessage() + "]."
|
|
||||||
, e);
|
|
||||||
|
|
||||||
return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChunkWrapper CreateProtoChunkWrapper(ServerLevel level, ChunkPos chunkPos)
|
|
||||||
{
|
|
||||||
ProtoChunk chunk = CreateProtoChunk(level, chunkPos);
|
|
||||||
return new ChunkWrapper(chunk, this.params.dhServerLevel.getLevelWrapper());
|
|
||||||
}
|
|
||||||
public static ProtoChunk CreateProtoChunk(ServerLevel level, ChunkPos chunkPos)
|
|
||||||
{
|
|
||||||
#if MC_VER <= MC_1_16_5
|
|
||||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY);
|
|
||||||
#elif MC_VER <= MC_1_17_1
|
|
||||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level);
|
|
||||||
#elif MC_VER <= MC_1_19_2
|
|
||||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null);
|
|
||||||
#elif MC_VER <= MC_1_19_4
|
|
||||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
|
|
||||||
#elif MC_VER < MC_1_21_3
|
|
||||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
|
|
||||||
#elif MC_VER < MC_1_21_9
|
|
||||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().lookupOrThrow(Registries.BIOME), null);
|
|
||||||
#else
|
|
||||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, PalettedContainerFactory.create(level.registryAccess()), null);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// base overrides //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close()
|
|
||||||
{
|
|
||||||
RegionFileStorageExternalCache regionStorage = this.regionFileStorageCacheRef.get();
|
|
||||||
if (regionStorage != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
regionStorage.close();
|
|
||||||
}
|
|
||||||
catch (ClosedChannelException ignore) { /* world generator is being shut down */ }
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Failed to close region file storage cache, error: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-148
@@ -1,148 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
|
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* these tag helpers are usedd to simplify tag accessing between MC versions
|
|
||||||
*/
|
|
||||||
public class CompoundTagUtil
|
|
||||||
{
|
|
||||||
|
|
||||||
/** defaults to "false" if the tag isn't present */
|
|
||||||
public static boolean getBoolean(CompoundTag tag, String key)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getBoolean(key);
|
|
||||||
#else
|
|
||||||
return tag.getBoolean(key).orElse(false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/** defaults to "0" if the tag isn't present */
|
|
||||||
public static byte getByte(CompoundTag tag, String key)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getByte(key);
|
|
||||||
#else
|
|
||||||
return tag.getByte(key).orElse((byte)0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/** defaults to "0" if the tag isn't present */
|
|
||||||
public static short getShort(ListTag tag, int index)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getShort(index);
|
|
||||||
#else
|
|
||||||
return tag.getShort(index).orElse((short)0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/** defaults to "0" if the tag isn't present */
|
|
||||||
public static int getInt(CompoundTag tag, String key)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getInt(key);
|
|
||||||
#else
|
|
||||||
return tag.getInt(key).orElse(0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/** defaults to "0" if the tag isn't present */
|
|
||||||
public static long getLong(CompoundTag tag, String key)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getInt(key);
|
|
||||||
#else
|
|
||||||
return tag.getLong(key).orElse(0L);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** defaults to null if the tag isn't present */
|
|
||||||
@Nullable
|
|
||||||
public static String getString(CompoundTag tag, String key)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getString(key);
|
|
||||||
#else
|
|
||||||
return tag.getString(key).orElse(null);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/** defaults to null if the tag isn't present */
|
|
||||||
@Nullable
|
|
||||||
public static byte[] getByteArray(CompoundTag tag, String key)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getByteArray(key);
|
|
||||||
#else
|
|
||||||
return tag.getByteArray(key).orElse(null);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** defaults to null if the tag isn't present */
|
|
||||||
@Nullable
|
|
||||||
public static CompoundTag getCompoundTag(CompoundTag tag, String key)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getCompound(key);
|
|
||||||
#else
|
|
||||||
return tag.getCompound(key).orElse(null);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/** defaults to null if the tag isn't present */
|
|
||||||
@Nullable
|
|
||||||
public static CompoundTag getCompoundTag(ListTag tag, int index)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getCompound(index);
|
|
||||||
#else
|
|
||||||
return tag.getCompound(index).orElse(null);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* defaults to null if the tag isn't present
|
|
||||||
* @param elementType unused after MC 1.21.5
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static ListTag getListTag(CompoundTag tag, String key, int elementType)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getList(key, elementType);
|
|
||||||
#else
|
|
||||||
return tag.getList(key).orElse(null);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/** defaults to null if the tag isn't present */
|
|
||||||
@Nullable
|
|
||||||
public static ListTag getListTag(ListTag tag, int index)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.getList(index);
|
|
||||||
#else
|
|
||||||
return tag.getList(index).orElse(null);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean contains(CompoundTag tag, String key, int index)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
return tag.contains(key, index);
|
|
||||||
#else
|
|
||||||
return tag.contains(key);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-28
@@ -1,28 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_21_1
|
|
||||||
|
|
||||||
import net.minecraft.server.level.GenerationChunkHolder;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
public class DhGenerationChunkHolder extends GenerationChunkHolder
|
|
||||||
{
|
|
||||||
|
|
||||||
public DhGenerationChunkHolder(ChunkPos pos) { super(pos); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTicketLevel() { return 0; }
|
|
||||||
@Override
|
|
||||||
public int getQueueLevel() { return 0; }
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
protected void addSaveDependency(CompletableFuture<?> completableFuture) { }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
-478
@@ -1,478 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import net.minecraft.world.level.block.SpawnerBlock;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Cursor3D;
|
|
||||||
import net.minecraft.core.SectionPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
|
||||||
import net.minecraft.util.Mth;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.ColorResolver;
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
|
||||||
#endif
|
|
||||||
import net.minecraft.world.level.LightLayer;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.block.Blocks;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
|
||||||
|
|
||||||
#if MC_VER <= MC_1_20_4
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
#else
|
|
||||||
import net.minecraft.world.level.chunk.status.*;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_21_1
|
|
||||||
import net.minecraft.util.StaticCache2D;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import net.minecraft.server.level.GenerationChunkHolder;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.material.Fluid;
|
|
||||||
import net.minecraft.world.ticks.BlackholeTickAccess;
|
|
||||||
import net.minecraft.world.ticks.LevelTickAccess;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
public class DhLitWorldGenRegion extends WorldGenRegion
|
|
||||||
{
|
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
|
||||||
|
|
||||||
private static ChunkStatus debugTriggeredForStatus = null;
|
|
||||||
|
|
||||||
|
|
||||||
public final ServerLevel serverLevel;
|
|
||||||
public final DummyLightEngine lightEngine;
|
|
||||||
public final BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator;
|
|
||||||
public final int writeRadius;
|
|
||||||
public final int size;
|
|
||||||
|
|
||||||
private final ChunkPos firstPos;
|
|
||||||
private final List<ChunkAccess> cache;
|
|
||||||
private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
|
|
||||||
* specifically: "it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap.getAndMoveToFirst()"
|
|
||||||
*/
|
|
||||||
private final ReentrantLock getChunkLock = new ReentrantLock();
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
|
||||||
private ChunkPos overrideCenterPos = null;
|
|
||||||
|
|
||||||
public void setOverrideCenter(ChunkPos pos) { overrideCenterPos = pos; }
|
|
||||||
#if MC_VER < MC_1_17_1
|
|
||||||
@Override
|
|
||||||
public int getCenterX()
|
|
||||||
{
|
|
||||||
return overrideCenterPos==null ? super.getCenterX() : overrideCenterPos.x;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int getCenterZ()
|
|
||||||
{
|
|
||||||
return overrideCenterPos==null ? super.getCenterX() : overrideCenterPos.z;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public ChunkPos getCenter()
|
|
||||||
{
|
|
||||||
return overrideCenterPos == null ? super.getCenter() : overrideCenterPos;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public DhLitWorldGenRegion(
|
|
||||||
int centerChunkX, int centerChunkZ,
|
|
||||||
ChunkAccess centerChunk,
|
|
||||||
ServerLevel serverLevel, DummyLightEngine lightEngine,
|
|
||||||
List<ChunkAccess> chunkList, ChunkStatus chunkStatus, int writeRadius,
|
|
||||||
BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator)
|
|
||||||
{
|
|
||||||
#if MC_VER == MC_1_16_5
|
|
||||||
super(serverLevel, chunkList);
|
|
||||||
#elif MC_VER < MC_1_21_1
|
|
||||||
super(serverLevel, chunkList, chunkStatus, writeRadius);
|
|
||||||
#else
|
|
||||||
super(serverLevel,
|
|
||||||
StaticCache2D.create(
|
|
||||||
centerChunkX, centerChunkZ,
|
|
||||||
writeRadius * 2, (x,z) -> new DhGenerationChunkHolder(new ChunkPos(x, z))),
|
|
||||||
new ChunkStep(chunkStatus,
|
|
||||||
// reverse is needed because MC uses the index of the chunkStatus to determine how many items are in the list instead of the actual list count
|
|
||||||
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
|
|
||||||
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
|
|
||||||
writeRadius, (WorldGenContext var1, ChunkStep var2, StaticCache2D<GenerationChunkHolder> var3, ChunkAccess var4) -> null),
|
|
||||||
centerChunk);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.firstPos = chunkList.get(0).getPos();
|
|
||||||
this.serverLevel = serverLevel;
|
|
||||||
this.generator = generator;
|
|
||||||
this.lightEngine = lightEngine;
|
|
||||||
this.writeRadius = writeRadius;
|
|
||||||
this.cache = chunkList;
|
|
||||||
this.size = Mth.floor(Math.sqrt(chunkList.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
// Bypass BCLib mixin overrides.
|
|
||||||
@Override
|
|
||||||
public boolean ensureCanWrite(BlockPos blockPos)
|
|
||||||
{
|
|
||||||
int i = SectionPos.blockToSectionCoord(blockPos.getX());
|
|
||||||
int j = SectionPos.blockToSectionCoord(blockPos.getZ());
|
|
||||||
ChunkPos chunkPos = this.getCenter();
|
|
||||||
ChunkAccess center = this.getChunk(chunkPos.x, chunkPos.z);
|
|
||||||
int k = Math.abs(chunkPos.x - i);
|
|
||||||
int l = Math.abs(chunkPos.z - j);
|
|
||||||
if (k > this.writeRadius || l > this.writeRadius)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
if (center.isUpgrading())
|
|
||||||
{
|
|
||||||
LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration();
|
|
||||||
|
|
||||||
int minY;
|
|
||||||
int maxY;
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
minY = levelHeightAccessor.getMinBuildHeight();
|
|
||||||
maxY = levelHeightAccessor.getMaxBuildHeight();
|
|
||||||
#else
|
|
||||||
minY = levelHeightAccessor.getMinY();
|
|
||||||
maxY = levelHeightAccessor.getMaxY();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (blockPos.getY() < minY || blockPos.getY() >= maxY)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
@Override
|
|
||||||
@NotNull
|
|
||||||
public LevelTickAccess<Block> getBlockTicks()
|
|
||||||
{
|
|
||||||
// DH world gen doesn't need ticking, so return the BlackholeTickAccess list (which causes all ticks to be ignored).
|
|
||||||
// If this isn't done the server may attempt to tick chunks outside the vanilla render distance,
|
|
||||||
// which can throw warnings or cause other issues
|
|
||||||
return BlackholeTickAccess.emptyLevelList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NotNull
|
|
||||||
public LevelTickAccess<Fluid> getFluidTicks() { return BlackholeTickAccess.emptyLevelList(); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO Check this
|
|
||||||
// @Override
|
|
||||||
// public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
|
|
||||||
// StructureFeature<?> structureFeature) {
|
|
||||||
// return structFeat.startsForFeature(sectionPos, structureFeature);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Skip updating the related tile entities
|
|
||||||
@Override
|
|
||||||
public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j)
|
|
||||||
{
|
|
||||||
ChunkAccess chunkAccess = this.getChunk(blockPos);
|
|
||||||
if (chunkAccess instanceof LevelChunk)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_5
|
|
||||||
chunkAccess.setBlockState(blockPos, blockState, /*isBlockMoving*/false);
|
|
||||||
#else
|
|
||||||
chunkAccess.setBlockState(blockPos, blockState, /*flags*/0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This is for post ticking for water on gen and stuff like that. Not enabled
|
|
||||||
// for now.
|
|
||||||
// if (blockState.hasPostProcess(this, blockPos))
|
|
||||||
// this.getChunk(blockPos).markPosForPostprocessing(blockPos);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip Dropping the item on destroy
|
|
||||||
@Override
|
|
||||||
public boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i)
|
|
||||||
{
|
|
||||||
BlockState blockState = this.getBlockState(blockPos);
|
|
||||||
if (blockState.isAir())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip BlockEntity stuff. It aren't really needed
|
|
||||||
@Override
|
|
||||||
public BlockEntity getBlockEntity(BlockPos blockPos)
|
|
||||||
{
|
|
||||||
BlockState blockState = this.getBlockState(blockPos);
|
|
||||||
|
|
||||||
// This is a bypass for the spawner block since MC complains about not having it
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
if (blockState.getBlock() instanceof SpawnerBlock)
|
|
||||||
{
|
|
||||||
return ((EntityBlock) blockState.getBlock()).newBlockEntity(blockPos, blockState);
|
|
||||||
}
|
|
||||||
else return null;
|
|
||||||
#else
|
|
||||||
if (blockState.getBlock() instanceof SpawnerBlock) {
|
|
||||||
return ((EntityBlock) blockState.getBlock()).newBlockEntity(this);
|
|
||||||
} else return null;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This needs to be manually overridden to make sure Lithium 0.11.2 and lower
|
|
||||||
* don't try to get null chunks. <br><br>
|
|
||||||
*
|
|
||||||
* Problematic Lithium code was removed in 0.13.0 (MC 1.21.1) and higher: <br>
|
|
||||||
* https://github.com/CaffeineMC/lithium-fabric/commit/b7cfd53a1ed0197e1d13dea2799b898eb52ecab3
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public BlockState getBlockState(BlockPos blockPos)
|
|
||||||
{
|
|
||||||
int chunkX = SectionPos.blockToSectionCoord(blockPos.getX());
|
|
||||||
int chunkZ = SectionPos.blockToSectionCoord(blockPos.getZ());
|
|
||||||
return this.getChunk(chunkX, chunkZ).getBlockState(blockPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Skip BlockEntity stuff. They aren't needed for our use case. */
|
|
||||||
@Override
|
|
||||||
public boolean addFreshEntity(@NotNull Entity entity) { return true; }
|
|
||||||
|
|
||||||
// Allays have empty chunks even if it's outside the worldGenRegion
|
|
||||||
// @Override
|
|
||||||
// public boolean hasChunk(int i, int j) {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Override to ensure no other mod mixins cause skipping the overrided
|
|
||||||
// getChunk(...)
|
|
||||||
@Override
|
|
||||||
public @NotNull ChunkAccess getChunk(int chunkX, int chunkZ)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// lock is to prevent issues with underlying MC code that doesn't support multithreading
|
|
||||||
this.getChunkLock.lock();
|
|
||||||
return this.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.getChunkLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override to ensure no other mod mixins cause skipping the overrided
|
|
||||||
// getChunk(...)
|
|
||||||
@Override
|
|
||||||
public @NotNull ChunkAccess getChunk(int chunkX, int chunkZ, @NotNull ChunkStatus chunkStatus)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// lock is to prevent issues with underlying MC code that doesn't support multithreading
|
|
||||||
this.getChunkLock.lock();
|
|
||||||
|
|
||||||
ChunkAccess chunk = this.getChunk(chunkX, chunkZ, chunkStatus, true);
|
|
||||||
if (chunk == null)
|
|
||||||
{
|
|
||||||
LodUtil.assertNotReach("getChunk shouldn't return null values");
|
|
||||||
}
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.getChunkLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Allows creating empty chunks even if they're outside the worldGenRegion */
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public ChunkAccess getChunk(int chunkX, int chunkZ, @NotNull ChunkStatus chunkStatus, boolean returnNonNull)
|
|
||||||
{
|
|
||||||
ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull);
|
|
||||||
if (chunk instanceof LevelChunk)
|
|
||||||
{
|
|
||||||
chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 ,/* allow writes */ false #endif );
|
|
||||||
}
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param returnNonNull if true this method will always return a non-null chunk,
|
|
||||||
* if false it will return null if no chunk exists at the given position with the given status
|
|
||||||
*/
|
|
||||||
private ChunkAccess getChunkAccess(int chunkX, int chunkZ, ChunkStatus chunkStatus, boolean returnNonNull)
|
|
||||||
{
|
|
||||||
ChunkAccess chunk = this.superHasChunk(chunkX, chunkZ) ? this.superGetChunk(chunkX, chunkZ) : null;
|
|
||||||
if (chunk != null && ChunkWrapper.getStatus(chunk).isOrAfter(chunkStatus))
|
|
||||||
{
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
else if (!returnNonNull)
|
|
||||||
{
|
|
||||||
// no chunk found with the necessary status and null return values are allowed
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// we need a non-null chunk
|
|
||||||
if (chunk == null)
|
|
||||||
{
|
|
||||||
// check memory
|
|
||||||
chunk = this.chunkMap.get(ChunkPos.asLong(chunkX, chunkZ));
|
|
||||||
if (chunk == null)
|
|
||||||
{
|
|
||||||
// chunk isn't in memory, generate a new one
|
|
||||||
chunk = this.generator.getChunk(chunkX, chunkZ);
|
|
||||||
if (chunk == null)
|
|
||||||
{
|
|
||||||
throw new NullPointerException("The provided generator should not return null!");
|
|
||||||
}
|
|
||||||
this.chunkMap.put(ChunkPos.asLong(chunkX, chunkZ), chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunkStatus != ChunkStatus.EMPTY
|
|
||||||
&& chunkStatus != debugTriggeredForStatus)
|
|
||||||
{
|
|
||||||
LOGGER.info("WorldGen requiring [" + chunkStatus + "]"
|
|
||||||
+ " is outside the expected range. Returning EMPTY chunk.");
|
|
||||||
debugTriggeredForStatus = chunkStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Use this instead of super.hasChunk() to bypass C2ME concurrency checks */
|
|
||||||
public boolean superHasChunk(int x, int z)
|
|
||||||
{
|
|
||||||
int k = x - this.firstPos.x;
|
|
||||||
int l = z - this.firstPos.z;
|
|
||||||
return l >= 0 && l < this.size && k >= 0 && k < this.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Use this instead of super.getChunk() to bypass C2ME concurrency checks */
|
|
||||||
private ChunkAccess superGetChunk(int x, int z)
|
|
||||||
{
|
|
||||||
int k = x - this.firstPos.x;
|
|
||||||
int l = z - this.firstPos.z;
|
|
||||||
return this.cache.get(k + l * this.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Overriding allows us to use our own lighting engine */
|
|
||||||
@Override
|
|
||||||
public @NotNull LevelLightEngine getLightEngine() { return this.lightEngine; }
|
|
||||||
|
|
||||||
/** Overriding allows us to use our own lighting engine */
|
|
||||||
@Override
|
|
||||||
public int getBrightness(@NotNull LightLayer lightLayer, @NotNull BlockPos blockPos) { return 0; }
|
|
||||||
|
|
||||||
/** Overriding allows us to use our own lighting engine */
|
|
||||||
@Override
|
|
||||||
public int getRawBrightness(@NotNull BlockPos blockPos, int i) { return 0; }
|
|
||||||
|
|
||||||
/** Overriding allows us to use our own lighting engine */
|
|
||||||
@Override
|
|
||||||
public boolean canSeeSky(@NotNull BlockPos blockPos)
|
|
||||||
{ return (this.getBrightness(LightLayer.SKY, blockPos) >= LodUtil.MAX_MC_LIGHT); }
|
|
||||||
|
|
||||||
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
|
|
||||||
{ return this.calculateBlockTint(blockPos, colorResolver); }
|
|
||||||
|
|
||||||
private Biome _getBiome(BlockPos pos)
|
|
||||||
{
|
|
||||||
#if MC_VER >= MC_1_18_2
|
|
||||||
return this.getBiome(pos).value();
|
|
||||||
#else
|
|
||||||
return this.getBiome(pos);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
|
||||||
{
|
|
||||||
#if MC_VER < MC_1_19_2
|
|
||||||
int i = (Minecraft.getInstance()).options.biomeBlendRadius;
|
|
||||||
#else
|
|
||||||
int i = (Minecraft.getInstance()).options.biomeBlendRadius().get();
|
|
||||||
#endif
|
|
||||||
if (i == 0)
|
|
||||||
return colorResolver.getColor((Biome) _getBiome(blockPos), blockPos.getX(), blockPos.getZ());
|
|
||||||
int j = (i * 2 + 1) * (i * 2 + 1);
|
|
||||||
int k = 0;
|
|
||||||
int l = 0;
|
|
||||||
int m = 0;
|
|
||||||
Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
|
|
||||||
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
||||||
while (cursor3D.advance())
|
|
||||||
{
|
|
||||||
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
|
|
||||||
int n = colorResolver.getColor((Biome) _getBiome((BlockPos) mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
|
|
||||||
k += (n & 0xFF0000) >> 16;
|
|
||||||
l += (n & 0xFF00) >> 8;
|
|
||||||
m += n & 0xFF;
|
|
||||||
}
|
|
||||||
return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-99
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.lighting.*;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.SectionPos;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.LightLayer;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.DataLayer;
|
|
||||||
|
|
||||||
public class DummyLightEngine extends LevelLightEngine
|
|
||||||
{
|
|
||||||
|
|
||||||
public DummyLightEngine(LightGetterAdaptor genRegion)
|
|
||||||
{
|
|
||||||
super(genRegion, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_20_1
|
|
||||||
@Override
|
|
||||||
public void onBlockEmissionIncrease(BlockPos blockPos, int i) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int runUpdates(int i, boolean bl, boolean bl2) { return 0; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enableLightSources(ChunkPos chunkPos, boolean bl) { }
|
|
||||||
|
|
||||||
#else
|
|
||||||
@Override
|
|
||||||
public int runLightUpdates() { return 0; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLightEnabled(ChunkPos $$0, boolean $$1) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void propagateLightSources(ChunkPos arg) { }
|
|
||||||
|
|
||||||
public boolean lightOnInSection(SectionPos $$0) { return false; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer #if MC_VER < MC_1_20_1 , boolean bl #endif ) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkBlock(BlockPos blockPos) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasLightWork() { return false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateSectionStatus(SectionPos sectionPos, boolean bl) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LayerLightEventListener getLayerListener(LightLayer lightLayer) { return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRawBrightness(BlockPos blockPos, int i) { return 0; }
|
|
||||||
|
|
||||||
public void lightChunk(ChunkAccess chunkAccess, boolean needLightBlockUpdate) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDebugData(LightLayer lightLayer, SectionPos sectionPos) { throw new UnsupportedOperationException("This should never be used!"); }
|
|
||||||
@Override
|
|
||||||
public void retainData(ChunkPos chunkPos, boolean bl) { }
|
|
||||||
|
|
||||||
#if MC_VER >= MC_1_17_1
|
|
||||||
@Override
|
|
||||||
public int getLightSectionCount() { throw new UnsupportedOperationException("This should never be used!"); }
|
|
||||||
@Override
|
|
||||||
public int getMinLightSection() { throw new UnsupportedOperationException("This should never be used!"); }
|
|
||||||
@Override
|
|
||||||
public int getMaxLightSection() { throw new UnsupportedOperationException("This should never be used!"); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user