mirror of
https://github.com/songoda/EpicAnchors.git
synced 2024-11-29 05:26:13 +01:00
Release v2.0.0-ALPHA (Merge pull request #2 from songoda/full-recode)
This commit is contained in:
commit
273844b0af
311
.editorconfig
Normal file
311
.editorconfig
Normal file
@ -0,0 +1,311 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
# max_line_length = 120
|
||||
tab_width = 4
|
||||
trim_trailing_whitespace = true
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_smart_tabs = false
|
||||
ij_visual_guides = none
|
||||
ij_wrap_on_typing = false
|
||||
|
||||
[*.java]
|
||||
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_extends_list = false
|
||||
ij_java_align_multiline_for = true
|
||||
ij_java_align_multiline_method_parentheses = false
|
||||
ij_java_align_multiline_parameters = true
|
||||
ij_java_align_multiline_parameters_in_calls = false
|
||||
ij_java_align_multiline_parenthesized_expression = false
|
||||
ij_java_align_multiline_records = true
|
||||
ij_java_align_multiline_resources = true
|
||||
ij_java_align_multiline_ternary_operation = false
|
||||
ij_java_align_multiline_text_blocks = false
|
||||
ij_java_align_multiline_throws_list = false
|
||||
ij_java_align_subsequent_simple_methods = false
|
||||
ij_java_align_throws_keyword = false
|
||||
ij_java_annotation_parameter_wrap = off
|
||||
ij_java_array_initializer_new_line_after_left_brace = false
|
||||
ij_java_array_initializer_right_brace_on_new_line = false
|
||||
ij_java_array_initializer_wrap = off
|
||||
ij_java_assert_statement_colon_on_next_line = false
|
||||
ij_java_assert_statement_wrap = off
|
||||
ij_java_assignment_wrap = off
|
||||
ij_java_binary_operation_sign_on_next_line = false
|
||||
ij_java_binary_operation_wrap = off
|
||||
ij_java_blank_lines_after_anonymous_class_header = 0
|
||||
ij_java_blank_lines_after_class_header = 0
|
||||
ij_java_blank_lines_after_imports = 1
|
||||
ij_java_blank_lines_after_package = 1
|
||||
ij_java_blank_lines_around_class = 1
|
||||
ij_java_blank_lines_around_field = 0
|
||||
ij_java_blank_lines_around_field_in_interface = 0
|
||||
ij_java_blank_lines_around_initializer = 1
|
||||
ij_java_blank_lines_around_method = 1
|
||||
ij_java_blank_lines_around_method_in_interface = 1
|
||||
ij_java_blank_lines_before_class_end = 0
|
||||
ij_java_blank_lines_before_imports = 1
|
||||
ij_java_blank_lines_before_method_body = 0
|
||||
ij_java_blank_lines_before_package = 0
|
||||
ij_java_block_brace_style = end_of_line
|
||||
ij_java_block_comment_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 = off
|
||||
ij_java_case_statement_on_separate_line = true
|
||||
ij_java_catch_on_new_line = false
|
||||
ij_java_class_annotation_wrap = split_into_lines
|
||||
ij_java_class_brace_style = end_of_line
|
||||
ij_java_class_count_to_use_import_on_demand = 15
|
||||
ij_java_class_names_in_javadoc = 1
|
||||
ij_java_do_not_indent_top_level_class_members = false
|
||||
ij_java_do_not_wrap_after_single_annotation = 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 = true
|
||||
ij_java_doc_add_blank_line_after_return = true
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_align_exception_comments = true
|
||||
ij_java_doc_align_param_comments = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = true
|
||||
ij_java_doc_enable_formatting = true
|
||||
ij_java_doc_enable_leading_asterisks = true
|
||||
ij_java_doc_indent_on_continuation = true
|
||||
ij_java_doc_keep_empty_lines = true
|
||||
ij_java_doc_keep_empty_parameter_tag = true
|
||||
ij_java_doc_keep_empty_return_tag = true
|
||||
ij_java_doc_keep_empty_throws_tag = true
|
||||
ij_java_doc_keep_invalid_tags = true
|
||||
ij_java_doc_param_description_on_new_line = false
|
||||
ij_java_doc_preserve_line_breaks = false
|
||||
ij_java_doc_use_throws_not_exception_tag = true
|
||||
ij_java_else_on_new_line = false
|
||||
ij_java_enum_constants_wrap = off
|
||||
ij_java_extends_keyword_wrap = off
|
||||
ij_java_extends_list_wrap = off
|
||||
ij_java_field_annotation_wrap = split_into_lines
|
||||
ij_java_finally_on_new_line = false
|
||||
ij_java_for_brace_force = never
|
||||
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 = 0
|
||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 0
|
||||
ij_java_keep_blank_lines_in_code = 1
|
||||
ij_java_keep_blank_lines_in_declarations = 1
|
||||
ij_java_keep_builder_methods_indents = false
|
||||
ij_java_keep_control_statement_in_one_line = true
|
||||
ij_java_keep_first_column_comment = true
|
||||
ij_java_keep_indents_on_empty_lines = false
|
||||
ij_java_keep_line_breaks = true
|
||||
ij_java_keep_multiple_expressions_in_one_line = false
|
||||
ij_java_keep_simple_blocks_in_one_line = false
|
||||
ij_java_keep_simple_classes_in_one_line = 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_at_first_column = true
|
||||
ij_java_method_annotation_wrap = split_into_lines
|
||||
ij_java_method_brace_style = end_of_line
|
||||
ij_java_method_call_chain_wrap = off
|
||||
ij_java_method_parameters_new_line_after_left_paren = false
|
||||
ij_java_method_parameters_right_paren_on_new_line = false
|
||||
ij_java_method_parameters_wrap = off
|
||||
ij_java_modifier_list_wrap = false
|
||||
ij_java_names_count_to_use_import_on_demand = 9
|
||||
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 = true
|
||||
ij_java_replace_null_check = true
|
||||
ij_java_replace_sum_lambda_with_method_ref = true
|
||||
ij_java_resource_list_new_line_after_left_paren = false
|
||||
ij_java_resource_list_right_paren_on_new_line = false
|
||||
ij_java_resource_list_wrap = off
|
||||
ij_java_rparen_on_new_line_in_record_header = false
|
||||
ij_java_space_after_closing_angle_bracket_in_type_argument = false
|
||||
ij_java_space_after_colon = true
|
||||
ij_java_space_after_comma = true
|
||||
ij_java_space_after_comma_in_type_arguments = true
|
||||
ij_java_space_after_for_semicolon = true
|
||||
ij_java_space_after_quest = true
|
||||
ij_java_space_after_type_cast = true
|
||||
ij_java_space_before_annotation_array_initializer_left_brace = false
|
||||
ij_java_space_before_annotation_parameter_list = false
|
||||
ij_java_space_before_array_initializer_left_brace = true
|
||||
ij_java_space_before_catch_keyword = true
|
||||
ij_java_space_before_catch_left_brace = true
|
||||
ij_java_space_before_catch_parentheses = true
|
||||
ij_java_space_before_class_left_brace = true
|
||||
ij_java_space_before_colon = true
|
||||
ij_java_space_before_colon_in_foreach = true
|
||||
ij_java_space_before_comma = false
|
||||
ij_java_space_before_do_left_brace = true
|
||||
ij_java_space_before_else_keyword = true
|
||||
ij_java_space_before_else_left_brace = true
|
||||
ij_java_space_before_finally_keyword = true
|
||||
ij_java_space_before_finally_left_brace = true
|
||||
ij_java_space_before_for_left_brace = true
|
||||
ij_java_space_before_for_parentheses = true
|
||||
ij_java_space_before_for_semicolon = false
|
||||
ij_java_space_before_if_left_brace = true
|
||||
ij_java_space_before_if_parentheses = true
|
||||
ij_java_space_before_method_call_parentheses = false
|
||||
ij_java_space_before_method_left_brace = true
|
||||
ij_java_space_before_method_parentheses = false
|
||||
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
||||
ij_java_space_before_quest = true
|
||||
ij_java_space_before_switch_left_brace = true
|
||||
ij_java_space_before_switch_parentheses = true
|
||||
ij_java_space_before_synchronized_left_brace = true
|
||||
ij_java_space_before_synchronized_parentheses = true
|
||||
ij_java_space_before_try_left_brace = true
|
||||
ij_java_space_before_try_parentheses = true
|
||||
ij_java_space_before_type_parameter_list = false
|
||||
ij_java_space_before_while_keyword = true
|
||||
ij_java_space_before_while_left_brace = true
|
||||
ij_java_space_before_while_parentheses = true
|
||||
ij_java_space_inside_one_line_enum_braces = false
|
||||
ij_java_space_within_empty_array_initializer_braces = true
|
||||
ij_java_space_within_empty_method_call_parentheses = false
|
||||
ij_java_space_within_empty_method_parentheses = false
|
||||
ij_java_spaces_around_additive_operators = true
|
||||
ij_java_spaces_around_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_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 = off
|
||||
ij_java_test_name_suffix = Test
|
||||
ij_java_throws_keyword_wrap = off
|
||||
ij_java_throws_list_wrap = off
|
||||
ij_java_use_external_annotations = false
|
||||
ij_java_use_fq_class_names = false
|
||||
ij_java_use_relative_indents = false
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_variable_annotation_wrap = off
|
||||
ij_java_visibility = public
|
||||
ij_java_while_brace_force = never
|
||||
ij_java_while_on_new_line = false
|
||||
ij_java_wrap_comments = false
|
||||
ij_java_wrap_first_method_in_call_chain = false
|
||||
ij_java_wrap_long_lines = false
|
||||
|
||||
[*.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, *.pom, *.rng, *.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_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
|
||||
|
||||
[{*.markdown, *.md}]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
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_keep_indents_on_empty_lines = false
|
||||
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
|
||||
|
||||
[{*.yaml, *.yml, *.lang}]
|
||||
indent_size = 2
|
||||
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
|
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: songoda
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "maven" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "monthly"
|
96
.github/workflows/maven.yml
vendored
Normal file
96
.github/workflows/maven.yml
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
name: 'Build & Test'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, development, full-recode ]
|
||||
pull_request:
|
||||
types: [ opened, synchronize, reopened ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Setup Java
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: 16
|
||||
distribution: adopt
|
||||
|
||||
# Checkout project files
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# Caches
|
||||
- name: 'Cache: Maven'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-maven-
|
||||
|
||||
# Build project
|
||||
- name: Build with Maven
|
||||
run: 'mvn -B -Duser.name="GitHub Runner on $GITHUB_REPOSITORY (id=$GITHUB_RUN_ID)" clean package'
|
||||
|
||||
# Upload build artifacts
|
||||
- name: 'Upload Build Artifact: EpicAnchors-*.jar'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: EpicAnchors-artifacts
|
||||
path: ./target/EpicAnchors-*.jar
|
||||
|
||||
##
|
||||
# Discord Webhook
|
||||
# TODO: Extract into external Action for better re-usability (and readability) [Copied SongodaCore]
|
||||
##
|
||||
- name: 'Discord Webhook (Success)'
|
||||
if: ${{ success() && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development' || github.ref == 'refs/heads/full-recode') }}
|
||||
continue-on-error: true
|
||||
run: |
|
||||
curl -X POST --data "{\"content\":null,\"embeds\":[{\"title\":\"Build succeeded!\",\"description\":\"The build with the ID #$GITHUB_RUN_NUMBER has succeeded!\",\"url\":\"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\",\"color\":5490477,\"fields\":[{\"name\":\"Branch\",\"value\":\"$GITHUB_REF\",\"inline\":true}],\"author\":{\"name\":\"$GITHUB_REPOSITORY\",\"url\":\"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY\",\"icon_url\":\"$GITHUB_SERVER_URL/songoda.png\"},\"footer\":{\"text\":\"Initiated by $GITHUB_ACTOR\",\"icon_url\":\"$GITHUB_SERVER_URL/$GITHUB_ACTOR.png\"}}],\"username\":\"OctoAgent\",\"avatar_url\":\"https://github.githubassets.com/images/modules/logos_page/Octocat.png\"}" --header 'Content-Type: application/json' $DISCORD_WEBHOOK
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_BUILD_STATUS_WEBHOOK }}
|
||||
- name: 'Discord Webhook (Failure)'
|
||||
if: ${{ failure() && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development' || github.ref == 'refs/heads/full-recode') }}
|
||||
continue-on-error: true
|
||||
run: |
|
||||
curl -X POST --data "{\"content\":null,\"embeds\":[{\"title\":\"Build failed!\",\"description\":\"The build with the ID #$GITHUB_RUN_NUMBER has failed!\",\"url\":\"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\",\"color\":15611419,\"fields\":[{\"name\":\"Branch\",\"value\":\"$GITHUB_REF\",\"inline\":true}],\"author\":{\"name\":\"$GITHUB_REPOSITORY\",\"url\":\"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY\",\"icon_url\":\"$GITHUB_SERVER_URL/songoda.png\"},\"footer\":{\"text\":\"Initiated by $GITHUB_ACTOR\",\"icon_url\":\"$GITHUB_SERVER_URL/$GITHUB_ACTOR.png\"}}],\"username\":\"OctoAgent\",\"avatar_url\":\"https://github.githubassets.com/images/modules/logos_page/Octocat.png\"}" --header "Content-Type:application/json" $DISCORD_WEBHOOK
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_BUILD_STATUS_WEBHOOK }}
|
||||
|
||||
|
||||
sonarcloud:
|
||||
name: SonarCloud
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Setup Java
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
|
||||
# Checkout project files
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
|
||||
# Caches
|
||||
- name: 'Cache: Maven'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-maven-
|
||||
- name: 'Cache: SonarCloud'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
|
||||
# SonarCloud static analysis
|
||||
- name: SonarCloud
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
.idea/*
|
||||
target/*
|
||||
EpicAnchors.iml
|
||||
/target/
|
||||
|
||||
# JetBrains IDEs
|
||||
/.idea/
|
||||
*.iml
|
||||
|
4
LICENSE
4
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018 Brianna O’Keefe
|
||||
Copyright (c) 2021 Songoda
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software with minimal restriction, including the rights to use, copy, modify or merge while excluding the rights to publish, (re)distribute, sub-license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
@ -6,4 +6,4 @@ The same distribution rights and limitations above shall similarly apply to any
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
@ -1,5 +0,0 @@
|
||||
# EpicAnchors
|
||||
|
||||
EpicAnchors allows your players to keep chunks loaded for a limited amount of time allowing redstone machines, mob grinders, spawners, and hoppers to all remain activated while the player is either out of normal range or offline.
|
||||
|
||||
![N|Solid](https://i.imgur.com/jKtE7ZM.png)
|
53
README.md
Normal file
53
README.md
Normal file
@ -0,0 +1,53 @@
|
||||
<!--suppress HtmlDeprecatedAttribute -->
|
||||
<div align="center">
|
||||
<img alt="EpicAnchors" src="https://cdn2.songoda.com/products/epicanchors/xTjYNZqmo1pVKZ1Za8YiLojGB6uM4bm6Bb0M5Spu.gif">
|
||||
|
||||
# EpicAnchors
|
||||
|
||||
**Allow your players to keep chunks loaded for a limited amount of time for a cost.**
|
||||
**Integrates with EpicSpawners to keep your mobs spawning even when you’re offline.**
|
||||
|
||||
<!-- Shields -->
|
||||
[![Discord](https://img.shields.io/discord/293212540723396608?color=7289DA&label=Discord&logo=discord&logoColor=7289DA)](https://discord.gg/songoda)
|
||||
[![Patreon](https://img.shields.io/badge/-Support_on_Patreon-F96854.svg?logo=patreon&style=flat&logoColor=white)](https://www.patreon.com/join/songoda)
|
||||
<br>
|
||||
[![Latest version](https://img.shields.io/badge/dynamic/xml?style=flat&color=blue&logo=github&logoColor=white&label=Latest&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsongoda%2FEpicAnchors%2Fmaster%2Fpom.xml&query=%2F*%5Blocal-name()%3D'project'%5D%2F*%5Blocal-name()%3D'version'%5D)](https://songoda.com/marketplace/product/31)
|
||||
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=songoda_EpicAnchors&metric=alert_status)](https://sonarcloud.io/dashboard?id=songoda_EpicAnchors)
|
||||
[![GitHub last commit](https://img.shields.io/github/last-commit/songoda/EpicAnchors?label=Last+commit)](https://github.com/songoda/EpicAnchors/commits)
|
||||
<br>
|
||||
[![bStats Servers](https://img.shields.io/bstats/servers/4816?label=Servers)](https://bstats.org/plugin/bukkit/EpicAnchors/4816)
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Marketplace](#marketplace)
|
||||
* [Documentation](#documentation)
|
||||
* [Support](#support)
|
||||
* [Suggestions](#suggestions)
|
||||
|
||||
## Introduction
|
||||
|
||||
`// TODO: Write an introduction`
|
||||
|
||||
## Marketplace
|
||||
|
||||
You can visit [our marketplace](https://songoda.com/marketplace/product/31) to download EpicAnchors as well as take a
|
||||
look at many other fantastic plugins which are sure to catch your eye.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find all the information about EpicAnchors, including dependencies, commands, permissions and incompatible
|
||||
plugins on [our wiki](https://wiki.songoda.com/Epic_Anchors).
|
||||
|
||||
Feel free to also contribute to the wiki as a way to help others in the community with using the plugin.
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter any issues while using the plugin, feel free to create a ticket
|
||||
on [our support desk](https://support.songoda.com).
|
||||
|
||||
## Suggestions
|
||||
|
||||
For suggestions about features you think should be added to the plugin to increase its functionality, feel free to
|
||||
create a thread over on [our feedback site](https://feedback.songoda.com).
|
180
pom.xml
180
pom.xml
@ -1,119 +1,165 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.songoda</groupId>
|
||||
<artifactId>EpicAnchors</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<version>1.4.10c</version>
|
||||
<version>2.0.0-ALPHA</version>
|
||||
<!-- Run 'mvn versions:set -DgenerateBackupPoms=false -DnewVersion=X.Y.Z-DEV' to update version recursively -->
|
||||
|
||||
<name>EpicAnchors</name>
|
||||
<description>Allow your players to keep chunks loaded for a limited amount of time for a cost.</description>
|
||||
<url>https://songoda.com/marketplace/product/31</url>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<java.release>8</java.release>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<sonar.projectKey>songoda_EpicAnchors</sonar.projectKey>
|
||||
<sonar.organization>songoda</sonar.organization>
|
||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||
<sonar.moduleKey>${project.groupId}:${project.artifactId}</sonar.moduleKey>
|
||||
</properties>
|
||||
|
||||
<issueManagement>
|
||||
<url>https://support.songoda.com/servicedesk/customer/portal/3</url>
|
||||
<system>Jira Service Desk</system>
|
||||
</issueManagement>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/songoda/EpicAnchors</url>
|
||||
<connection>scm:git:git:github.com/songoda/EpicAnchors.git</connection>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<finalName>EpicAnchors-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<version>3.8.1</version>
|
||||
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||
<artifactId>replacer</artifactId>
|
||||
<version>1.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>replace</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<file>${project.build.directory}/classes/plugin.yml</file>
|
||||
<replacements>
|
||||
<replacement>
|
||||
<token>maven-version-number</token>
|
||||
<value>${project.version}</value>
|
||||
</replacement>
|
||||
</replacements>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
|
||||
<release>${java.release}</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shaded</id>
|
||||
<phase>package</phase>
|
||||
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
|
||||
<configuration>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<finalName>${project.name}-${project.version}</finalName>
|
||||
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>com.songoda:SongodaCore</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<filters>
|
||||
<!-- This filter is primarily for anvil gui right now -->
|
||||
<!--<filter>
|
||||
<artifact>com.songoda:SongodaCore-NMS*</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>-->
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.songoda.core</pattern>
|
||||
<shadedPattern>${project.groupId}.epicanchors.core</shadedPattern>
|
||||
<shadedPattern>com.songoda.epicanchors.core</shadedPattern>
|
||||
</relocation>
|
||||
|
||||
<relocation>
|
||||
<pattern>epicanchors.nms</pattern>
|
||||
<shadedPattern>com.songoda.epicanchors.nms</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
|
||||
<excludes>
|
||||
<exclude>META-INF/**</exclude>
|
||||
<exclude>LICENSE</exclude>
|
||||
<exclude>LICENSE.**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
|
||||
<filter>
|
||||
<artifact>org.jetbrains:annotations</artifact>
|
||||
<excludes>
|
||||
<exclude>**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
|
||||
<filter>
|
||||
<artifact>com.songoda:epicanchors-v*</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>apache.snapshots</id>
|
||||
<url>https://repository.apache.org/snapshots/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>public</id>
|
||||
<url>https://repo.songoda.com/repository/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<id>spigotmc-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>songoda-public</id>
|
||||
<url>https://repo.songoda.com/repository/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.16.4</version>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.8-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.songoda</groupId>
|
||||
<artifactId>SongodaCore</artifactId>
|
||||
<version>LATEST</version>
|
||||
<version>2.5.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Dev dependencies -->
|
||||
<dependency>
|
||||
<groupId>com.songoda</groupId>
|
||||
<artifactId>epicspawners</artifactId>
|
||||
<version>6-pre4</version>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>21.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
128
src/main/java/com/songoda/epicanchors/Anchor.java
Normal file
128
src/main/java/com/songoda/epicanchors/Anchor.java
Normal file
@ -0,0 +1,128 @@
|
||||
package com.songoda.epicanchors;
|
||||
|
||||
import com.songoda.epicanchors.utils.WorldUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Anchor {
|
||||
private final int dbId;
|
||||
|
||||
private final UUID owner;
|
||||
|
||||
private final Location location;
|
||||
private int ticksLeft;
|
||||
|
||||
public Anchor(int dbId, @Nullable UUID owner, @NotNull Location location, int ticksLeft) {
|
||||
if (dbId <= 0) throw new IllegalArgumentException("Invalid value for dbId");
|
||||
if (ticksLeft <= 0 && ticksLeft != -1) throw new IllegalArgumentException("Invalid value for ticksLeft");
|
||||
|
||||
Objects.requireNonNull(location.getWorld()); // Sanity check
|
||||
|
||||
this.dbId = dbId;
|
||||
|
||||
this.owner = owner;
|
||||
|
||||
this.location = location;
|
||||
this.ticksLeft = ticksLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* <b></b>This method is automatically synchronized with the server's main thread using
|
||||
* {@link org.bukkit.scheduler.BukkitScheduler#runTask(Plugin, Runnable)}</b>
|
||||
*
|
||||
* @see Bukkit#isPrimaryThread()
|
||||
* @see org.bukkit.scheduler.BukkitScheduler#runTask(Plugin, Runnable)
|
||||
*/
|
||||
protected void init(Plugin plugin) {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
WorldUtils.loadAnchoredChunk(getChunk(), plugin);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> init(plugin));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <b></b>This method is automatically synchronized with the server's main thread using
|
||||
* {@link org.bukkit.scheduler.BukkitScheduler#runTask(Plugin, Runnable)}</b>
|
||||
*
|
||||
* @see Bukkit#isPrimaryThread()
|
||||
* @see org.bukkit.scheduler.BukkitScheduler#runTask(Plugin, Runnable)
|
||||
*/
|
||||
protected void deInit(Plugin plugin) {
|
||||
// TODO: Document that holograms are not removed or add boolean flag to remove them
|
||||
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
WorldUtils.unloadAnchoredChunk(getChunk(), plugin);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> deInit(plugin));
|
||||
}
|
||||
}
|
||||
|
||||
public int getDbId() {
|
||||
return this.dbId;
|
||||
}
|
||||
|
||||
public UUID getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public boolean isLegacy() {
|
||||
return this.owner == null;
|
||||
}
|
||||
|
||||
public @NotNull Location getLocation() {
|
||||
return this.location.clone();
|
||||
}
|
||||
|
||||
public @NotNull World getWorld() {
|
||||
return this.location.getWorld();
|
||||
}
|
||||
|
||||
public @NotNull Chunk getChunk() {
|
||||
return this.location.getChunk();
|
||||
}
|
||||
|
||||
public int getTicksLeft() {
|
||||
return this.ticksLeft;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setTicksLeft(int ticksLeft) {
|
||||
if (ticksLeft < 0) throw new IllegalArgumentException("Invalid value for ticksLeft");
|
||||
|
||||
this.ticksLeft = ticksLeft;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public int addTicksLeft(int ticks) {
|
||||
if (!isInfinite()) {
|
||||
this.ticksLeft += ticks;
|
||||
}
|
||||
|
||||
return this.ticksLeft;
|
||||
}
|
||||
|
||||
public int removeTicksLeft(int ticks) {
|
||||
if (!isInfinite()) {
|
||||
this.ticksLeft -= ticks;
|
||||
|
||||
if (this.ticksLeft < 0) {
|
||||
this.ticksLeft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return this.ticksLeft;
|
||||
}
|
||||
|
||||
public boolean isInfinite() {
|
||||
return this.ticksLeft == -1;
|
||||
}
|
||||
}
|
471
src/main/java/com/songoda/epicanchors/AnchorManager.java
Normal file
471
src/main/java/com/songoda/epicanchors/AnchorManager.java
Normal file
@ -0,0 +1,471 @@
|
||||
package com.songoda.epicanchors;
|
||||
|
||||
import com.songoda.core.SongodaPlugin;
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.compatibility.CompatibleParticleHandler;
|
||||
import com.songoda.core.compatibility.CompatibleSound;
|
||||
import com.songoda.core.hooks.HologramManager;
|
||||
import com.songoda.core.nms.NmsManager;
|
||||
import com.songoda.core.nms.nbt.NBTItem;
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import com.songoda.core.utils.TimeUtils;
|
||||
import com.songoda.epicanchors.api.AnchorAccessCheck;
|
||||
import com.songoda.epicanchors.files.DataManager;
|
||||
import com.songoda.epicanchors.files.Settings;
|
||||
import com.songoda.epicanchors.utils.Callback;
|
||||
import com.songoda.epicanchors.utils.UpdateCallback;
|
||||
import com.songoda.epicanchors.utils.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class AnchorManager {
|
||||
private static final String ERR_WORLD_NOT_READY = "EpicAnchors has not finished initializing that world yet";
|
||||
private static final String NBT_TICKS_KEY = "EpicAnchors_Ticks".toLowerCase();
|
||||
|
||||
private final SongodaPlugin plugin;
|
||||
private final DataManager dataManager;
|
||||
|
||||
private final Map<World, Set<Anchor>> anchors = new HashMap<>(3);
|
||||
private final Set<Player> visualizedChunk = new HashSet<>();
|
||||
private final List<AnchorAccessCheck> accessChecks = new LinkedList<>();
|
||||
|
||||
private boolean ready;
|
||||
|
||||
public AnchorManager(SongodaPlugin plugin, DataManager dataManager) {
|
||||
this.plugin = Objects.requireNonNull(plugin);
|
||||
this.dataManager = Objects.requireNonNull(dataManager);
|
||||
}
|
||||
|
||||
protected void saveAll() {
|
||||
for (Set<Anchor> anchorSet : anchors.values()) {
|
||||
this.dataManager.updateAnchors(anchorSet, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void deInitAll() {
|
||||
for (World world : anchors.keySet().toArray(new World[0])) {
|
||||
deInitAnchors(world);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initAnchorsAsync(@NotNull World world, @Nullable UpdateCallback callback) {
|
||||
if (this.anchors.containsKey(world)) {
|
||||
if (callback != null) {
|
||||
callback.accept(null);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
long start = System.nanoTime();
|
||||
|
||||
this.dataManager.getAnchors(world, (ex, result) -> {
|
||||
if (ex == null) {
|
||||
this.anchors.computeIfAbsent(world, k -> new HashSet<>());
|
||||
|
||||
for (Anchor anchor : result) {
|
||||
anchor.init(this.plugin);
|
||||
|
||||
this.anchors.computeIfAbsent(anchor.getWorld(), k -> new HashSet<>())
|
||||
.add(anchor);
|
||||
}
|
||||
|
||||
long end = System.nanoTime();
|
||||
this.plugin.getLogger().info("Initialized anchors in world '" + world.getName() + "' " +
|
||||
"(" + TimeUnit.NANOSECONDS.toMillis(end - start) + "ms)");
|
||||
|
||||
if (callback != null) {
|
||||
callback.accept(null);
|
||||
}
|
||||
} else {
|
||||
if (callback != null) {
|
||||
callback.accept(ex);
|
||||
} else {
|
||||
Utils.logException(this.plugin, ex, "SQLite");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void deInitAnchors(@NotNull World world) {
|
||||
Set<Anchor> tmpAnchors = this.anchors.remove(world);
|
||||
|
||||
if (tmpAnchors != null) {
|
||||
this.dataManager.updateAnchors(tmpAnchors, null);
|
||||
|
||||
for (Anchor anchor : tmpAnchors) {
|
||||
anchor.deInit(this.plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setReady() {
|
||||
this.ready = true;
|
||||
|
||||
Bukkit.getScheduler().runTaskTimer(plugin, this::saveAll, 20L * 60 * 5, 20L * 60 * 5);
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isReady(World world) {
|
||||
return this.ready && anchors.containsKey(world);
|
||||
}
|
||||
|
||||
/* Getter */
|
||||
|
||||
public Anchor[] getAnchors(@NotNull World world) {
|
||||
Set<Anchor> set = anchors.get(world);
|
||||
|
||||
if (set != null) {
|
||||
return set.toArray(new Anchor[0]);
|
||||
}
|
||||
|
||||
return new Anchor[0];
|
||||
}
|
||||
|
||||
public @Nullable Anchor getAnchor(@NotNull Block b) {
|
||||
if (!isReady(b.getWorld())) {
|
||||
throw new IllegalStateException(ERR_WORLD_NOT_READY);
|
||||
}
|
||||
|
||||
Location bLoc = b.getLocation();
|
||||
Set<Anchor> set = anchors.get(b.getWorld());
|
||||
|
||||
if (set != null) {
|
||||
for (Anchor anchor : set) {
|
||||
if (anchor.getLocation().equals(bLoc)) {
|
||||
return anchor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isAnchor(@NotNull Block b) {
|
||||
return getAnchor(b) != null;
|
||||
}
|
||||
|
||||
public boolean hasAnchor(@NotNull Chunk chunk) {
|
||||
if (!isReady(chunk.getWorld())) {
|
||||
throw new IllegalStateException(ERR_WORLD_NOT_READY);
|
||||
}
|
||||
|
||||
Set<Anchor> set = anchors.get(chunk.getWorld());
|
||||
|
||||
if (set != null) {
|
||||
for (Anchor anchor : set) {
|
||||
if (anchor.getChunk().equals(chunk)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public List<Anchor> searchAnchors(Location center, double searchRadius) {
|
||||
return searchAnchors(center, searchRadius, false);
|
||||
}
|
||||
|
||||
public List<Anchor> searchAnchors(Location center, double searchRadius, boolean ignoreHeight) {
|
||||
List<Anchor> result = new ArrayList<>();
|
||||
|
||||
if (ignoreHeight) {
|
||||
center = center.clone();
|
||||
center.setY(0);
|
||||
}
|
||||
|
||||
for (Anchor anchor : getAnchors(center.getWorld())) {
|
||||
Location loc = anchor.getLocation();
|
||||
|
||||
if (ignoreHeight) {
|
||||
loc.setY(0);
|
||||
}
|
||||
|
||||
if (center.distance(loc) <= searchRadius) {
|
||||
result.add(anchor);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Create 'n Destroy */
|
||||
|
||||
/**
|
||||
* Creates a new anchor at a given location
|
||||
*
|
||||
* @param loc The block location for the anchor
|
||||
* @param ticks The amount of ticks the anchor lives or -1 for infinite
|
||||
*/
|
||||
public void createAnchor(@NotNull Location loc, @NotNull UUID owner, int ticks, @Nullable Callback<Anchor> callback) {
|
||||
if (!isReady(loc.getWorld())) {
|
||||
throw new IllegalStateException(ERR_WORLD_NOT_READY);
|
||||
}
|
||||
|
||||
this.dataManager.insertAnchorAsync(loc, Objects.requireNonNull(owner), ticks, (ex, anchor) -> {
|
||||
if (ex != null) {
|
||||
if (callback != null) {
|
||||
callback.accept(ex, null);
|
||||
} else {
|
||||
Utils.logException(this.plugin, ex, "SQLite");
|
||||
}
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
Block b = loc.getBlock();
|
||||
b.setType(Settings.MATERIAL.getMaterial().getMaterial());
|
||||
|
||||
anchors.computeIfAbsent(anchor.getWorld(), k -> new HashSet<>())
|
||||
.add(anchor);
|
||||
|
||||
updateHologram(anchor);
|
||||
|
||||
if (callback != null) {
|
||||
callback.accept(null, anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void destroyAnchor(@NotNull Anchor anchor) {
|
||||
destroyAnchor(anchor, false);
|
||||
}
|
||||
|
||||
public void destroyAnchor(@NotNull Anchor anchor, boolean forceSkipItemDrop) {
|
||||
if (!isReady(anchor.getWorld())) {
|
||||
throw new IllegalStateException(ERR_WORLD_NOT_READY);
|
||||
}
|
||||
|
||||
for (Set<Anchor> value : anchors.values()) {
|
||||
value.remove(anchor);
|
||||
}
|
||||
|
||||
removeHologram(anchor);
|
||||
|
||||
Location anchorLoc = anchor.getLocation();
|
||||
Block anchorBlock = anchorLoc.getBlock();
|
||||
Material anchorMaterial = anchorBlock.getType();
|
||||
|
||||
if (anchorBlock.getType() == Settings.MATERIAL.getMaterial().getMaterial()) {
|
||||
anchorBlock.setType(Material.AIR);
|
||||
}
|
||||
|
||||
// Drop anchor as an item
|
||||
if (!forceSkipItemDrop &&
|
||||
Settings.ALLOW_ANCHOR_BREAKING.getBoolean() &&
|
||||
(anchor.isInfinite() || anchor.getTicksLeft() >= 20)) {
|
||||
anchor.getWorld().dropItemNaturally(anchorLoc, createAnchorItem(anchor.getTicksLeft(), anchorMaterial));
|
||||
}
|
||||
|
||||
// Particles & Sound
|
||||
anchor.getWorld().playSound(anchorLoc, CompatibleSound.ENTITY_GENERIC_EXPLODE.getSound(), 10, 10);
|
||||
CompatibleParticleHandler.spawnParticles(CompatibleParticleHandler.ParticleType.getParticle(Settings.PARTICLE_DESTROY.getString()),
|
||||
anchor.getLocation().add(.5, .5, .5), 100, .5, .5, .5);
|
||||
|
||||
anchor.deInit(this.plugin);
|
||||
this.dataManager.deleteAnchorAsync(anchor);
|
||||
}
|
||||
|
||||
/* Anchor access */
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void registerAccessCheck(AnchorAccessCheck accessCheck) {
|
||||
if (!accessChecks.contains(accessCheck)) {
|
||||
// Adding at the start of the list makes sure the default check is
|
||||
accessChecks.add(accessCheck);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accessCheck The {@link AnchorAccessCheck} to remove
|
||||
*
|
||||
* @return true if the {@link AnchorAccessCheck} has been found and removed, false otherwise
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public boolean unregisterAccessCheck(AnchorAccessCheck accessCheck) {
|
||||
return accessChecks.remove(accessCheck);
|
||||
}
|
||||
|
||||
public boolean hasAccess(@NotNull Anchor anchor, @NotNull OfflinePlayer p) {
|
||||
return hasAccess(anchor, p.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player has access to an Anchor. By default only the owner has access to an Anchor.
|
||||
* <br>
|
||||
* Other plugins can grant access to other players (e.g. friends).
|
||||
* <br>
|
||||
* Legacy anchors without an owner automatically grant access to all players.
|
||||
*
|
||||
* @return true if the player may access the Anchor, false otherwise
|
||||
*
|
||||
* @see #registerAccessCheck(AnchorAccessCheck)
|
||||
*/
|
||||
public boolean hasAccess(@NotNull Anchor anchor, @NotNull UUID uuid) {
|
||||
if (anchor.isLegacy() || anchor.getOwner().equals(uuid)) return true;
|
||||
|
||||
for (AnchorAccessCheck accessCheck : this.accessChecks) {
|
||||
if (accessCheck.check(anchor, uuid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Anchor item */
|
||||
|
||||
public ItemStack createAnchorItem(int ticks) {
|
||||
return createAnchorItem(ticks, Settings.MATERIAL.getMaterial());
|
||||
}
|
||||
|
||||
public ItemStack createAnchorItem(int ticks, Material material) {
|
||||
return createAnchorItem(ticks, CompatibleMaterial.getMaterial(material));
|
||||
}
|
||||
|
||||
public ItemStack createAnchorItem(int ticks, CompatibleMaterial material) {
|
||||
if (ticks <= 0 && ticks != -1) throw new IllegalArgumentException();
|
||||
|
||||
ItemStack item = material.getItem();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
|
||||
assert meta != null;
|
||||
meta.setDisplayName(formatAnchorText(ticks, false));
|
||||
meta.setLore(TextUtils.formatText(Settings.LORE.getString().split("\r?\n")));
|
||||
item.setItemMeta(meta);
|
||||
|
||||
NBTItem nbtItem = NmsManager.getNbt().of(item);
|
||||
nbtItem.set(NBT_TICKS_KEY, ticks);
|
||||
|
||||
return nbtItem.finish();
|
||||
}
|
||||
|
||||
public static int getTicksFromItem(ItemStack item) {
|
||||
NBTItem nbtItem = NmsManager.getNbt().of(item);
|
||||
|
||||
if (nbtItem.has(NBT_TICKS_KEY)) {
|
||||
return nbtItem.getInt(NBT_TICKS_KEY);
|
||||
}
|
||||
|
||||
// Legacy code (pre v2) to stay cross-version compatible
|
||||
if (Settings.MATERIAL.getMaterial().getMaterial() == item.getType()) {
|
||||
|
||||
if (nbtItem.has("ticks")) {
|
||||
int result = nbtItem.getInt("ticks");
|
||||
|
||||
return result == -99 ? -1 : result;
|
||||
}
|
||||
|
||||
// Tries to get the ticks remaining from hidden text
|
||||
if (item.hasItemMeta() &&
|
||||
item.getItemMeta().hasDisplayName() &&
|
||||
item.getItemMeta().getDisplayName().contains(":")) {
|
||||
try {
|
||||
int result = Integer.parseInt(item.getItemMeta().getDisplayName().replace("§", "").split(":")[0]);
|
||||
|
||||
return result == -99 ? -1 : result;
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Chunk visualization */
|
||||
|
||||
public boolean toggleChunkVisualized(Player p) {
|
||||
boolean visualize = !hasChunksVisualized(p);
|
||||
|
||||
setChunksVisualized(p, visualize);
|
||||
|
||||
return visualize;
|
||||
}
|
||||
|
||||
public void setChunksVisualized(Player p, boolean visualize) {
|
||||
if (visualize) {
|
||||
this.visualizedChunk.add(p);
|
||||
} else {
|
||||
this.visualizedChunk.remove(p);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean hasChunksVisualized(Player p) {
|
||||
return this.visualizedChunk.contains(p);
|
||||
}
|
||||
|
||||
/* Holograms */
|
||||
|
||||
public void updateHolograms(List<Anchor> anchors) {
|
||||
// are holograms enabled?
|
||||
if (!Settings.HOLOGRAMS.getBoolean() || !HologramManager.getManager().isEnabled()) return;
|
||||
|
||||
Map<Location, List<String>> hologramData = new HashMap<>(anchors.size());
|
||||
|
||||
for (Anchor anchor : anchors) {
|
||||
hologramData.put(anchor.getLocation(),
|
||||
Collections.singletonList(formatAnchorText(anchor.getTicksLeft(), true)));
|
||||
}
|
||||
|
||||
// Create the holograms
|
||||
HologramManager.bulkUpdateHolograms(hologramData);
|
||||
}
|
||||
|
||||
private void updateHologram(Anchor anchor) {
|
||||
updateHolograms(Collections.singletonList(anchor));
|
||||
}
|
||||
|
||||
private String formatAnchorText(int ticks, boolean shorten) {
|
||||
String remaining;
|
||||
|
||||
if (ticks < 0) {
|
||||
remaining = this.plugin.getLocale().getMessage("Infinite").getMessage();
|
||||
} else {
|
||||
long millis = (ticks / 20L) * 1000L;
|
||||
|
||||
remaining = TimeUtils.makeReadable(millis);
|
||||
|
||||
if (shorten && millis > 60 * 5 * 1000 /* 5 minutes */ &&
|
||||
remaining.charAt(remaining.length() - 1) == 's') {
|
||||
int i = remaining.lastIndexOf(' ');
|
||||
|
||||
remaining = remaining.substring(0, i);
|
||||
}
|
||||
|
||||
if (remaining.isEmpty()) {
|
||||
remaining = "0s";
|
||||
}
|
||||
}
|
||||
|
||||
return TextUtils.formatText(Settings.NAME_TAG.getString().replace("{REMAINING}", remaining));
|
||||
}
|
||||
|
||||
private static void removeHologram(Anchor anchor) {
|
||||
HologramManager.removeHologram(anchor.getLocation());
|
||||
}
|
||||
}
|
@ -4,214 +4,150 @@ import com.songoda.core.SongodaCore;
|
||||
import com.songoda.core.SongodaPlugin;
|
||||
import com.songoda.core.commands.CommandManager;
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import com.songoda.core.configuration.Config;
|
||||
import com.songoda.core.database.DatabaseConnector;
|
||||
import com.songoda.core.database.SQLiteConnector;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import com.songoda.core.hooks.EconomyManager;
|
||||
import com.songoda.core.hooks.HologramManager;
|
||||
import com.songoda.core.nms.NmsManager;
|
||||
import com.songoda.core.nms.nbt.NBTItem;
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import com.songoda.epicanchors.anchor.Anchor;
|
||||
import com.songoda.epicanchors.anchor.AnchorManager;
|
||||
import com.songoda.epicanchors.commands.*;
|
||||
import com.songoda.epicanchors.listeners.BlockListeners;
|
||||
import com.songoda.epicanchors.listeners.InteractListeners;
|
||||
import com.songoda.epicanchors.listeners.PortalListeners;
|
||||
import com.songoda.epicanchors.settings.Settings;
|
||||
import com.songoda.epicanchors.commands.EpicAnchorsCommand;
|
||||
import com.songoda.epicanchors.commands.sub.GiveCommand;
|
||||
import com.songoda.epicanchors.commands.sub.ReloadCommand;
|
||||
import com.songoda.epicanchors.commands.sub.SettingsCommand;
|
||||
import com.songoda.epicanchors.commands.sub.ShowCommand;
|
||||
import com.songoda.epicanchors.files.DataManager;
|
||||
import com.songoda.epicanchors.files.Settings;
|
||||
import com.songoda.epicanchors.files.migration.AnchorMigration;
|
||||
import com.songoda.epicanchors.files.migration._1_InitialMigration;
|
||||
import com.songoda.epicanchors.listener.AnchorListener;
|
||||
import com.songoda.epicanchors.listener.BlockListener;
|
||||
import com.songoda.epicanchors.listener.WorldListener;
|
||||
import com.songoda.epicanchors.tasks.AnchorTask;
|
||||
import com.songoda.epicanchors.tasks.VisualizeTask;
|
||||
import com.songoda.epicanchors.utils.Methods;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import com.songoda.epicanchors.utils.ThreadSync;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class EpicAnchors extends SongodaPlugin {
|
||||
|
||||
private static EpicAnchors INSTANCE;
|
||||
|
||||
private final Config dataFile = new Config(this, "data.yml");
|
||||
|
||||
private final GuiManager guiManager = new GuiManager(this);
|
||||
public final class EpicAnchors extends SongodaPlugin {
|
||||
private GuiManager guiManager;
|
||||
private AnchorManager anchorManager;
|
||||
private CommandManager commandManager;
|
||||
|
||||
public static EpicAnchors getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
private DataManager dataManager;
|
||||
|
||||
@Override
|
||||
public void onPluginLoad() {
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginEnable() {
|
||||
// Songoda Updater
|
||||
SongodaCore.registerPlugin(this, 31, CompatibleMaterial.END_PORTAL_FRAME);
|
||||
|
||||
// Initialize database
|
||||
this.getLogger().info("Initializing SQLite...");
|
||||
DatabaseConnector dbCon = new SQLiteConnector(this);
|
||||
this.dataManager = new DataManager(dbCon, this);
|
||||
AnchorMigration anchorMigration = new AnchorMigration(dbCon, this.dataManager,
|
||||
new _1_InitialMigration());
|
||||
anchorMigration.runMigrations();
|
||||
|
||||
anchorMigration.migrateLegacyData(this);
|
||||
this.anchorManager = new AnchorManager(this, this.dataManager);
|
||||
|
||||
// Economy [1/2]
|
||||
EconomyManager.load();
|
||||
|
||||
// Config
|
||||
Settings.setupConfig();
|
||||
this.setLocale(Settings.LANGUAGE.getString(), false);
|
||||
|
||||
// Economy [2/2]
|
||||
EconomyManager.getManager().setPreferredHook(Settings.ECONOMY_PLUGIN.getString());
|
||||
|
||||
// Holograms
|
||||
HologramManager.load(this);
|
||||
|
||||
// Event Listener
|
||||
this.guiManager = new GuiManager(this);
|
||||
guiManager.init();
|
||||
PluginManager pluginManager = Bukkit.getPluginManager();
|
||||
pluginManager.registerEvents(new WorldListener(
|
||||
world -> this.anchorManager.initAnchorsAsync(world, null),
|
||||
world -> this.anchorManager.deInitAnchors(world)),
|
||||
this);
|
||||
pluginManager.registerEvents(new AnchorListener(this), this);
|
||||
pluginManager.registerEvents(new BlockListener(this.anchorManager), this);
|
||||
|
||||
// Commands
|
||||
CommandManager commandManager = new CommandManager(this);
|
||||
commandManager.addCommand(new EpicAnchorsCommand(this, commandManager))
|
||||
.addSubCommands(
|
||||
new GiveCommand(this),
|
||||
new ReloadCommand(this),
|
||||
new SettingsCommand(this, this.guiManager),
|
||||
new ShowCommand(this)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginDisable() {
|
||||
saveToFile();
|
||||
// Save all Anchors
|
||||
if (this.dataManager != null) {
|
||||
this.anchorManager.deInitAll();
|
||||
|
||||
this.dataManager.close();
|
||||
}
|
||||
|
||||
// Remove all holograms
|
||||
HologramManager.removeAllHolograms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataLoad() {}
|
||||
public void onDataLoad() {
|
||||
new Thread(() -> {
|
||||
ThreadSync tSync = new ThreadSync();
|
||||
|
||||
@Override
|
||||
public void onPluginEnable() {
|
||||
// Run Songoda Updater
|
||||
SongodaCore.registerPlugin(this, 31, CompatibleMaterial.END_PORTAL_FRAME);
|
||||
for (World w : Bukkit.getWorlds()) {
|
||||
this.anchorManager.initAnchorsAsync(w, (ex) -> {
|
||||
if (ex != null) {
|
||||
this.getLogger().log(Level.FINER, ex, () -> "Failed to initialize world '" + w.getName() + "'");
|
||||
}
|
||||
|
||||
// Load Economy
|
||||
EconomyManager.load();
|
||||
tSync.release();
|
||||
});
|
||||
|
||||
// Setup Config
|
||||
Settings.setupConfig();
|
||||
this.setLocale(Settings.LANGUGE_MODE.getString(), false);
|
||||
tSync.waitForRelease();
|
||||
tSync.reset();
|
||||
}
|
||||
|
||||
// Set economy preference
|
||||
EconomyManager.getManager().setPreferredHook(Settings.ECONOMY_PLUGIN.getString());
|
||||
this.anchorManager.setReady();
|
||||
|
||||
// Register commands
|
||||
this.commandManager = new CommandManager(this);
|
||||
this.commandManager.addCommand(new CommandEpicAnchors(this))
|
||||
.addSubCommands(
|
||||
new CommandGive(this),
|
||||
new CommandReload(this),
|
||||
new CommandSettings(this, guiManager),
|
||||
new CommandShow(this)
|
||||
);
|
||||
|
||||
anchorManager = new AnchorManager();
|
||||
Bukkit.getScheduler().runTaskLater(this, this::loadAnchorsFromFile, 5L);
|
||||
|
||||
// Start tasks
|
||||
new AnchorTask(this);
|
||||
new VisualizeTask(this);
|
||||
|
||||
// Register Listeners
|
||||
guiManager.init();
|
||||
PluginManager pluginManager = Bukkit.getPluginManager();
|
||||
pluginManager.registerEvents(new BlockListeners(this), this);
|
||||
pluginManager.registerEvents(new InteractListeners(this), this);
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9))
|
||||
pluginManager.registerEvents(new PortalListeners(this), this);
|
||||
|
||||
// Register Hologram Plugin
|
||||
HologramManager.load(this);
|
||||
|
||||
if (Settings.HOLOGRAMS.getBoolean())
|
||||
loadHolograms();
|
||||
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(this, this::saveToFile, 6000, 6000);
|
||||
// Start tasks
|
||||
new AnchorTask(this).startTask();
|
||||
new VisualizeTask(this).startTask();
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigReload() {
|
||||
this.setLocale(Settings.LANGUGE_MODE.getString(), true);
|
||||
this.loadAnchorsFromFile();
|
||||
this.setLocale(Settings.LANGUAGE.getString(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Config> getExtraConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void loadHolograms() {
|
||||
Collection<Anchor> anchors = getAnchorManager().getAnchors().values();
|
||||
if (anchors.size() == 0) return;
|
||||
|
||||
for (Anchor anchor : anchors) {
|
||||
if (anchor.getWorld() == null) continue;
|
||||
updateHologram(anchor);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearHologram(Anchor anchor) {
|
||||
HologramManager.removeHologram(correctHeight(anchor.getLocation()));
|
||||
}
|
||||
|
||||
public void updateHologram(Anchor anchor) {
|
||||
// are holograms enabled?
|
||||
if (!Settings.HOLOGRAMS.getBoolean() || !HologramManager.getManager().isEnabled()) return;
|
||||
// verify that this is a anchor
|
||||
if (anchor.getLocation().getBlock().getType() != Settings.MATERIAL.getMaterial().getMaterial()) return;
|
||||
// grab the name
|
||||
String name = Methods.formatName(anchor.getTicksLeft()).trim();
|
||||
Location location = correctHeight(anchor.getLocation());
|
||||
// create the hologram
|
||||
HologramManager.updateHologram(location, name);
|
||||
}
|
||||
|
||||
private Location correctHeight(Location location) {
|
||||
if (location.getBlock().getType() != CompatibleMaterial.END_PORTAL_FRAME.getMaterial())
|
||||
location.add(0, .05, 0);
|
||||
return location;
|
||||
}
|
||||
|
||||
private void loadAnchorsFromFile() {
|
||||
dataFile.load();
|
||||
if (!dataFile.contains("Anchors")) return;
|
||||
for (String locationStr : dataFile.getConfigurationSection("Anchors").getKeys(false)) {
|
||||
Location location = Methods.unserializeLocation(locationStr);
|
||||
int ticksLeft = dataFile.getInt("Anchors." + locationStr + ".ticksLeft");
|
||||
anchorManager.addAnchor(location, new Anchor(location, ticksLeft));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveToFile() {
|
||||
dataFile.clearConfig(true);
|
||||
for (Anchor anchor : anchorManager.getAnchors().values()) {
|
||||
String locationStr = Methods.serializeLocation(anchor.getLocation());
|
||||
dataFile.set("Anchors." + locationStr + ".ticksLeft", anchor.getTicksLeft());
|
||||
}
|
||||
dataFile.save();
|
||||
}
|
||||
|
||||
public int getTicksFromItem(ItemStack item) {
|
||||
NBTItem nbtItem = NmsManager.getNbt().of(item);
|
||||
if (nbtItem.has("ticks")) {
|
||||
return nbtItem.getNBTObject("ticks").asInt();
|
||||
}
|
||||
|
||||
// Legacy code. Tries to get the ticks remaining from hidden text.
|
||||
if (!item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return 0;
|
||||
if (item.getItemMeta().getDisplayName().contains(":")) {
|
||||
return Integer.parseInt(item.getItemMeta().getDisplayName().replace("\u00A7", "").split(":")[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public ItemStack makeAnchorItem(int ticks) {
|
||||
ItemStack item = Settings.MATERIAL.getMaterial().getItem();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.setDisplayName(Methods.formatName(ticks));
|
||||
ArrayList<String> lore = new ArrayList<>();
|
||||
String[] parts = Settings.LORE.getString().split("\\|");
|
||||
for (String line : parts) {
|
||||
lore.add(TextUtils.formatText(line));
|
||||
}
|
||||
meta.setLore(lore);
|
||||
item.setItemMeta(meta);
|
||||
|
||||
NBTItem nbtItem = NmsManager.getNbt().of(item);
|
||||
nbtItem.set("ticks", ticks);
|
||||
return nbtItem.finish();
|
||||
}
|
||||
|
||||
public CommandManager getCommandManager() {
|
||||
return commandManager;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public GuiManager getGuiManager() {
|
||||
return guiManager;
|
||||
return this.guiManager;
|
||||
}
|
||||
|
||||
public AnchorManager getAnchorManager() {
|
||||
return anchorManager;
|
||||
return this.anchorManager;
|
||||
}
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
package com.songoda.epicanchors.anchor;
|
||||
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import com.songoda.core.hooks.EconomyManager;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.settings.Settings;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class Anchor {
|
||||
|
||||
private Location location;
|
||||
private int ticksLeft;
|
||||
private boolean isInfinite;
|
||||
|
||||
private final int chunkX;
|
||||
private final int chunkZ;
|
||||
|
||||
public Anchor(Location location, int ticksLeft) {
|
||||
this.location = location;
|
||||
this.chunkX = location.getBlockX() >> 4;
|
||||
this.chunkZ = location.getBlockZ() >> 4;
|
||||
this.ticksLeft = ticksLeft;
|
||||
this.isInfinite = (ticksLeft == -99);
|
||||
}
|
||||
|
||||
public void addTime(String type, Player player) {
|
||||
EpicAnchors instance = EpicAnchors.getInstance();
|
||||
|
||||
if (type.equals("ECO")) {
|
||||
if (!EconomyManager.isEnabled()) return;
|
||||
double cost = instance.getConfig().getDouble("Main.Economy Cost");
|
||||
if (EconomyManager.hasBalance(player, cost)) {
|
||||
EconomyManager.withdrawBalance(player, cost);
|
||||
} else {
|
||||
instance.getLocale().getMessage("event.upgrade.cannotafford").sendPrefixedMessage(player);
|
||||
return;
|
||||
}
|
||||
} else if (type.equals("XP")) {
|
||||
int cost = instance.getConfig().getInt("Main.XP Cost");
|
||||
if (player.getLevel() >= cost || player.getGameMode() == GameMode.CREATIVE) {
|
||||
if (player.getGameMode() != GameMode.CREATIVE) {
|
||||
player.setLevel(player.getLevel() - cost);
|
||||
}
|
||||
} else {
|
||||
instance.getLocale().getMessage("event.upgrade.cannotafford").sendPrefixedMessage(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ticksLeft = ticksLeft + 20 * 60 * 30;
|
||||
Sound sound = ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9) ? Sound.ENTITY_PLAYER_LEVELUP : Sound.valueOf("LEVEL_UP");
|
||||
player.playSound(player.getLocation(), sound, 0.6F, 15.0F);
|
||||
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9))
|
||||
player.getWorld().spawnParticle(Particle.SPELL_WITCH, getLocation().add(.5, .5, .5), 100, .5, .5, .5);
|
||||
}
|
||||
|
||||
public void bust() {
|
||||
EpicAnchors plugin = EpicAnchors.getInstance();
|
||||
|
||||
if (Settings.ALLOW_ANCHOR_BREAKING.getBoolean() && getTicksLeft() > 0) {
|
||||
ItemStack item = plugin.makeAnchorItem(getTicksLeft());
|
||||
getLocation().getWorld().dropItemNaturally(getLocation(), item);
|
||||
}
|
||||
plugin.clearHologram(this);
|
||||
location.getBlock().setType(Material.AIR);
|
||||
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9))
|
||||
location.getWorld().spawnParticle(Particle.LAVA, location.clone().add(.5, .5, .5), 5, 0, 0, 0, 5);
|
||||
|
||||
location.getWorld().playSound(location, ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9)
|
||||
? Sound.ENTITY_GENERIC_EXPLODE : Sound.valueOf("EXPLODE"), 10, 10);
|
||||
|
||||
plugin.getAnchorManager().removeAnchor(location);
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location.clone();
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return chunkX;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return location.getWorld();
|
||||
}
|
||||
|
||||
public int getTicksLeft() {
|
||||
return ticksLeft;
|
||||
}
|
||||
|
||||
public void setTicksLeft(int ticksLeft) {
|
||||
this.ticksLeft = ticksLeft;
|
||||
}
|
||||
|
||||
public boolean isInfinite() {
|
||||
return isInfinite;
|
||||
}
|
||||
|
||||
public void setInfinite(boolean infinite) {
|
||||
isInfinite = infinite;
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package com.songoda.epicanchors.anchor;
|
||||
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AnchorManager {
|
||||
|
||||
private final Map<Location, Anchor> registeredAnchors = new HashMap<>();
|
||||
|
||||
public Anchor addAnchor(Location location, Anchor anchor) {
|
||||
return registeredAnchors.put(roundLocation(location), anchor);
|
||||
}
|
||||
|
||||
public void removeAnchor(Location location) {
|
||||
registeredAnchors.remove(roundLocation(location));
|
||||
}
|
||||
|
||||
public Anchor getAnchor(Location location) {
|
||||
return registeredAnchors.get(roundLocation(location));
|
||||
}
|
||||
|
||||
public Anchor getAnchor(String world, int chunkX, int chunkZ) {
|
||||
return this.registeredAnchors.values().stream()
|
||||
.filter(anchor -> anchor.getWorld().getName().equals(world) && anchor.getChunkX() == chunkX && anchor.getChunkZ() == chunkZ).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public boolean isAnchor(Location location) {
|
||||
return registeredAnchors.containsKey(location);
|
||||
}
|
||||
|
||||
public Map<Location, Anchor> getAnchors() {
|
||||
return Collections.unmodifiableMap(registeredAnchors);
|
||||
}
|
||||
|
||||
private Location roundLocation(org.bukkit.Location location) {
|
||||
location = location.clone();
|
||||
location.setX(location.getBlockX());
|
||||
location.setY(location.getBlockY());
|
||||
location.setZ(location.getBlockZ());
|
||||
return location;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.songoda.epicanchors.api;
|
||||
|
||||
import com.songoda.epicanchors.Anchor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AnchorAccessCheck {
|
||||
boolean check(@NotNull Anchor anchor, @NotNull UUID uuid);
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package com.songoda.epicanchors.commands;
|
||||
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.utils.Methods;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandGive extends AbstractCommand {
|
||||
|
||||
final EpicAnchors instance;
|
||||
|
||||
public CommandGive(EpicAnchors instance) {
|
||||
super(false, "give");
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
if (args.length != 2) return ReturnType.SYNTAX_ERROR;
|
||||
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null && !args[0].trim().toLowerCase().equals("all")) {
|
||||
instance.getLocale().newMessage("&cThat is not a player...").sendPrefixedMessage(sender);
|
||||
return ReturnType.SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ItemStack itemStack;
|
||||
|
||||
if (Methods.isInt(args[1])) {
|
||||
itemStack = (Integer.parseInt(args[1]) <= 0) ? instance.makeAnchorItem(-99) : instance.makeAnchorItem(Integer.parseInt(args[1]) * 20 * 60 * 60);
|
||||
} else if (args[1].toLowerCase().equals("infinite")) {
|
||||
itemStack = instance.makeAnchorItem(-99);
|
||||
} else {
|
||||
instance.getLocale().newMessage("&cYou can only use whole numbers...").sendPrefixedMessage(sender);
|
||||
return ReturnType.FAILURE;
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
target.getInventory().addItem(itemStack);
|
||||
instance.getLocale().getMessage("command.give.success").sendPrefixedMessage(target);
|
||||
} else {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.getInventory().addItem(itemStack);
|
||||
instance.getLocale().getMessage("command.give.success").sendPrefixedMessage(player);
|
||||
}
|
||||
}
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender commandSender, String... strings) {
|
||||
if (strings.length == 1) {
|
||||
List<String> players = new ArrayList<>();
|
||||
players.add("all");
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
players.add(player.getName());
|
||||
}
|
||||
return players;
|
||||
} else if (strings.length == 2) {
|
||||
return Arrays.asList("1", "2", "3", "4", "5");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "epicanchors.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/ea give <player/all> <amount in hours / infinite>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Gives an operator the ability to spawn a ChunkAnchor of his or her choice.";
|
||||
}
|
||||
}
|
@ -1,30 +1,35 @@
|
||||
package com.songoda.epicanchors.commands;
|
||||
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.core.commands.CommandManager;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandEpicAnchors extends AbstractCommand {
|
||||
public class EpicAnchorsCommand extends AbstractCommand {
|
||||
private final EpicAnchors plugin;
|
||||
private final CommandManager commandManager;
|
||||
|
||||
final EpicAnchors instance;
|
||||
public EpicAnchorsCommand(EpicAnchors plugin, CommandManager commandManager) {
|
||||
super(CommandType.CONSOLE_OK, false, "EpicAnchors");
|
||||
|
||||
public CommandEpicAnchors(EpicAnchors instance) {
|
||||
super(false, "EpicAnchors");
|
||||
this.instance = instance;
|
||||
this.plugin = plugin;
|
||||
this.commandManager = commandManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
sender.sendMessage("");
|
||||
instance.getLocale().newMessage("&7Version " + instance.getDescription().getVersion()
|
||||
this.plugin.getLocale().newMessage("&7Version " + this.plugin.getDescription().getVersion()
|
||||
+ " Created with <3 by &5&l&oSongoda").sendPrefixedMessage(sender);
|
||||
|
||||
for (AbstractCommand command : instance.getCommandManager().getAllCommands()) {
|
||||
if (command.getPermissionNode() == null || sender.hasPermission(command.getPermissionNode())) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&8 - &a" + command.getSyntax() + "&7 - " + command.getDescription()));
|
||||
for (AbstractCommand cmd : this.commandManager.getAllCommands()) {
|
||||
if (cmd.getPermissionNode() == null || sender.hasPermission(cmd.getPermissionNode())) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
"&8 - &a" + cmd.getSyntax() + "&7 - " + cmd.getDescription()));
|
||||
}
|
||||
}
|
||||
sender.sendMessage("");
|
||||
@ -34,7 +39,7 @@ public class CommandEpicAnchors extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender commandSender, String... strings) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
@ -0,0 +1,116 @@
|
||||
package com.songoda.epicanchors.commands.sub;
|
||||
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.utils.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class GiveCommand extends AbstractCommand {
|
||||
private final EpicAnchors plugin;
|
||||
|
||||
public GiveCommand(EpicAnchors plugin) {
|
||||
super(CommandType.CONSOLE_OK, false, "give");
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
if (args.length != 2) return ReturnType.SYNTAX_ERROR;
|
||||
|
||||
Player target = Bukkit.getPlayerExact(args[0]);
|
||||
|
||||
if (target == null &&
|
||||
!args[0].trim().equalsIgnoreCase("all") &&
|
||||
!args[0].trim().equalsIgnoreCase("@a")) {
|
||||
plugin.getLocale().newMessage("&cThat is not a player...").sendPrefixedMessage(sender);
|
||||
|
||||
return ReturnType.SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
ItemStack itemStack;
|
||||
|
||||
if (Utils.isInt(args[1]) && Integer.parseInt(args[1]) > 0) {
|
||||
itemStack = plugin.getAnchorManager().createAnchorItem(Integer.parseInt(args[1]) * 20 * 60 * 60);
|
||||
} else if (args[1].equalsIgnoreCase("infinite")) {
|
||||
itemStack = plugin.getAnchorManager().createAnchorItem(-1);
|
||||
} else {
|
||||
plugin.getLocale().newMessage("&cYou can only use positive whole numbers...").sendPrefixedMessage(sender);
|
||||
|
||||
return ReturnType.FAILURE;
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
target.getInventory().addItem(itemStack);
|
||||
plugin.getLocale().getMessage("command.give.success").sendPrefixedMessage(target);
|
||||
} else {
|
||||
for (Player online : Bukkit.getOnlinePlayers()) {
|
||||
online.getInventory().addItem(itemStack);
|
||||
plugin.getLocale().getMessage("command.give.success").sendPrefixedMessage(online);
|
||||
}
|
||||
}
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender commandSender, String... args) {
|
||||
if (args.length == 1) {
|
||||
Set<String> players = new HashSet<>();
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
players.add(player.getName());
|
||||
}
|
||||
|
||||
players.add("all");
|
||||
|
||||
if ("@a".startsWith(args[0].toLowerCase())) {
|
||||
players.add("@a");
|
||||
}
|
||||
|
||||
return Utils.getMatches(args[0], players, true);
|
||||
} else if (args.length == 2) {
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
result.add("infinite");
|
||||
|
||||
if (args[1].isEmpty()) {
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
result.add(String.valueOf(i));
|
||||
}
|
||||
} else if (Utils.isInt(args[1]) && args[1].charAt(0) != '-') {
|
||||
result.add(args[1] + "0");
|
||||
result.add(args[1] + "00");
|
||||
result.add(args[1] + "000");
|
||||
}
|
||||
|
||||
return Utils.getMatches(args[1], result, true);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "EpicAnchors.cmd.give";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/ea give <player/all> <amount in hours / infinite>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Gives an operator the ability to spawn a ChunkAnchor of his or her choice.";
|
||||
}
|
||||
}
|
@ -1,35 +1,36 @@
|
||||
package com.songoda.epicanchors.commands;
|
||||
package com.songoda.epicanchors.commands.sub;
|
||||
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandReload extends AbstractCommand {
|
||||
public class ReloadCommand extends AbstractCommand {
|
||||
private final EpicAnchors plugin;
|
||||
|
||||
final EpicAnchors instance;
|
||||
|
||||
public CommandReload(EpicAnchors instance) {
|
||||
super(false, "reload");
|
||||
this.instance = instance;
|
||||
public ReloadCommand(EpicAnchors plugin) {
|
||||
super(CommandType.CONSOLE_OK, false, "reload");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
instance.reloadConfig();
|
||||
instance.getLocale().getMessage("&7Configuration and Language files reloaded.").sendPrefixedMessage(sender);
|
||||
plugin.reloadConfig();
|
||||
plugin.getLocale().getMessage("&7Configuration and Language files reloaded.").sendPrefixedMessage(sender);
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "epicanchors.admin";
|
||||
return "EpicAnchors.cmd.reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -41,5 +42,4 @@ public class CommandReload extends AbstractCommand {
|
||||
public String getDescription() {
|
||||
return "Reload the Configuration and Language files.";
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.songoda.epicanchors.commands;
|
||||
package com.songoda.epicanchors.commands.sub;
|
||||
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.core.configuration.editor.PluginConfigGui;
|
||||
@ -7,15 +7,16 @@ import com.songoda.epicanchors.EpicAnchors;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandSettings extends AbstractCommand {
|
||||
public class SettingsCommand extends AbstractCommand {
|
||||
private final EpicAnchors instance;
|
||||
private final GuiManager guiManager;
|
||||
|
||||
final EpicAnchors instance;
|
||||
final GuiManager guiManager;
|
||||
public SettingsCommand(EpicAnchors instance, GuiManager manager) {
|
||||
super(CommandType.PLAYER_ONLY, false, "settings");
|
||||
|
||||
public CommandSettings(EpicAnchors instance, GuiManager manager) {
|
||||
super(true, "settings");
|
||||
this.instance = instance;
|
||||
this.guiManager = manager;
|
||||
}
|
||||
@ -23,17 +24,18 @@ public class CommandSettings extends AbstractCommand {
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
guiManager.showGUI((Player) sender, new PluginConfigGui(instance));
|
||||
|
||||
return AbstractCommand.ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender commandSender, String... strings) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "epicanchors.admin";
|
||||
return "EpicAnchors.cmd.settings";
|
||||
}
|
||||
|
||||
@Override
|
@ -1,50 +1,49 @@
|
||||
package com.songoda.epicanchors.commands;
|
||||
package com.songoda.epicanchors.commands.sub;
|
||||
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.tasks.VisualizeTask;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandShow extends AbstractCommand {
|
||||
|
||||
public class ShowCommand extends AbstractCommand {
|
||||
private final EpicAnchors plugin;
|
||||
|
||||
public CommandShow(EpicAnchors plugin) {
|
||||
super(true, "show");
|
||||
public ShowCommand(EpicAnchors plugin) {
|
||||
super(CommandType.PLAYER_ONLY, false, "show");
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
if(!(sender instanceof Player)) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(ChatColor.RED + "Command must be called as a player");
|
||||
|
||||
return ReturnType.FAILURE;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
|
||||
if (args.length != 0)
|
||||
return ReturnType.SYNTAX_ERROR;
|
||||
if (args.length != 0) return ReturnType.SYNTAX_ERROR;
|
||||
|
||||
if(VisualizeTask.togglePlayer(player))
|
||||
plugin.getLocale().getMessage("command.show.start").sendPrefixedMessage(player);
|
||||
else
|
||||
plugin.getLocale().getMessage("command.show.stop").sendPrefixedMessage(player);
|
||||
boolean visualize = this.plugin.getAnchorManager().toggleChunkVisualized((Player) sender);
|
||||
|
||||
plugin.getLocale().getMessage("command.show." + (visualize ? "start" : "stop"))
|
||||
.sendPrefixedMessage(sender);
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "epicanchors.show";
|
||||
return "EpicAnchors.cmd.show";
|
||||
}
|
||||
|
||||
@Override
|
274
src/main/java/com/songoda/epicanchors/files/DataManager.java
Normal file
274
src/main/java/com/songoda/epicanchors/files/DataManager.java
Normal file
@ -0,0 +1,274 @@
|
||||
package com.songoda.epicanchors.files;
|
||||
|
||||
import com.songoda.core.database.DataManagerAbstract;
|
||||
import com.songoda.core.database.DatabaseConnector;
|
||||
import com.songoda.epicanchors.Anchor;
|
||||
import com.songoda.epicanchors.files.migration.AnchorMigration;
|
||||
import com.songoda.epicanchors.utils.Callback;
|
||||
import com.songoda.epicanchors.utils.UpdateCallback;
|
||||
import com.songoda.epicanchors.utils.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DataManager extends DataManagerAbstract {
|
||||
private final ExecutorService thread = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final String anchorTable;
|
||||
|
||||
public DataManager(DatabaseConnector databaseConnector, Plugin plugin) {
|
||||
super(databaseConnector, plugin);
|
||||
|
||||
this.anchorTable = getTableName(super.getTablePrefix(), "anchors");
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (!this.thread.isShutdown()) {
|
||||
this.thread.shutdown();
|
||||
|
||||
try {
|
||||
if (!this.thread.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||
// Try stopping the thread forcefully (there is basically no hope left for the data)
|
||||
this.thread.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Utils.logException(super.plugin, ex);
|
||||
}
|
||||
|
||||
this.databaseConnector.closeConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public void exists(@NotNull String worldName, int x, int y, int z, @NotNull Callback<Boolean> callback) {
|
||||
this.databaseConnector.connect((con) -> {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT id FROM " + this.anchorTable +
|
||||
" WHERE world_name =? AND x =? AND y =? AND z=?;")) {
|
||||
ps.setString(1, worldName);
|
||||
ps.setInt(2, x);
|
||||
ps.setInt(3, y);
|
||||
ps.setInt(4, z);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
callback.accept(null, rs.next());
|
||||
} catch (Exception ex) {
|
||||
resolveCallback(callback, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void getAnchors(@Nullable World world, @NotNull Callback<List<Anchor>> callback) {
|
||||
List<Anchor> result = new ArrayList<>();
|
||||
|
||||
this.databaseConnector.connect((con) -> {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM " + this.anchorTable +
|
||||
(world != null ? " WHERE world_name =?" : "") + ";")) {
|
||||
if (world != null) {
|
||||
ps.setString(1, world.getName());
|
||||
}
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
result.add(extractAnchor(rs));
|
||||
}
|
||||
|
||||
callback.accept(null, result);
|
||||
} catch (Exception ex) {
|
||||
resolveCallback(callback, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void insertAnchorAsync(Location loc, UUID owner, int ticks, Callback<Anchor> callback) {
|
||||
this.thread.execute(() -> insertAnchor(loc, owner, ticks, callback));
|
||||
}
|
||||
|
||||
public void insertAnchor(Location loc, UUID owner, int ticks, Callback<Anchor> callback) {
|
||||
this.databaseConnector.connect((con) -> {
|
||||
try (PreparedStatement ps = con.prepareStatement("INSERT INTO " + this.anchorTable +
|
||||
"(owner, world_name,x,y,z, ticks_left) VALUES (?,?,?,?,?, ?);");// Future SQLite version might support 'RETURNING *'
|
||||
PreparedStatement psFetch = con.prepareStatement("SELECT * FROM " + this.anchorTable +
|
||||
" WHERE world_name =? AND x =? AND y =? AND z=?;")) {
|
||||
ps.setString(1, owner != null ? owner.toString() : null);
|
||||
|
||||
ps.setString(2, Objects.requireNonNull(loc.getWorld()).getName());
|
||||
psFetch.setString(1, Objects.requireNonNull(loc.getWorld()).getName());
|
||||
|
||||
ps.setInt(3, loc.getBlockX());
|
||||
psFetch.setInt(2, loc.getBlockX());
|
||||
|
||||
ps.setInt(4, loc.getBlockY());
|
||||
psFetch.setInt(3, loc.getBlockY());
|
||||
|
||||
ps.setInt(5, loc.getBlockZ());
|
||||
psFetch.setInt(4, loc.getBlockZ());
|
||||
|
||||
ps.setInt(6, ticks);
|
||||
|
||||
ps.executeUpdate();
|
||||
|
||||
if (callback != null) {
|
||||
ResultSet rs = psFetch.executeQuery();
|
||||
rs.next();
|
||||
|
||||
callback.accept(null, extractAnchor(rs));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
resolveCallback(callback, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void migrateAnchor(List<AnchorMigration.LegacyAnchorEntry> anchorEntries, UpdateCallback callback) {
|
||||
this.databaseConnector.connect((con) -> {
|
||||
con.setAutoCommit(false);
|
||||
|
||||
SQLException err = null;
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("INSERT INTO " + this.anchorTable +
|
||||
"(world_name,x,y,z, ticks_left) VALUES (?,?,?,?, ?);")) {
|
||||
for (AnchorMigration.LegacyAnchorEntry entry : anchorEntries) {
|
||||
ps.setString(1, entry.worldName);
|
||||
ps.setInt(2, entry.x);
|
||||
ps.setInt(3, entry.y);
|
||||
ps.setInt(4, entry.z);
|
||||
|
||||
ps.setInt(5, entry.ticksLeft);
|
||||
|
||||
ps.addBatch();
|
||||
}
|
||||
|
||||
int[] batchRes = ps.executeBatch();
|
||||
|
||||
for (int i : batchRes) {
|
||||
if (i < 0 && i != Statement.SUCCESS_NO_INFO) {
|
||||
throw new AssertionError("Batch-INSERT failed for at least one statement with code " + i + "");
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
err = ex;
|
||||
}
|
||||
|
||||
if (err == null) {
|
||||
con.commit();
|
||||
|
||||
resolveUpdateCallback(callback, null);
|
||||
} else {
|
||||
con.rollback();
|
||||
|
||||
resolveUpdateCallback(callback, err);
|
||||
}
|
||||
|
||||
con.setAutoCommit(true);
|
||||
});
|
||||
}
|
||||
|
||||
public void updateAnchors(Collection<Anchor> anchors, UpdateCallback callback) {
|
||||
this.databaseConnector.connect((con) -> {
|
||||
con.setAutoCommit(false);
|
||||
|
||||
SQLException err = null;
|
||||
|
||||
for (Anchor anchor : anchors) {
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE " + this.anchorTable +
|
||||
" SET ticks_left =? WHERE id =?;")) {
|
||||
ps.setInt(1, anchor.getTicksLeft());
|
||||
ps.setInt(2, anchor.getDbId());
|
||||
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
err = ex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err == null) {
|
||||
con.commit();
|
||||
|
||||
resolveUpdateCallback(callback, null);
|
||||
} else {
|
||||
con.rollback();
|
||||
|
||||
resolveUpdateCallback(callback, err);
|
||||
}
|
||||
|
||||
con.setAutoCommit(true);
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteAnchorAsync(Anchor anchor) {
|
||||
deleteAnchorAsync(anchor, null);
|
||||
}
|
||||
|
||||
public void deleteAnchorAsync(Anchor anchor, UpdateCallback callback) {
|
||||
this.thread.execute(() ->
|
||||
this.databaseConnector.connect((con) -> {
|
||||
try (PreparedStatement ps = con.prepareStatement("DELETE FROM " + this.anchorTable +
|
||||
" WHERE id =?;")) {
|
||||
ps.setInt(1, anchor.getDbId());
|
||||
|
||||
ps.executeUpdate();
|
||||
|
||||
resolveUpdateCallback(callback, null);
|
||||
} catch (Exception ex) {
|
||||
resolveUpdateCallback(callback, ex);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public static String getTableName(String prefix, String name) {
|
||||
String result = prefix + name;
|
||||
|
||||
if (!result.matches("[a-z0-9_]+")) {
|
||||
throw new IllegalStateException("The generated table name '" + result + "' contains invalid characters");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Anchor extractAnchor(ResultSet rs) throws SQLException {
|
||||
String ownerStr = rs.getString("owner");
|
||||
|
||||
return new Anchor(rs.getInt("id"),
|
||||
ownerStr != null ? UUID.fromString(ownerStr) : null,
|
||||
new Location(Bukkit.getWorld(rs.getString("world_name")),
|
||||
rs.getInt("x"),
|
||||
rs.getInt("y"),
|
||||
rs.getInt("z")),
|
||||
rs.getInt("ticks_left"));
|
||||
}
|
||||
|
||||
private void resolveUpdateCallback(@Nullable UpdateCallback callback, @Nullable Exception ex) {
|
||||
if (callback != null) {
|
||||
callback.accept(ex);
|
||||
} else if (ex != null) {
|
||||
Utils.logException(this.plugin, ex, "SQLite");
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveCallback(@Nullable Callback<?> callback, @NotNull Exception ex) {
|
||||
if (callback != null) {
|
||||
callback.accept(ex, null);
|
||||
} else {
|
||||
Utils.logException(this.plugin, ex, "SQLite");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +1,72 @@
|
||||
package com.songoda.epicanchors.settings;
|
||||
package com.songoda.epicanchors.files;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import com.songoda.core.compatibility.CompatibleParticleHandler;
|
||||
import com.songoda.core.configuration.Config;
|
||||
import com.songoda.core.configuration.ConfigSetting;
|
||||
import com.songoda.core.hooks.EconomyManager;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class Settings {
|
||||
private Settings() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
static final Config config = EpicAnchors.getInstance().getCoreConfig();
|
||||
public static final Config config = JavaPlugin.getPlugin(EpicAnchors.class).getCoreConfig();
|
||||
|
||||
public static final ConfigSetting NAMETAG = new ConfigSetting(config, "Main.Name Tag", "&6Anchor &8(&7{REMAINING}&8)",
|
||||
public static final ConfigSetting NAME_TAG = new ConfigSetting(config, "Main.Name Tag",
|
||||
"&6Anchor &8(&7{REMAINING}&8)",
|
||||
"The anchor name tag used on the item and in the hologram.");
|
||||
|
||||
public static final ConfigSetting LORE = new ConfigSetting(config, "Main.Anchor Lore", "&7Place down to keep that chunk|&7loaded until the time runs out.",
|
||||
public static final ConfigSetting LORE = new ConfigSetting(config, "Main.Anchor Lore",
|
||||
"&7Place down to keep that chunk\n&7loaded until the time runs out.",
|
||||
"The lore on the anchor item.");
|
||||
|
||||
public static final ConfigSetting MATERIAL = new ConfigSetting(config, "Main.Anchor Block Material", CompatibleMaterial.END_PORTAL_FRAME.getMaterial().name(),
|
||||
"The material an anchor is represented with?");
|
||||
public static final ConfigSetting MATERIAL = new ConfigSetting(config, "Main.Anchor Block Material",
|
||||
CompatibleMaterial.END_PORTAL_FRAME.name(), "The material an anchor is represented with?");
|
||||
|
||||
public static final ConfigSetting ADD_TIME_WITH_XP = new ConfigSetting(config, "Main.Add Time With XP", true,
|
||||
"Should players be able to add time to their anchors by using experience?");
|
||||
|
||||
public static final ConfigSetting XP_COST = new ConfigSetting(config, "Main.XP Cost", 10,
|
||||
"The cost in experience levels to add 30 minutes to an anchor.");
|
||||
|
||||
public static final ConfigSetting ADD_TIME_WITH_ECONOMY = new ConfigSetting(config, "Main.Add Time With Economy", true,
|
||||
"Should players be able to add time to their anchors",
|
||||
"by using economy?");
|
||||
"Should players be able to add time to their anchors by using economy?");
|
||||
|
||||
public static final ConfigSetting ECONOMY_COST = new ConfigSetting(config, "Main.Economy Cost", 5000.0,
|
||||
"The cost in economy to add 30 minutes to an anchor.");
|
||||
|
||||
public static final ConfigSetting ADD_TIME_WITH_XP = new ConfigSetting(config, "Main.Add Time With XP", true,
|
||||
"Should players be able to add time to their anchors",
|
||||
"by using experience?");
|
||||
|
||||
public static final ConfigSetting XP_COST = new ConfigSetting(config, "Main.XP Cost", 10,
|
||||
"The cost in experience to add 30 minutes to an anchor.");
|
||||
|
||||
public static final ConfigSetting ALLOW_ANCHOR_BREAKING = new ConfigSetting(config, "Main.Allow Anchor Breaking", false,
|
||||
"Should players be able to break anchors?");
|
||||
"Should players be able to break anchors and get an item dropped?");
|
||||
|
||||
public static final ConfigSetting HOLOGRAMS = new ConfigSetting(config, "Main.Holograms", true,
|
||||
"Toggle holograms showing above anchors.");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final ConfigSetting ECONOMY_PLUGIN = new ConfigSetting(config, "Main.Economy", EconomyManager.getEconomy() == null ? "Vault" : EconomyManager.getEconomy().getName(),
|
||||
"Which economy plugin should be used?",
|
||||
"Supported plugins you have installed: \"" + EconomyManager.getManager().getRegisteredPlugins().stream().collect(Collectors.joining("\", \"")) + "\".");
|
||||
"Supported plugins you have installed: \"" + String.join(", ", EconomyManager.getManager().getRegisteredPlugins()) + "\".");
|
||||
|
||||
public static final ConfigSetting ECO_ICON = new ConfigSetting(config, "Interfaces.Economy Icon", "SUNFLOWER",
|
||||
public static final ConfigSetting ECO_ICON = new ConfigSetting(config, "Interfaces.Economy Icon", CompatibleMaterial.SUNFLOWER.name(),
|
||||
"Item to be displayed as the icon for economy upgrades.");
|
||||
|
||||
public static final ConfigSetting XP_ICON = new ConfigSetting(config, "Interfaces.XP Icon", "EXPERIENCE_BOTTLE",
|
||||
public static final ConfigSetting XP_ICON = new ConfigSetting(config, "Interfaces.XP Icon", CompatibleMaterial.EXPERIENCE_BOTTLE.name(),
|
||||
"Item to be displayed as the icon for XP upgrades.");
|
||||
|
||||
public static final ConfigSetting GLASS_TYPE_1 = new ConfigSetting(config, "Interfaces.Glass Type 1", "GRAY_STAINED_GLASS_PANE");
|
||||
public static final ConfigSetting GLASS_TYPE_2 = new ConfigSetting(config, "Interfaces.Glass Type 2", "BLUE_STAINED_GLASS_PANE");
|
||||
public static final ConfigSetting GLASS_TYPE_3 = new ConfigSetting(config, "Interfaces.Glass Type 3", "LIGHT_BLUE_STAINED_GLASS_PANE");
|
||||
public static final ConfigSetting GLASS_TYPE_1 = new ConfigSetting(config, "Interfaces.Glass Type 1", CompatibleMaterial.GRAY_STAINED_GLASS_PANE.name());
|
||||
public static final ConfigSetting GLASS_TYPE_2 = new ConfigSetting(config, "Interfaces.Glass Type 2", CompatibleMaterial.BLUE_STAINED_GLASS_PANE.name());
|
||||
public static final ConfigSetting GLASS_TYPE_3 = new ConfigSetting(config, "Interfaces.Glass Type 3", CompatibleMaterial.LIGHT_BLUE_STAINED_GLASS_PANE.name());
|
||||
|
||||
public static final ConfigSetting LANGUGE_MODE = new ConfigSetting(config, "System.Language Mode", "en_US",
|
||||
public static final ConfigSetting PARTICLE_DESTROY = new ConfigSetting(config, "Particles.Destroy",
|
||||
CompatibleParticleHandler.ParticleType.LAVA.name());
|
||||
public static final ConfigSetting PARTICLE_UPGRADE = new ConfigSetting(config, "Particles.Upgrade",
|
||||
CompatibleParticleHandler.ParticleType.SPELL_WITCH.name());
|
||||
public static final ConfigSetting PARTICLE_VISUALIZER = new ConfigSetting(config, "Particles.Visualizer",
|
||||
CompatibleParticleHandler.ParticleType.VILLAGER_HAPPY.name());
|
||||
|
||||
public static final ConfigSetting LANGUAGE = new ConfigSetting(config, "System.Language Mode", "en_US",
|
||||
"The enabled language file.",
|
||||
"More language files (if available) can be found in the plugins data folder.");
|
||||
|
||||
@ -66,7 +76,8 @@ public class Settings {
|
||||
*/
|
||||
public static void setupConfig() {
|
||||
config.load();
|
||||
config.setAutoremove(true).setAutosave(true);
|
||||
config.setAutoremove(true)
|
||||
.setAutosave(true);
|
||||
|
||||
// convert glass pane settings
|
||||
int color;
|
||||
@ -91,4 +102,4 @@ public class Settings {
|
||||
|
||||
config.saveChanges();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package com.songoda.epicanchors.files.migration;
|
||||
|
||||
import com.songoda.core.configuration.Config;
|
||||
import com.songoda.core.configuration.ConfigSection;
|
||||
import com.songoda.core.database.DataMigration;
|
||||
import com.songoda.core.database.DataMigrationManager;
|
||||
import com.songoda.core.database.DatabaseConnector;
|
||||
import com.songoda.epicanchors.files.DataManager;
|
||||
import com.songoda.epicanchors.utils.ThreadSync;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class AnchorMigration extends DataMigrationManager {
|
||||
private final DataManager dataManager;
|
||||
|
||||
public AnchorMigration(DatabaseConnector databaseConnector, DataManager dataManager, DataMigration... migrations) {
|
||||
super(databaseConnector, dataManager, migrations);
|
||||
|
||||
this.dataManager = dataManager;
|
||||
}
|
||||
|
||||
public void migrateLegacyData(Plugin plugin) {
|
||||
long start = System.nanoTime();
|
||||
|
||||
AtomicBoolean abortMigration = new AtomicBoolean(false);
|
||||
int migratedAnchors = 0;
|
||||
|
||||
Config legacyData = new Config(plugin, "data.yml");
|
||||
|
||||
if (legacyData.getFile().exists()) {
|
||||
ThreadSync thSync = new ThreadSync();
|
||||
|
||||
legacyData.load();
|
||||
|
||||
ConfigSection cfgSection = legacyData.getConfigurationSection("Anchors");
|
||||
|
||||
if (cfgSection == null) return;
|
||||
|
||||
List<LegacyAnchorEntry> anchorQueue = new ArrayList<>();
|
||||
|
||||
for (String locationStr : cfgSection.getKeys(false)) {
|
||||
int ticksLeft = cfgSection.getInt(locationStr + ".ticksLeft");
|
||||
String[] locArgs = deserializeLegacyLocation(locationStr);
|
||||
|
||||
if (ticksLeft == -99) {
|
||||
ticksLeft = -1;
|
||||
}
|
||||
|
||||
if (locArgs.length == 0) {
|
||||
abortMigration.set(true);
|
||||
|
||||
plugin.getLogger().warning(() -> "Error migrating anchor '" + locationStr + "': invalid format - expected 'worldName:X:Y:Z'");
|
||||
break;
|
||||
}
|
||||
|
||||
String worldName = locArgs[0];
|
||||
int x = Location.locToBlock(Double.parseDouble(locArgs[1]));
|
||||
int y = Location.locToBlock(Double.parseDouble(locArgs[2]));
|
||||
int z = Location.locToBlock(Double.parseDouble(locArgs[3]));
|
||||
|
||||
int finalTicksLeft = ticksLeft;
|
||||
dataManager.exists(worldName, x, y, z, (ex, anchorExists) -> {
|
||||
if (ex == null) {
|
||||
if (anchorExists) {
|
||||
cfgSection.set(locationStr, null);
|
||||
} else {
|
||||
anchorQueue.add(new LegacyAnchorEntry(worldName, x, y, z, finalTicksLeft));
|
||||
}
|
||||
} else {
|
||||
abortMigration.set(true);
|
||||
|
||||
plugin.getLogger().log(Level.WARNING, ex, () -> "Error migrating Anchor '" + locationStr + "' from '" +
|
||||
legacyData.getFile().getName() + "'");
|
||||
}
|
||||
|
||||
thSync.release();
|
||||
});
|
||||
|
||||
thSync.waitForRelease();
|
||||
thSync.reset();
|
||||
|
||||
if (abortMigration.get()) break;
|
||||
|
||||
++migratedAnchors;
|
||||
}
|
||||
|
||||
if (!abortMigration.get()) {
|
||||
int finalMigratedAnchors = migratedAnchors;
|
||||
this.dataManager.migrateAnchor(anchorQueue, ex -> {
|
||||
long end = System.nanoTime();
|
||||
|
||||
if (ex == null) {
|
||||
try {
|
||||
Files.deleteIfExists(legacyData.getFile().toPath());
|
||||
} catch (IOException err) {
|
||||
plugin.getLogger().warning("Could not delete '" + legacyData.getFile().getName() + "' after data migration: " + err.getMessage());
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Successfully migrated " + finalMigratedAnchors + " Anchors from '" +
|
||||
legacyData.getFile().getName() + "' (" + TimeUnit.NANOSECONDS.toMillis(end - start) + "ms)");
|
||||
} else {
|
||||
legacyData.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] deserializeLegacyLocation(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
str = str
|
||||
.replace("w:", "")
|
||||
.replace("x:", ":")
|
||||
.replace("y:", ":")
|
||||
.replace("z:", ":")
|
||||
.replace("/", ".");
|
||||
|
||||
String[] args = str.split(":");
|
||||
|
||||
return args.length == 4 ? args : new String[0];
|
||||
}
|
||||
|
||||
public static class LegacyAnchorEntry {
|
||||
public final String worldName;
|
||||
public final int x;
|
||||
public final int y;
|
||||
public final int z;
|
||||
|
||||
public final int ticksLeft;
|
||||
|
||||
public LegacyAnchorEntry(String worldName, int x, int y, int z, int ticksLeft) {
|
||||
this.worldName = worldName;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.ticksLeft = ticksLeft;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.songoda.epicanchors.files.migration;
|
||||
|
||||
import com.songoda.core.database.DataMigration;
|
||||
import com.songoda.epicanchors.files.DataManager;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class _1_InitialMigration extends DataMigration {
|
||||
public _1_InitialMigration() {
|
||||
super(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(Connection connection, String tablePrefix) throws SQLException {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute("CREATE TABLE " + DataManager.getTableName(tablePrefix, "anchors") + "(" +
|
||||
"id INTEGER NOT NULL," +
|
||||
"world_name TEXT NOT NULL," +
|
||||
"x INTEGER NOT NULL," +
|
||||
"y INTEGER NOT NULL," +
|
||||
"z INTEGER NOT NULL," +
|
||||
"ticks_left INTEGER NOT NULL," +
|
||||
"owner VARCHAR(36)," +
|
||||
"PRIMARY KEY(id AUTOINCREMENT)" +
|
||||
");");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package com.songoda.epicanchors.gui;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiUtils;
|
||||
import com.songoda.core.hooks.EconomyManager;
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import com.songoda.core.utils.TimeUtils;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.anchor.Anchor;
|
||||
import com.songoda.epicanchors.settings.Settings;
|
||||
import com.songoda.epicanchors.utils.Methods;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class GUIOverview extends Gui {
|
||||
|
||||
private final EpicAnchors plugin;
|
||||
private final Anchor anchor;
|
||||
private final Player player;
|
||||
|
||||
private int task;
|
||||
|
||||
public GUIOverview(EpicAnchors plugin, Anchor anchor, Player player) {
|
||||
this.plugin = plugin;
|
||||
this.anchor = anchor;
|
||||
this.player = player;
|
||||
|
||||
this.setRows(3);
|
||||
this.setTitle(TextUtils.formatText(plugin.getLocale().getMessage("interface.anchor.title").getMessage()));
|
||||
|
||||
runTask();
|
||||
constructGUI();
|
||||
this.setOnClose(action -> Bukkit.getScheduler().cancelTask(task));
|
||||
}
|
||||
|
||||
private void constructGUI() {
|
||||
ItemStack glass1 = GuiUtils.getBorderItem(Settings.GLASS_TYPE_1.getMaterial());
|
||||
ItemStack glass2 = GuiUtils.getBorderItem(Settings.GLASS_TYPE_2.getMaterial());
|
||||
ItemStack glass3 = GuiUtils.getBorderItem(Settings.GLASS_TYPE_3.getMaterial());
|
||||
|
||||
setDefaultItem(glass1);
|
||||
|
||||
mirrorFill(0, 0, true, true, glass2);
|
||||
mirrorFill(0, 1, true, true, glass2);
|
||||
mirrorFill(0, 2, true, true, glass3);
|
||||
mirrorFill(1, 0, false, true, glass2);
|
||||
mirrorFill(1, 1, false, true, glass3);
|
||||
|
||||
setItem(13, GuiUtils.createButtonItem(plugin.makeAnchorItem(anchor.getTicksLeft()),
|
||||
plugin.getLocale().getMessage("interface.anchor.smalltitle").getMessage(),
|
||||
(anchor.isInfinite()) ? ChatColor.GRAY + "Infinite" : ChatColor.GRAY + TimeUtils.makeReadable((long) (anchor.getTicksLeft() / 20) * 1000) + " remaining."));
|
||||
|
||||
if (EconomyManager.isEnabled() && Settings.ADD_TIME_WITH_ECONOMY.getBoolean()) {
|
||||
setButton(15, GuiUtils.createButtonItem(Settings.ECO_ICON.getMaterial(CompatibleMaterial.SUNFLOWER),
|
||||
plugin.getLocale().getMessage("interface.button.addtimewitheconomy").getMessage(),
|
||||
plugin.getLocale().getMessage("interface.button.addtimewitheconomylore")
|
||||
.processPlaceholder("cost", EconomyManager.formatEconomy(Settings.ECONOMY_COST.getDouble())).getMessage()), // EconomyManager.formatEconomy adds its own prefix/suffix
|
||||
event -> checkInfiniteAndAlert(anchor, event.player, true));
|
||||
}
|
||||
|
||||
if (Settings.ADD_TIME_WITH_XP.getBoolean()) {
|
||||
setButton(11, GuiUtils.createButtonItem(Settings.XP_ICON.getMaterial(CompatibleMaterial.EXPERIENCE_BOTTLE),
|
||||
plugin.getLocale().getMessage("interface.button.addtimewithxp").getMessage(),
|
||||
plugin.getLocale().getMessage("interface.button.addtimewithxplore")
|
||||
.processPlaceholder("cost", String.valueOf(Settings.XP_COST.getInt())).getMessage()),
|
||||
event -> checkInfiniteAndAlert(anchor, event.player, false));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void runTask() {
|
||||
task = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
|
||||
updateItem(13, plugin.getLocale().getMessage("interface.anchor.smalltitle").getMessage(),
|
||||
(anchor.isInfinite()) ? ChatColor.GRAY + "Infinite" : ChatColor.GRAY + TimeUtils.makeReadable((long) (anchor.getTicksLeft() / 20) * 1000) + " remaining.");
|
||||
}, 5L, 5L);
|
||||
}
|
||||
|
||||
private void checkInfiniteAndAlert(Anchor anchor, Player p, boolean eco) {
|
||||
if (anchor.isInfinite()) {
|
||||
plugin.getLocale().getMessage("interface.button.infinite").sendPrefixedMessage(p);
|
||||
} else {
|
||||
if (eco) {
|
||||
anchor.addTime("ECO", p);
|
||||
} else {
|
||||
anchor.addTime("XP", p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
src/main/java/com/songoda/epicanchors/guis/AnchorGui.java
Normal file
135
src/main/java/com/songoda/epicanchors/guis/AnchorGui.java
Normal file
@ -0,0 +1,135 @@
|
||||
package com.songoda.epicanchors.guis;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.compatibility.CompatibleParticleHandler;
|
||||
import com.songoda.core.compatibility.CompatibleSound;
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiUtils;
|
||||
import com.songoda.core.hooks.EconomyManager;
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import com.songoda.core.utils.TimeUtils;
|
||||
import com.songoda.epicanchors.Anchor;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.files.Settings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class AnchorGui extends Gui {
|
||||
private final EpicAnchors plugin;
|
||||
private final Anchor anchor;
|
||||
|
||||
public AnchorGui(EpicAnchors plugin, Anchor anchor) {
|
||||
this.plugin = plugin;
|
||||
this.anchor = anchor;
|
||||
|
||||
setRows(3);
|
||||
setTitle(TextUtils.formatText(plugin.getLocale().getMessage("interface.anchor.title").getMessage()));
|
||||
|
||||
constructGUI();
|
||||
runPreparedGuiTask(this.plugin, this, this.anchor);
|
||||
}
|
||||
|
||||
private void constructGUI() {
|
||||
prepareGui(this.plugin, this, this.anchor);
|
||||
|
||||
if (Settings.ADD_TIME_WITH_XP.getBoolean()) {
|
||||
String itemName = plugin.getLocale().getMessage("interface.button.addtimewithxp").getMessage();
|
||||
String itemLore = plugin.getLocale().getMessage("interface.button.addtimewithxplore")
|
||||
.processPlaceholder("cost", Settings.XP_COST.getInt())
|
||||
.getMessage();
|
||||
|
||||
setButton(11,
|
||||
GuiUtils.createButtonItem(Settings.XP_ICON.getMaterial(CompatibleMaterial.EXPERIENCE_BOTTLE), itemName, itemLore),
|
||||
event -> buyTime(anchor, event.player, false));
|
||||
}
|
||||
|
||||
if (EconomyManager.isEnabled() && Settings.ADD_TIME_WITH_ECONOMY.getBoolean()) {
|
||||
String itemName = plugin.getLocale().getMessage("interface.button.addtimewitheconomy").getMessage();
|
||||
String itemLore = plugin.getLocale().getMessage("interface.button.addtimewitheconomylore")
|
||||
// EconomyManager#formatEconomy adds its own prefix/suffix
|
||||
.processPlaceholder("cost", EconomyManager.formatEconomy(Settings.ECONOMY_COST.getDouble()))
|
||||
.getMessage();
|
||||
|
||||
setButton(15,
|
||||
GuiUtils.createButtonItem(Settings.ECO_ICON.getMaterial(CompatibleMaterial.SUNFLOWER), itemName, itemLore),
|
||||
event -> buyTime(anchor, event.player, true));
|
||||
}
|
||||
}
|
||||
|
||||
private void buyTime(Anchor anchor, Player p, boolean eco) {
|
||||
if (anchor.isInfinite()) {
|
||||
this.plugin.getLocale().getMessage("interface.button.infinite").sendPrefixedMessage(p);
|
||||
} else {
|
||||
boolean success = false;
|
||||
|
||||
if (eco) {
|
||||
double cost = Settings.ECONOMY_COST.getDouble();
|
||||
|
||||
success = EconomyManager.withdrawBalance(p, cost);
|
||||
} else {
|
||||
int cost = Settings.XP_COST.getInt();
|
||||
|
||||
if (p.getLevel() >= cost || p.getGameMode() == GameMode.CREATIVE) {
|
||||
if (p.getGameMode() != GameMode.CREATIVE) {
|
||||
p.setLevel(p.getLevel() - cost);
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
anchor.addTicksLeft(20 * 60 * 30); // 30 minutes
|
||||
|
||||
p.playSound(p.getLocation(), CompatibleSound.ENTITY_PLAYER_LEVELUP.getSound(), 0.6F, 15);
|
||||
CompatibleParticleHandler.spawnParticles(CompatibleParticleHandler.ParticleType.getParticle(Settings.PARTICLE_UPGRADE.getString()),
|
||||
anchor.getLocation().add(.5, .5, .5), 100, .5, .5, .5);
|
||||
} else {
|
||||
this.plugin.getLocale().getMessage("event.upgrade.cannotafford").sendPrefixedMessage(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void prepareGui(EpicAnchors plugin, Gui gui, Anchor anchor) {
|
||||
ItemStack glass1 = GuiUtils.getBorderItem(Settings.GLASS_TYPE_1.getMaterial());
|
||||
ItemStack glass2 = GuiUtils.getBorderItem(Settings.GLASS_TYPE_2.getMaterial());
|
||||
ItemStack glass3 = GuiUtils.getBorderItem(Settings.GLASS_TYPE_3.getMaterial());
|
||||
|
||||
gui.setDefaultItem(glass1);
|
||||
|
||||
gui.mirrorFill(0, 0, true, true, glass2);
|
||||
gui.mirrorFill(0, 1, true, true, glass2);
|
||||
gui.mirrorFill(0, 2, true, true, glass3);
|
||||
gui.mirrorFill(1, 0, false, true, glass2);
|
||||
gui.mirrorFill(1, 1, false, true, glass3);
|
||||
|
||||
String itemName = plugin.getLocale().getMessage("interface.anchor.smalltitle").getMessage();
|
||||
String itemLore = anchor.isInfinite() ?
|
||||
ChatColor.GRAY + "Infinite" :
|
||||
ChatColor.GRAY + TimeUtils.makeReadable((long) ((anchor.getTicksLeft() / 20.0) * 1000)) + " remaining.";
|
||||
|
||||
gui.setItem(13, GuiUtils.createButtonItem(plugin.getAnchorManager().createAnchorItem(
|
||||
anchor.getTicksLeft(), anchor.getLocation().getBlock().getType()),
|
||||
itemName, itemLore));
|
||||
}
|
||||
|
||||
protected static void runPreparedGuiTask(EpicAnchors plugin, Gui gui, Anchor anchor) {
|
||||
int taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
|
||||
if (anchor.getTicksLeft() == 0) {
|
||||
gui.close();
|
||||
} else {
|
||||
String itemName = plugin.getLocale().getMessage("interface.anchor.smalltitle").getMessage();
|
||||
String itemLore = anchor.isInfinite() ?
|
||||
ChatColor.GRAY + "Infinite" :
|
||||
ChatColor.GRAY + TimeUtils.makeReadable((long) ((anchor.getTicksLeft() / 20.0) * 1000)) + " remaining.";
|
||||
|
||||
gui.updateItem(13, itemName, itemLore);
|
||||
}
|
||||
}, 0, 20);
|
||||
|
||||
gui.setOnClose(action -> Bukkit.getScheduler().cancelTask(taskId));
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.songoda.epicanchors.guis;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiUtils;
|
||||
import com.songoda.core.gui.methods.Closable;
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import com.songoda.epicanchors.Anchor;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.files.Settings;
|
||||
import com.songoda.epicanchors.utils.Callback;
|
||||
|
||||
public class DestroyConfirmationGui extends Gui {
|
||||
private final EpicAnchors plugin;
|
||||
private final Anchor anchor;
|
||||
|
||||
private Callback<Boolean> handler;
|
||||
|
||||
public DestroyConfirmationGui(EpicAnchors plugin, Anchor anchor, Callback<Boolean> callback) {
|
||||
this.plugin = plugin;
|
||||
this.anchor = anchor;
|
||||
|
||||
this.handler = (ex, result) -> {
|
||||
this.handler = null;
|
||||
this.close();
|
||||
|
||||
callback.accept(ex, result);
|
||||
};
|
||||
|
||||
this.setRows(3);
|
||||
this.setTitle(TextUtils.formatText(plugin.getLocale().getMessage("interface.anchor.title").getMessage()));
|
||||
|
||||
constructGUI();
|
||||
AnchorGui.runPreparedGuiTask(this.plugin, this, this.anchor);
|
||||
|
||||
Closable currClosable = this.closer;
|
||||
this.closer = event -> {
|
||||
currClosable.onClose(event);
|
||||
|
||||
if (this.handler != null) {
|
||||
this.handler.accept(null, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void constructGUI() {
|
||||
AnchorGui.prepareGui(this.plugin, this, this.anchor);
|
||||
|
||||
String cancelLore = plugin.getLocale().getMessage("interface.button.cancelDestroyLore").getMessage();
|
||||
String confirmLore = plugin.getLocale().getMessage("interface.button." +
|
||||
(Settings.ALLOW_ANCHOR_BREAKING.getBoolean() ? "confirmDestroyLore" : "confirmDestroyLoreNoDrops"))
|
||||
.getMessage();
|
||||
|
||||
setButton(11, GuiUtils.createButtonItem(CompatibleMaterial.RED_TERRACOTTA,
|
||||
plugin.getLocale().getMessage("interface.button.cancelDestroy").getMessage(),
|
||||
cancelLore.isEmpty() ? new String[0] : new String[] {cancelLore}),
|
||||
event -> this.handler.accept(null, false));
|
||||
|
||||
setButton(15, GuiUtils.createButtonItem(CompatibleMaterial.GREEN_TERRACOTTA,
|
||||
plugin.getLocale().getMessage("interface.button.confirmDestroy").getMessage(),
|
||||
confirmLore.isEmpty() ? new String[0] : new String[] {confirmLore}),
|
||||
event -> this.handler.accept(null, true));
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package com.songoda.epicanchors.listener;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleHand;
|
||||
import com.songoda.core.compatibility.CompatibleParticleHandler;
|
||||
import com.songoda.core.compatibility.CompatibleSound;
|
||||
import com.songoda.epicanchors.Anchor;
|
||||
import com.songoda.epicanchors.AnchorManager;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.files.Settings;
|
||||
import com.songoda.epicanchors.guis.AnchorGui;
|
||||
import com.songoda.epicanchors.guis.DestroyConfirmationGui;
|
||||
import com.songoda.epicanchors.utils.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class AnchorListener implements Listener {
|
||||
private final EpicAnchors plugin;
|
||||
|
||||
public AnchorListener(EpicAnchors instance) {
|
||||
this.plugin = instance;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockPlace(BlockPlaceEvent e) {
|
||||
ItemStack item = e.getItemInHand();
|
||||
|
||||
if (item.hasItemMeta() &&
|
||||
item.getItemMeta().hasDisplayName() &&
|
||||
Settings.MATERIAL.getMaterial().getMaterial() == e.getBlock().getType()) {
|
||||
if (!plugin.getAnchorManager().isReady(e.getBlock().getWorld())) {
|
||||
e.setCancelled(true);
|
||||
e.getPlayer().sendMessage("Anchors are still being initialized - Please wait a moment"); // TODO
|
||||
} else {
|
||||
int ticksLeft = AnchorManager.getTicksFromItem(item);
|
||||
|
||||
if (ticksLeft != 0) {
|
||||
boolean dropOnErr = e.getPlayer().getGameMode() != GameMode.CREATIVE;
|
||||
|
||||
plugin.getAnchorManager().createAnchor(e.getBlock().getLocation(), e.getPlayer().getUniqueId(), ticksLeft,
|
||||
(ex, result) -> {
|
||||
if (ex != null) {
|
||||
Utils.logException(this.plugin, ex, "SQLite");
|
||||
e.getPlayer().sendMessage("Error creating anchor!"); // TODO
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
if (Settings.MATERIAL.getMaterial().getMaterial() == e.getBlock().getType()) {
|
||||
e.getBlock().setType(Material.AIR);
|
||||
}
|
||||
|
||||
if (dropOnErr) {
|
||||
e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(),
|
||||
plugin.getAnchorManager().createAnchorItem(ticksLeft, item.getType()));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockInteract(PlayerInteractEvent e) {
|
||||
if (e.getClickedBlock() == null ||
|
||||
!plugin.getAnchorManager().isReady(e.getClickedBlock().getWorld())) return;
|
||||
|
||||
Player p = e.getPlayer();
|
||||
Anchor anchor = plugin.getAnchorManager().getAnchor(e.getClickedBlock());
|
||||
|
||||
if (anchor != null) {
|
||||
e.setCancelled(true);
|
||||
|
||||
if (p.hasPermission("EpicAnchors.admin") ||
|
||||
this.plugin.getAnchorManager().hasAccess(anchor, p)) {
|
||||
if (e.getAction() == Action.LEFT_CLICK_BLOCK) { // Destroy anchor
|
||||
this.plugin.getGuiManager().showGUI(e.getPlayer(),
|
||||
new DestroyConfirmationGui(this.plugin, anchor, (ex, result) -> {
|
||||
if (result) {
|
||||
BlockBreakEvent blockBreakEvent = new BlockBreakEvent(e.getClickedBlock(), p);
|
||||
Bukkit.getPluginManager().callEvent(blockBreakEvent);
|
||||
|
||||
if (!blockBreakEvent.isCancelled()) {
|
||||
plugin.getAnchorManager().destroyAnchor(anchor);
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else if (e.getAction() == Action.RIGHT_CLICK_BLOCK) { // Manage anchor
|
||||
ItemStack item = CompatibleHand.MAIN_HAND.getItem(e.getPlayer());
|
||||
int itemTicks = AnchorManager.getTicksFromItem(item);
|
||||
|
||||
if (itemTicks != 0) {
|
||||
if (!anchor.isInfinite()) {
|
||||
if (itemTicks == -1) {
|
||||
anchor.setTicksLeft(-1);
|
||||
} else {
|
||||
anchor.addTicksLeft(itemTicks);
|
||||
}
|
||||
|
||||
if (p.getGameMode() != GameMode.CREATIVE) {
|
||||
CompatibleHand.MAIN_HAND.takeItem(p, 1);
|
||||
}
|
||||
|
||||
p.playSound(p.getLocation(), CompatibleSound.ENTITY_PLAYER_LEVELUP.getSound(), .6F, 15);
|
||||
CompatibleParticleHandler.spawnParticles(CompatibleParticleHandler.ParticleType.getParticle(Settings.PARTICLE_UPGRADE.getString()),
|
||||
anchor.getLocation().add(.5, .5, .5), 100, .5, .5, .5);
|
||||
}
|
||||
} else {
|
||||
plugin.getGuiManager().showGUI(p, new AnchorGui(plugin, anchor));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
plugin.getLocale().getMessage("event.general.nopermission").sendMessage(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.songoda.epicanchors.listener;
|
||||
|
||||
import com.songoda.epicanchors.AnchorManager;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBurnEvent;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.bukkit.event.block.BlockPistonExtendEvent;
|
||||
import org.bukkit.event.block.BlockPistonRetractEvent;
|
||||
import org.bukkit.event.block.LeavesDecayEvent;
|
||||
import org.bukkit.event.world.PortalCreateEvent;
|
||||
|
||||
public class BlockListener implements Listener {
|
||||
private final AnchorManager manager;
|
||||
|
||||
public BlockListener(AnchorManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
private void onBlockBurn(BlockBurnEvent e) {
|
||||
if (!this.manager.isReady(e.getBlock().getWorld())) return;
|
||||
|
||||
if (manager.isAnchor(e.getBlock())) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
private void onBlockPiston(BlockPistonExtendEvent e) {
|
||||
if (!this.manager.isReady(e.getBlock().getWorld())) return;
|
||||
|
||||
if (manager.isAnchor(e.getBlock())) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
private void onBlockPiston(BlockPistonRetractEvent e) {
|
||||
if (!this.manager.isReady(e.getBlock().getWorld())) return;
|
||||
|
||||
if (manager.isAnchor(e.getBlock())) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
private void onBlockPhysics(BlockPhysicsEvent e) {
|
||||
if (!this.manager.isReady(e.getBlock().getWorld())) return;
|
||||
|
||||
if (manager.isAnchor(e.getBlock())) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
private void onBlockPiston(LeavesDecayEvent e) {
|
||||
if (!this.manager.isReady(e.getBlock().getWorld())) return;
|
||||
|
||||
if (manager.isAnchor(e.getBlock())) {
|
||||
e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPortalCreation(PortalCreateEvent e) {
|
||||
if (!this.manager.isReady(e.getWorld())) return;
|
||||
|
||||
for (Block b : e.getBlocks()) {
|
||||
if (manager.isAnchor(b)) {
|
||||
e.setCancelled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
package com.songoda.epicanchors.listener;
|
||||
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockFormEvent;
|
||||
import org.bukkit.event.block.BlockFromToEvent;
|
||||
import org.bukkit.event.block.BlockGrowEvent;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.event.entity.SpawnerSpawnEvent;
|
||||
import org.bukkit.event.inventory.FurnaceSmeltEvent;
|
||||
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DebugListener implements Listener {
|
||||
private final EpicAnchors plugin;
|
||||
private final Logger logger;
|
||||
|
||||
public DebugListener(EpicAnchors plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
this.logger = Logger.getLogger(plugin.getName() + "-DEBUG");
|
||||
}
|
||||
|
||||
private void logDebug(String s) {
|
||||
LogRecord logRecord = new LogRecord(Level.INFO, s);
|
||||
logRecord.setMessage("[" + this.logger.getName() + "] " + logRecord.getMessage());
|
||||
|
||||
this.logger.log(logRecord);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onBlockPhysics(BlockPhysicsEvent e) {
|
||||
if (skipEvent(e.getBlock().getChunk())) return;
|
||||
|
||||
logDebug("BlockPhysicsEvent (" + e.getBlock().getType() + ")");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onBlockForm(BlockFormEvent e) {
|
||||
if (skipEvent(e.getBlock().getChunk())) return;
|
||||
|
||||
logDebug("BlockFormEvent (" + e.getBlock().getType() + " -> " + e.getNewState().getType() + ")");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onBlockFromTo(BlockFromToEvent e) {
|
||||
if (skipEvent(e.getBlock().getChunk())) return;
|
||||
|
||||
logDebug("BlockFromToEvent");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onFurnace(FurnaceSmeltEvent e) {
|
||||
if (skipEvent(e.getBlock().getChunk())) return;
|
||||
|
||||
logDebug("FurnaceSmeltEvent");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onSneak(PlayerToggleSneakEvent e) {
|
||||
Chunk chunk = e.getPlayer().getLocation().getChunk();
|
||||
|
||||
if (e.getPlayer().isFlying() || skipEvent(chunk)) return;
|
||||
|
||||
Map<String, Integer> count = new HashMap<>();
|
||||
|
||||
if (e.isSneaking()) {
|
||||
e.getPlayer().sendMessage("§e§lEntities");
|
||||
for (Entity entity : chunk.getEntities()) {
|
||||
count.compute(entity.getType().name(), (k, v) -> v == null ? 1 : v + 1);
|
||||
}
|
||||
} else {
|
||||
e.getPlayer().sendMessage("§e§lTileEntities");
|
||||
for (BlockState blockState : chunk.getTileEntities()) {
|
||||
count.compute(blockState.getType().name(), (k, v) -> v == null ? 1 : v + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Integer> m = count.entrySet().stream()
|
||||
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
|
||||
(e1, e2) -> e1, LinkedHashMap::new));
|
||||
|
||||
for (Map.Entry<String, Integer> entry : m.entrySet()) {
|
||||
String entityName = WordUtils.capitalize(entry.getKey().toLowerCase(), new char[] {'_'}).replace("_", "");
|
||||
|
||||
e.getPlayer().sendMessage("§a" + entityName + "§7:§r " + entry.getValue());
|
||||
}
|
||||
|
||||
e.getPlayer().sendMessage("");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onSpawner(SpawnerSpawnEvent e) {
|
||||
if (skipEvent(e.getSpawner().getBlock().getChunk())) return;
|
||||
|
||||
logDebug("SpawnerSpawnEvent (" + e.getEntity().getType().name() + ")");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onCreatureSpawn(CreatureSpawnEvent e) {
|
||||
if (skipEvent(e.getLocation().getChunk())) return;
|
||||
|
||||
logDebug("CreatureSpawnEvent (" + e.getEntity().getType().name() + ")");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onBlockGrow(BlockGrowEvent e) {
|
||||
if (skipEvent(e.getBlock().getChunk())) return;
|
||||
|
||||
logDebug("BlockGrowEvent (" + e.getBlock().getType() + ")");
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onInvItemMove(InventoryMoveItemEvent e) {
|
||||
if (e.getSource().getHolder() == null) return;
|
||||
|
||||
Location loc = null;
|
||||
|
||||
try {
|
||||
loc = (Location) e.getSource().getHolder().getClass().getDeclaredMethod("getLocation").invoke(e.getSource().getHolder());
|
||||
} catch (Exception ex) {
|
||||
try {
|
||||
loc = (Location) e.getSource().getClass().getDeclaredMethod("getLocation").invoke(e.getSource());
|
||||
} catch (Exception ex2) {
|
||||
logDebug("InventoryMoveItemEvent (Potentially in a chunk without Anchor [Not supported at current server version])");
|
||||
}
|
||||
}
|
||||
|
||||
if (loc == null || skipEvent(loc.getChunk())) return;
|
||||
|
||||
logDebug("InventoryMoveItemEvent (" + e.getSource().getType() + " -> " + e.getDestination().getType() + ")");
|
||||
}
|
||||
|
||||
private boolean skipEvent(Chunk chunk) {
|
||||
return !this.plugin.getAnchorManager().isReady(chunk.getWorld()) || !this.plugin.getAnchorManager().hasAnchor(chunk);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.songoda.epicanchors.listener;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class WorldListener implements Listener {
|
||||
private final Consumer<World> initAnchorsInWorld;
|
||||
private final Consumer<World> deInitAnchorsInWorld;
|
||||
|
||||
public WorldListener(Consumer<World> initAnchorsInWorld, Consumer<World> deInitAnchorsInWorld) {
|
||||
this.initAnchorsInWorld = initAnchorsInWorld;
|
||||
this.deInitAnchorsInWorld = deInitAnchorsInWorld;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
private void onWorldLoad(WorldLoadEvent e) {
|
||||
initAnchorsInWorld.accept(e.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onWorldUnload(WorldUnloadEvent e) {
|
||||
deInitAnchorsInWorld.accept(e.getWorld());
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package com.songoda.epicanchors.listeners;
|
||||
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.anchor.Anchor;
|
||||
import com.songoda.epicanchors.settings.Settings;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class BlockListeners implements Listener {
|
||||
|
||||
private EpicAnchors plugin;
|
||||
|
||||
public BlockListeners(EpicAnchors instance) {
|
||||
this.plugin = instance;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockPlace(BlockPlaceEvent event) {
|
||||
|
||||
ItemStack item = event.getItemInHand();
|
||||
|
||||
if (!item.hasItemMeta()
|
||||
|| !item.getItemMeta().hasDisplayName()
|
||||
|| Settings.MATERIAL.getMaterial().getMaterial() != event.getBlock().getType()
|
||||
|| plugin.getTicksFromItem(item) == 0) return;
|
||||
|
||||
Anchor anchor = new Anchor(event.getBlock().getLocation(), plugin.getTicksFromItem(item));
|
||||
|
||||
if (plugin.getTicksFromItem(item) == -99) {
|
||||
anchor.setInfinite(true);
|
||||
}
|
||||
|
||||
plugin.getAnchorManager().addAnchor(event.getBlock().getLocation(), anchor);
|
||||
plugin.updateHologram(anchor);
|
||||
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package com.songoda.epicanchors.listeners;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleHand;
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.compatibility.CompatibleParticleHandler;
|
||||
import com.songoda.core.compatibility.CompatibleSound;
|
||||
import com.songoda.core.utils.ItemUtils;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.anchor.Anchor;
|
||||
import com.songoda.epicanchors.gui.GUIOverview;
|
||||
import com.songoda.epicanchors.settings.Settings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class InteractListeners implements Listener {
|
||||
|
||||
private final EpicAnchors instance;
|
||||
|
||||
public InteractListeners(EpicAnchors instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockInteract(PlayerInteractEvent event) {
|
||||
if (event.getClickedBlock() == null) return;
|
||||
|
||||
Anchor anchor = instance.getAnchorManager().getAnchor(event.getClickedBlock().getLocation());
|
||||
|
||||
if (anchor == null) return;
|
||||
event.setCancelled(true);
|
||||
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
|
||||
BlockBreakEvent blockBreakEvent = new BlockBreakEvent(event.getClickedBlock(), player);
|
||||
Bukkit.getPluginManager().callEvent(blockBreakEvent);
|
||||
if (blockBreakEvent.isCancelled())
|
||||
return;
|
||||
|
||||
anchor.bust();
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack item = player.getItemInHand();
|
||||
|
||||
if (Settings.MATERIAL.getMaterial().matches(item)) {
|
||||
if (instance.getTicksFromItem(item) == 0) return;
|
||||
|
||||
anchor.setTicksLeft(anchor.getTicksLeft() + instance.getTicksFromItem(item));
|
||||
|
||||
if (player.getGameMode() != GameMode.CREATIVE)
|
||||
ItemUtils.takeActiveItem(player, CompatibleHand.getHand(event));
|
||||
|
||||
player.playSound(player.getLocation(), CompatibleSound.ENTITY_PLAYER_LEVELUP.getSound(), 0.6F, 15.0F);
|
||||
|
||||
CompatibleParticleHandler.spawnParticles(CompatibleParticleHandler.ParticleType.SPELL_WITCH, anchor.getLocation().add(.5, .5, .5), 100, .5, .5, .5);
|
||||
|
||||
} else {
|
||||
instance.getGuiManager().showGUI(player, new GUIOverview(EpicAnchors.getInstance(), anchor, player));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.songoda.epicanchors.listeners;
|
||||
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.PortalCreateEvent;
|
||||
|
||||
public class PortalListeners implements Listener {
|
||||
|
||||
private EpicAnchors plugin;
|
||||
|
||||
public PortalListeners(EpicAnchors instance) {
|
||||
this.plugin = instance;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPortalCreation(PortalCreateEvent e) {
|
||||
if (e.getBlocks().size() < 1) return;
|
||||
if (plugin.getAnchorManager().isAnchor(e.getBlocks().get(0).getLocation())) e.setCancelled(true);
|
||||
}
|
||||
}
|
@ -1,145 +1,122 @@
|
||||
package com.songoda.epicanchors.tasks;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||
import com.songoda.core.compatibility.CompatibleParticleHandler;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import com.songoda.core.nms.NmsManager;
|
||||
import com.songoda.epicanchors.Anchor;
|
||||
import com.songoda.epicanchors.AnchorManager;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.anchor.Anchor;
|
||||
import com.songoda.epicanchors.settings.Settings;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import com.songoda.epicanchors.utils.Utils;
|
||||
import com.songoda.epicanchors.utils.WorldUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* More information about what types of game ticks there are and what does what: https://minecraft.fandom.com/wiki/Tick
|
||||
*/
|
||||
public class AnchorTask extends BukkitRunnable {
|
||||
private static final int TASK_INTERVAL = 3;
|
||||
|
||||
private static EpicAnchors plugin;
|
||||
private final EpicAnchors plugin;
|
||||
private final AnchorManager anchorManager;
|
||||
|
||||
private Map<Location, Integer> delays = new HashMap<>();
|
||||
private boolean randomTicksFailed;
|
||||
private boolean spawnerTicksFailed;
|
||||
|
||||
private Class<?> clazzEntity, clazzCraftEntity, clazzMinecraftServer;
|
||||
|
||||
private Method methodTick, methodGetHandle;
|
||||
|
||||
private Field fieldCurrentTick, fieldActivatedTick;
|
||||
|
||||
private boolean epicSpawners;
|
||||
|
||||
public AnchorTask(EpicAnchors plug) {
|
||||
plugin = plug;
|
||||
epicSpawners = Bukkit.getPluginManager().getPlugin("EpicSpawners") != null;
|
||||
|
||||
try {
|
||||
String ver = Bukkit.getServer().getClass().getPackage().getName().substring(23);
|
||||
clazzMinecraftServer = Class.forName("net.minecraft.server." + ver + ".MinecraftServer");
|
||||
clazzEntity = Class.forName("net.minecraft.server." + ver + ".Entity");
|
||||
clazzCraftEntity = Class.forName("org.bukkit.craftbukkit." + ver + ".entity.CraftEntity");
|
||||
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13))
|
||||
methodTick = clazzEntity.getDeclaredMethod("tick");
|
||||
else if (ServerVersion.isServerVersion(ServerVersion.V1_12))
|
||||
methodTick = clazzEntity.getDeclaredMethod("B_");
|
||||
else if (ServerVersion.isServerVersion(ServerVersion.V1_11))
|
||||
methodTick = clazzEntity.getDeclaredMethod("A_");
|
||||
else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9))
|
||||
methodTick = clazzEntity.getDeclaredMethod("m");
|
||||
else
|
||||
methodTick = clazzEntity.getDeclaredMethod("t_");
|
||||
|
||||
methodGetHandle = clazzCraftEntity.getDeclaredMethod("getHandle");
|
||||
|
||||
fieldCurrentTick = clazzMinecraftServer.getDeclaredField("currentTick");
|
||||
fieldActivatedTick = clazzEntity.getDeclaredField("activatedTick");
|
||||
|
||||
} catch (ReflectiveOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.runTaskTimer(plugin, 0, 3);
|
||||
public AnchorTask(EpicAnchors plugin) {
|
||||
this.plugin = plugin;
|
||||
this.anchorManager = plugin.getAnchorManager();
|
||||
}
|
||||
|
||||
private void doParticle() {
|
||||
for (Anchor anchor : plugin.getAnchorManager().getAnchors().values()) {
|
||||
Location location1 = anchor.getLocation().add(.5, .5, .5);
|
||||
if (location1.getWorld() == null) continue;
|
||||
CompatibleParticleHandler.redstoneParticles(location1, 255, 255, 255, 1.2F, 5, .75F);
|
||||
}
|
||||
public void startTask() {
|
||||
runTaskTimer(this.plugin, TASK_INTERVAL, TASK_INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
doParticle();
|
||||
for (Anchor anchor : new ArrayList<>(plugin.getAnchorManager().getAnchors().values())) {
|
||||
try {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
if (!this.anchorManager.isReady(world)) return;
|
||||
|
||||
if (anchor.getLocation() == null) continue;
|
||||
int randomTicks = WorldUtils.getRandomTickSpeed(world) * TASK_INTERVAL;
|
||||
|
||||
plugin.updateHologram(anchor);
|
||||
Set<Chunk> alreadyTicked = new HashSet<>();
|
||||
Anchor[] anchorsInWorld = this.anchorManager.getAnchors(world);
|
||||
List<Anchor> toUpdateHolo = new ArrayList<>(anchorsInWorld.length);
|
||||
|
||||
Location location = anchor.getLocation();
|
||||
if (CompatibleMaterial.getMaterial(location.getBlock()) != Settings.MATERIAL.getMaterial())
|
||||
continue;
|
||||
|
||||
Chunk chunk = location.getChunk();
|
||||
chunk.load();
|
||||
|
||||
// Load entities
|
||||
for (Entity entity : chunk.getEntities()) {
|
||||
if (!(entity instanceof LivingEntity) || entity instanceof Player) continue;
|
||||
|
||||
if (entity.getNearbyEntities(32, 32, 32).stream().anyMatch(entity1 -> entity1 instanceof Player)) {
|
||||
continue;
|
||||
// Skip all chunks with players in them
|
||||
for (Player pInWorld : world.getPlayers()) {
|
||||
alreadyTicked.add(pInWorld.getLocation().getChunk());
|
||||
}
|
||||
|
||||
try {
|
||||
Object objCraftEntity = clazzCraftEntity.cast(entity);
|
||||
Object objEntity = methodGetHandle.invoke(objCraftEntity);
|
||||
for (Anchor anchor : anchorsInWorld) {
|
||||
Chunk chunk = anchor.getChunk();
|
||||
|
||||
fieldActivatedTick.set(objEntity, fieldCurrentTick.getLong(objEntity));
|
||||
methodTick.invoke(objEntity);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
e.printStackTrace();
|
||||
// Tick the anchor's chunk (but not multiple times)
|
||||
if (alreadyTicked.add(chunk)) {
|
||||
// Having a chunk loaded takes care of entities and weather (https://minecraft.fandom.com/wiki/Tick#Chunk_tick)
|
||||
if (!chunk.isLoaded()) {
|
||||
// Loading an already loaded chunk still fires the ChunkLoadEvent and might have a huge
|
||||
// impact on performance if other plugins do not expect that either...
|
||||
|
||||
WorldUtils.loadAnchoredChunk(chunk, this.plugin);
|
||||
}
|
||||
|
||||
if (!randomTicksFailed) {
|
||||
try {
|
||||
NmsManager.getWorld().randomTickChunk(chunk, randomTicks);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
this.plugin.getLogger().log(Level.FINER, ex,
|
||||
() -> "Failed to do random ticks on this server implementation(/version) - " +
|
||||
"Skipping further random ticks.");
|
||||
|
||||
randomTicksFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!spawnerTicksFailed) {
|
||||
try {
|
||||
NmsManager.getWorld().tickInactiveSpawners(chunk, TASK_INTERVAL);
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
this.plugin.getLogger().log(Level.FINER, ex,
|
||||
() -> "Failed to do spawner ticks on this server implementation(/version) - " +
|
||||
"Skipping further spawner ticks.");
|
||||
|
||||
spawnerTicksFailed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Only update hologram if a player is nearby
|
||||
// Simplify player location to chunks to potentially group players
|
||||
// Use the server view distance to calculate minimum distance to count as not-nearby
|
||||
|
||||
// Destroy anchors and queue hologram update
|
||||
if (!anchor.isInfinite()) {
|
||||
int ticksLeft = anchor.removeTicksLeft(3);
|
||||
|
||||
if (ticksLeft == 0) {
|
||||
this.anchorManager.destroyAnchor(anchor);
|
||||
} else {
|
||||
toUpdateHolo.add(anchor);
|
||||
}
|
||||
} else {
|
||||
toUpdateHolo.add(anchor);
|
||||
}
|
||||
}
|
||||
|
||||
// Update holograms on queued anchors
|
||||
anchorManager.updateHolograms(toUpdateHolo);
|
||||
}
|
||||
|
||||
int ticksLeft = anchor.getTicksLeft();
|
||||
|
||||
if (!anchor.isInfinite()) {
|
||||
anchor.setTicksLeft(ticksLeft - 3);
|
||||
}
|
||||
|
||||
if (ticksLeft <= 0 && !anchor.isInfinite()) {
|
||||
anchor.bust();
|
||||
chunk.unload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!epicSpawners || com.songoda.epicspawners.EpicSpawners.getInstance().getSpawnerManager() == null) continue;
|
||||
|
||||
com.songoda.epicspawners.EpicSpawners.getInstance().getSpawnerManager().getSpawners().stream()
|
||||
.filter(spawner -> spawner.getWorld().isChunkLoaded(spawner.getX() >> 4, spawner.getZ() >> 4)
|
||||
&& chunk == spawner.getLocation().getChunk()).forEach(spawner -> {
|
||||
Block block = spawner.getLocation().getBlock();
|
||||
|
||||
if (!delays.containsKey(block.getLocation())) {
|
||||
delays.put(block.getLocation(), spawner.updateDelay());
|
||||
return;
|
||||
}
|
||||
int delay = delays.get(block.getLocation());
|
||||
delay -= 1;
|
||||
delays.put(block.getLocation(), delay);
|
||||
if (delay <= 0) {
|
||||
spawner.spawn();
|
||||
delays.remove(block.getLocation());
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
Utils.logException(this.plugin, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,102 +1,111 @@
|
||||
package com.songoda.epicanchors.tasks;
|
||||
|
||||
import com.songoda.core.compatibility.CompatibleParticleHandler;
|
||||
import com.songoda.epicanchors.Anchor;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.anchor.Anchor;
|
||||
import com.songoda.epicanchors.anchor.AnchorManager;
|
||||
import org.bukkit.*;
|
||||
import com.songoda.epicanchors.files.Settings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Set;
|
||||
|
||||
public class VisualizeTask extends BukkitRunnable {
|
||||
private static final int TASK_INTERVAL = 30;
|
||||
|
||||
private static VisualizeTask instance;
|
||||
private static EpicAnchors plugin;
|
||||
private final static Map<OfflinePlayer, Boolean> active = new ConcurrentHashMap();
|
||||
private final static Random random = new Random();
|
||||
int radius;
|
||||
private final EpicAnchors plugin;
|
||||
|
||||
public VisualizeTask(EpicAnchors plug) {
|
||||
plugin = plug;
|
||||
radius = Bukkit.getServer().getViewDistance();
|
||||
if (instance == null) {
|
||||
instance = this;
|
||||
instance.runTaskTimerAsynchronously(plugin, 60, 10);
|
||||
}
|
||||
private final int radius = Bukkit.getServer().getViewDistance();
|
||||
|
||||
public VisualizeTask(EpicAnchors plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static boolean togglePlayer(Player p) {
|
||||
Boolean isActive = active.get(p);
|
||||
active.put(p, isActive = (isActive == null || !isActive));
|
||||
return isActive;
|
||||
}
|
||||
|
||||
public static void removePlayer(Player p) {
|
||||
active.remove(p);
|
||||
public void startTask() {
|
||||
runTaskTimer(plugin, TASK_INTERVAL, TASK_INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
active.entrySet().stream()
|
||||
.filter(e -> e.getValue() && e.getKey().isOnline())
|
||||
.forEach(e -> particleTick((Player) e.getKey()));
|
||||
}
|
||||
HashMap<Chunk, Set<Player>> chunksToVisualize = new HashMap<>();
|
||||
Set<Chunk> loadedChunks = new HashSet<>();
|
||||
|
||||
void particleTick(Player player) {
|
||||
final AnchorManager anchorManager = plugin.getAnchorManager();
|
||||
final Location playerLocation = player.getLocation();
|
||||
final World world = playerLocation.getWorld();
|
||||
// start and stop chunk coordinates
|
||||
int startY = playerLocation.getBlockY() + 1;
|
||||
int cxi = (playerLocation.getBlockX() >> 4) - radius, cxn = cxi + radius * 2;
|
||||
int czi = (playerLocation.getBlockZ() >> 4) - radius, czn = czi + radius * 2;
|
||||
// loop through the chunks to find applicable ones
|
||||
for (int cx = cxi; cx < cxn; cx++) {
|
||||
for (int cz = czi; cz < czn; cz++) {
|
||||
// sanity check
|
||||
if (!world.isChunkLoaded(cx, cz))
|
||||
continue;
|
||||
CompatibleParticleHandler.ParticleType particleType = CompatibleParticleHandler.ParticleType.getParticle(Settings.PARTICLE_VISUALIZER.getString());
|
||||
|
||||
// so! Is this a claimed chunk?
|
||||
Anchor anchor = anchorManager.getAnchor(world.getName(), cx, cz);
|
||||
if (anchor != null) {
|
||||
// we found one!
|
||||
// now we get to spawn the silly particles for the player
|
||||
showChunkParticles(player, world.getChunkAt(cx, cz), startY);
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
if (!this.plugin.getAnchorManager().isReady(world)) continue;
|
||||
|
||||
loadedChunks.clear();
|
||||
for (Anchor anchor : this.plugin.getAnchorManager().getAnchors(world)) {
|
||||
loadedChunks.add(anchor.getChunk());
|
||||
}
|
||||
|
||||
if (!loadedChunks.isEmpty()) {
|
||||
for (Player p : world.getPlayers()) {
|
||||
if (!this.plugin.getAnchorManager().hasChunksVisualized(p)) continue;
|
||||
|
||||
Location pLoc = p.getLocation();
|
||||
|
||||
// start and stop chunk coordinates
|
||||
int cxi = (pLoc.getBlockX() >> 4) - radius;
|
||||
int cxn = cxi + radius * 2;
|
||||
int czi = (pLoc.getBlockZ() >> 4) - radius;
|
||||
int czn = czi + radius * 2;
|
||||
|
||||
// loop through the chunks to find applicable ones
|
||||
for (int cx = cxi; cx < cxn; ++cx) {
|
||||
for (int cz = czi; cz < czn; ++cz) {
|
||||
Chunk chunk = world.getChunkAt(cx, cz);
|
||||
|
||||
if (loadedChunks.contains(chunk)) {
|
||||
chunksToVisualize.computeIfAbsent(chunk, k -> new HashSet<>())
|
||||
.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showChunkParticles(Player player, Chunk c, int startY) {
|
||||
// loop through the chunk
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
// show about 1/5 of the blocks per tick
|
||||
boolean show = random.nextFloat() < .2;
|
||||
if (!show)
|
||||
continue;
|
||||
for (Map.Entry<Chunk, Set<Player>> entry : chunksToVisualize.entrySet()) {
|
||||
int maxY = entry.getKey().getWorld().getMaxHeight();
|
||||
|
||||
// Exclude everything over max height
|
||||
if (startY >= c.getWorld().getMaxHeight()) continue;
|
||||
for (Player p : entry.getValue()) {
|
||||
int startY = p.getLocation().getBlockY() + 2;
|
||||
|
||||
// only show if there is a space to show above a solid block
|
||||
Block b = c.getBlock(x, startY, z);
|
||||
int maxDown = 8;
|
||||
do {
|
||||
show = b.getType().isTransparent() && !(b = b.getRelative(BlockFace.DOWN)).getType().isTransparent();
|
||||
} while (--maxDown > 0 && !show);
|
||||
if (startY <= 0) continue;
|
||||
|
||||
// can we do this?
|
||||
if (show) {
|
||||
final Location loc = b.getLocation().add(.5, 1.5, .5);
|
||||
// loop through the chunk
|
||||
for (int x = 0; x < 16; ++x) {
|
||||
for (int z = 0; z < 16; ++z) {
|
||||
if (Math.random() < .125) { // Don't spawn particles on each block
|
||||
if (startY >= maxY) {
|
||||
startY = maxY - 1;
|
||||
}
|
||||
|
||||
player.spawnParticle(Particle.VILLAGER_HAPPY, loc, 0, 0, 0, 0, 1);
|
||||
Block b = entry.getKey().getBlock(x, startY, z);
|
||||
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
if (b.getType().isSolid()) break;
|
||||
|
||||
b = b.getRelative(BlockFace.DOWN);
|
||||
}
|
||||
|
||||
if (!b.isEmpty() && !b.getRelative(BlockFace.UP).getType().isOccluding()) {
|
||||
CompatibleParticleHandler.spawnParticles(particleType,
|
||||
b.getLocation().add(.5, 1.5, .5),
|
||||
0, 0, 0, 0, 1, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.songoda.epicanchors.utils;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Callback<T> {
|
||||
void accept(@Nullable Exception ex, T result);
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package com.songoda.epicanchors.utils;
|
||||
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import com.songoda.core.utils.TimeUtils;
|
||||
import com.songoda.epicanchors.EpicAnchors;
|
||||
import com.songoda.epicanchors.settings.Settings;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Methods {
|
||||
|
||||
private static Map<String, Location> serializeCache = new HashMap<>();
|
||||
|
||||
public static String formatName(int ticks) {
|
||||
|
||||
String remaining = TimeUtils.makeReadable((ticks / 20L) * 1000L);
|
||||
|
||||
String name = Settings.NAMETAG.getString().replace("{REMAINING}", (ticks <= 0)
|
||||
? EpicAnchors.getInstance().getLocale().getMessage("infinite").getMessage() : remaining);
|
||||
|
||||
|
||||
return TextUtils.formatText(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the location specified.
|
||||
*
|
||||
* @param location The location that is to be saved.
|
||||
* @return The serialized data.
|
||||
*/
|
||||
public static String serializeLocation(Location location) {
|
||||
if (location == null)
|
||||
return "";
|
||||
String w = location.getWorld().getName();
|
||||
double x = location.getX();
|
||||
double y = location.getY();
|
||||
double z = location.getZ();
|
||||
String str = w + ":" + x + ":" + y + ":" + z;
|
||||
str = str.replace(".0", "").replace("/", "");
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a location from the string.
|
||||
*
|
||||
* @param str The string to parse.
|
||||
* @return The location that was serialized in the string.
|
||||
*/
|
||||
public static Location unserializeLocation(String str) {
|
||||
if (str == null || str.equals(""))
|
||||
return null;
|
||||
if (serializeCache.containsKey(str)) {
|
||||
return serializeCache.get(str).clone();
|
||||
}
|
||||
String cacheKey = str;
|
||||
str = str.replace("y:", ":").replace("z:", ":").replace("w:", "").replace("x:", ":").replace("/", ".");
|
||||
List<String> args = Arrays.asList(str.split("\\s*:\\s*"));
|
||||
|
||||
World world = Bukkit.getWorld(args.get(0));
|
||||
double x = Double.parseDouble(args.get(1)), y = Double.parseDouble(args.get(2)), z = Double.parseDouble(args.get(3));
|
||||
Location location = new Location(world, x, y, z, 0, 0);
|
||||
serializeCache.put(cacheKey, location.clone());
|
||||
return location;
|
||||
}
|
||||
|
||||
public static boolean isInt(String number) {
|
||||
if (number != null && !number.equals("")) {
|
||||
try {
|
||||
Integer.parseInt(number);
|
||||
return true;
|
||||
} catch (NumberFormatException var2) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.songoda.epicanchors.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class ReflectionUtils {
|
||||
private ReflectionUtils() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static Object getFieldValue(Object instance, String fieldName) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field f = getField(instance, fieldName);
|
||||
boolean accessible = f.isAccessible();
|
||||
|
||||
f.setAccessible(true);
|
||||
|
||||
Object result = f.get(instance);
|
||||
|
||||
f.setAccessible(accessible);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void setFieldValue(Object instance, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field f = getField(instance, fieldName);
|
||||
boolean accessible = f.isAccessible();
|
||||
|
||||
f.setAccessible(true);
|
||||
|
||||
f.set(instance, value);
|
||||
|
||||
f.setAccessible(accessible);
|
||||
}
|
||||
|
||||
private static Field getField(Object instance, String fieldName) throws NoSuchFieldException {
|
||||
Field f = null;
|
||||
|
||||
Class<?> currClass = instance.getClass();
|
||||
do {
|
||||
try {
|
||||
f = currClass.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
currClass = currClass.getSuperclass();
|
||||
|
||||
if (currClass == null) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} while (f == null);
|
||||
|
||||
return f;
|
||||
}
|
||||
}
|
30
src/main/java/com/songoda/epicanchors/utils/ThreadSync.java
Normal file
30
src/main/java/com/songoda/epicanchors/utils/ThreadSync.java
Normal file
@ -0,0 +1,30 @@
|
||||
package com.songoda.epicanchors.utils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ThreadSync {
|
||||
private final Object syncObj = new Object();
|
||||
private final AtomicReference<Boolean> waiting = new AtomicReference<>(true);
|
||||
|
||||
public void waitForRelease() {
|
||||
synchronized (syncObj) {
|
||||
while (waiting.get()) {
|
||||
try {
|
||||
syncObj.wait();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
synchronized (syncObj) {
|
||||
waiting.set(false);
|
||||
syncObj.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
waiting.set(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.songoda.epicanchors.utils;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface UpdateCallback {
|
||||
void accept(@Nullable Exception ex);
|
||||
}
|
60
src/main/java/com/songoda/epicanchors/utils/Utils.java
Normal file
60
src/main/java/com/songoda/epicanchors/utils/Utils.java
Normal file
@ -0,0 +1,60 @@
|
||||
package com.songoda.epicanchors.utils;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Utils {
|
||||
private Utils() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static boolean isInt(String s) {
|
||||
if (s != null && !s.isEmpty()) {
|
||||
try {
|
||||
Integer.parseInt(s);
|
||||
return true;
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all values of a String[] which start with a given String
|
||||
*
|
||||
* @param value The String to search for
|
||||
* @param list The array to iterate
|
||||
*
|
||||
* @return A list with all the matches
|
||||
*/
|
||||
public static List<String> getMatches(String value, Collection<String> list, boolean caseInsensitive) {
|
||||
List<String> result = new LinkedList<>();
|
||||
|
||||
for (String str : list) {
|
||||
if (str.startsWith(value.toLowerCase())
|
||||
|| (caseInsensitive && str.toLowerCase().startsWith(value.toLowerCase()))) {
|
||||
result.add(str);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void logException(@Nullable Plugin plugin, @NotNull Throwable th) {
|
||||
logException(plugin, th, null);
|
||||
}
|
||||
|
||||
public static void logException(@Nullable Plugin plugin, @NotNull Throwable th, @Nullable String type) {
|
||||
Logger logger = plugin != null ? plugin.getLogger() : Logger.getGlobal();
|
||||
|
||||
logger.log(Level.FINER, th, () -> "A " + (type == null ? "critical" : type) + " error occurred");
|
||||
}
|
||||
}
|
66
src/main/java/com/songoda/epicanchors/utils/WorldUtils.java
Normal file
66
src/main/java/com/songoda/epicanchors/utils/WorldUtils.java
Normal file
@ -0,0 +1,66 @@
|
||||
package com.songoda.epicanchors.utils;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@SuppressWarnings("JavaReflectionMemberAccess")
|
||||
public class WorldUtils {
|
||||
private WorldUtils() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
private static final Method addChunkTicketMethod;
|
||||
private static final Method removeChunkTicketMethod;
|
||||
|
||||
static {
|
||||
Method tmpAdd;
|
||||
Method tmpRem;
|
||||
|
||||
try {
|
||||
tmpAdd = Chunk.class.getDeclaredMethod("addPluginChunkTicket", Plugin.class);
|
||||
tmpRem = Chunk.class.getDeclaredMethod("removePluginChunkTicket", Plugin.class);
|
||||
} catch (NoSuchMethodException ignore) {
|
||||
tmpAdd = null;
|
||||
tmpRem = null;
|
||||
}
|
||||
|
||||
addChunkTicketMethod = tmpAdd;
|
||||
removeChunkTicketMethod = tmpRem;
|
||||
}
|
||||
|
||||
public static int getRandomTickSpeed(World world) {
|
||||
try {
|
||||
return Integer.parseInt(world.getGameRuleValue("randomTickSpeed"));
|
||||
} catch (NumberFormatException ignore) {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean loadAnchoredChunk(Chunk chunk, Plugin plugin) {
|
||||
if (addChunkTicketMethod != null) {
|
||||
try {
|
||||
return (boolean) addChunkTicketMethod.invoke(chunk, plugin);
|
||||
} catch (IllegalAccessException | InvocationTargetException ex) {
|
||||
Utils.logException(plugin, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return chunk.load();
|
||||
}
|
||||
|
||||
public static boolean unloadAnchoredChunk(Chunk chunk, Plugin plugin) {
|
||||
if (removeChunkTicketMethod != null) {
|
||||
try {
|
||||
removeChunkTicketMethod.invoke(chunk, plugin);
|
||||
} catch (IllegalAccessException | InvocationTargetException ex) {
|
||||
Utils.logException(plugin, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return chunk.unload();
|
||||
}
|
||||
}
|
@ -14,9 +14,16 @@ interface:
|
||||
addtimewithxplore: '&7Cost: &a%cost% Levels'
|
||||
addtimewitheconomy: '&aAdd 30 Minutes with ECO'
|
||||
addtimewitheconomylore: '&7Cost: &a$%cost%'
|
||||
|
||||
cancelDestroy: '&4Cancel'
|
||||
cancelDestroyLore: ''
|
||||
confirmDestroy: '&2Confirm'
|
||||
confirmDestroyLore: '&aA new anchor with the remaining time will be dropped.'
|
||||
confirmDestroyLoreNoDrops: '&4The remaining time on this anchor will be lost!'
|
||||
|
||||
infinite: '&cCannot upgrade an infinite anchor!'
|
||||
anchor:
|
||||
title: ChunkAnchor
|
||||
title: 'ChunkAnchor'
|
||||
smalltitle: '&eChunkAnchor'
|
||||
|
||||
# Command Messages
|
||||
|
@ -1,13 +1,29 @@
|
||||
name: EpicAnchors
|
||||
description: EpicAnchors
|
||||
version: maven-version-number
|
||||
softdepend: [EpicSpawners, Towny, RedProtect, Kingdoms, PlotsSquared, GriefPrevention, USkyBlock, ASkyBlock, WorldGuard, Factions, Vault, HolographicDisplays, Holograms, CMI]
|
||||
main: com.songoda.epicanchors.EpicAnchors
|
||||
author: songoda
|
||||
name: ${project.name}
|
||||
description: ${project.description}
|
||||
version: ${project.version}
|
||||
api-version: 1.13
|
||||
|
||||
main: com.songoda.epicanchors.EpicAnchors
|
||||
softdepend:
|
||||
- Holograms
|
||||
- HolographicDisplays
|
||||
- Vault
|
||||
|
||||
author: Songoda
|
||||
authors: [ SpraxDev ]
|
||||
website: ${project.url}
|
||||
|
||||
commands:
|
||||
EpicAnchors:
|
||||
description: I have no idea.
|
||||
aliases:
|
||||
- ea
|
||||
|
||||
permissions:
|
||||
EpicAnchors.cmd.show:
|
||||
default: true
|
||||
aliases: [ea]
|
||||
usage: /<command> [reload]
|
||||
|
||||
EpicAnchors.admin:
|
||||
children:
|
||||
EpicAnchors.cmd.reload: true
|
||||
EpicAnchors.cmd.settings: true
|
||||
EpicAnchors.cmd.give: true
|
||||
|
Loading…
Reference in New Issue
Block a user