Showing preview only (1,274K chars total). Download the full file or copy to clipboard to get everything.
Repository: kernitus/BukkitOldCombatMechanics
Branch: master
Commit: 7edb0a525dc4
Files: 129
Total size: 1.2 MB
Directory structure:
gitextract_5hk29j6x/
├── .github/
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-bug-report.yaml
│ │ ├── 2-question.yaml
│ │ ├── 3-feature.yaml
│ │ └── config.yml
│ ├── release-please-config.json
│ ├── release-please-manifest.json
│ └── workflows/
│ ├── build-upload-release.yml
│ ├── dev-builds.yml
│ ├── release-please.yml
│ └── wrap-issue-form-codeblocks.yml
├── .gitignore
├── AGENTS.md
├── CHANGELOG.md
├── LICENCE
├── README.md
├── build.gradle.kts
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src/
├── integrationTest/
│ ├── kotlin/
│ │ └── kernitus/
│ │ └── plugin/
│ │ └── OldCombatMechanics/
│ │ ├── AttackCompat.kt
│ │ ├── AttackCooldownHeldItemIntegrationTest.kt
│ │ ├── AttackCooldownTrackerIntegrationTest.kt
│ │ ├── AttackRangeIntegrationTest.kt
│ │ ├── AttributeModifierCompat.kt
│ │ ├── ChorusFruitIntegrationTest.kt
│ │ ├── ConfigMigrationIntegrationTest.kt
│ │ ├── ConsumableComponentIntegrationTest.kt
│ │ ├── CopperToolsIntegrationTest.kt
│ │ ├── CustomWeaponDamageIntegrationTest.kt
│ │ ├── DisableOffhandIntegrationTest.kt
│ │ ├── DisableOffhandReflectionIntegrationTest.kt
│ │ ├── EnderpearlCooldownIntegrationTest.kt
│ │ ├── FakePlayer.kt
│ │ ├── FireAspectOverdamageIntegrationTest.kt
│ │ ├── FishingRodVelocityIntegrationTest.kt
│ │ ├── GoldenAppleIntegrationTest.kt
│ │ ├── InGameTester.kt
│ │ ├── InGameTesterIntegrationTest.kt
│ │ ├── InvulnerabilityDamageIntegrationTest.kt
│ │ ├── KotestRunner.kt
│ │ ├── LegacyFakePlayer12.kt
│ │ ├── LegacyFakePlayer9.kt
│ │ ├── ModesetRulesIntegrationTest.kt
│ │ ├── OCMTest.kt
│ │ ├── OCMTestMain.kt
│ │ ├── OldArmourDurabilityIntegrationTest.kt
│ │ ├── OldCriticalHitsIntegrationTest.kt
│ │ ├── OldPotionEffectsIntegrationTest.kt
│ │ ├── OldToolDamageMobIntegrationTest.kt
│ │ ├── PacketCancellationIntegrationTest.kt
│ │ ├── PaperSwordBlockingDamageReductionIntegrationTest.kt
│ │ ├── PlayerKnockbackIntegrationTest.kt
│ │ ├── PlayerRegenIntegrationTest.kt
│ │ ├── SpigotFunctionChooserIntegrationTest.kt
│ │ ├── SwordBlockingIntegrationTest.kt
│ │ ├── SwordSweepIntegrationTest.kt
│ │ ├── Tally.kt
│ │ ├── TestResultWriter.kt
│ │ ├── TesterUtils.kt
│ │ ├── ToolDamageTooltipIntegrationTest.kt
│ │ └── WeaponDurabilityIntegrationTest.kt
│ └── resources/
│ └── plugin.yml
└── main/
├── java/
│ └── kernitus/
│ └── plugin/
│ └── OldCombatMechanics/
│ ├── ModuleLoader.java
│ ├── OCMConfigHandler.java
│ ├── OCMMain.java
│ ├── UpdateChecker.java
│ ├── commands/
│ │ ├── OCMCommandCompleter.java
│ │ └── OCMCommandHandler.java
│ ├── hooks/
│ │ ├── PlaceholderAPIHook.java
│ │ └── api/
│ │ └── Hook.java
│ ├── module/
│ │ ├── ModuleAttackCooldown.java
│ │ ├── ModuleAttackFrequency.java
│ │ ├── ModuleAttackRange.java
│ │ ├── ModuleAttackSounds.java
│ │ ├── ModuleChorusFruit.java
│ │ ├── ModuleDisableCrafting.java
│ │ ├── ModuleDisableEnderpearlCooldown.java
│ │ ├── ModuleDisableOffHand.java
│ │ ├── ModuleFishingKnockback.java
│ │ ├── ModuleFishingRodVelocity.java
│ │ ├── ModuleGoldenApple.java
│ │ ├── ModuleOldArmourDurability.java
│ │ ├── ModuleOldArmourStrength.java
│ │ ├── ModuleOldBrewingStand.java
│ │ ├── ModuleOldBurnDelay.java
│ │ ├── ModuleOldCriticalHits.java
│ │ ├── ModuleOldPotionEffects.java
│ │ ├── ModuleOldToolDamage.java
│ │ ├── ModulePlayerKnockback.java
│ │ ├── ModulePlayerRegen.java
│ │ ├── ModuleProjectileKnockback.java
│ │ ├── ModuleShieldDamageReduction.java
│ │ ├── ModuleSwordBlocking.java
│ │ ├── ModuleSwordSweep.java
│ │ ├── ModuleSwordSweepParticles.java
│ │ └── OCMModule.java
│ ├── paper/
│ │ └── PaperSwordBlocking.java
│ ├── updater/
│ │ ├── ModuleUpdateChecker.java
│ │ ├── SpigetUpdateChecker.java
│ │ └── VersionChecker.java
│ └── utilities/
│ ├── Config.java
│ ├── ConfigUtils.java
│ ├── EventRegistry.java
│ ├── MathsHelper.java
│ ├── Messenger.java
│ ├── TextUtils.java
│ ├── damage/
│ │ ├── AttackCooldownTracker.java
│ │ ├── DamageUtils.java
│ │ ├── DefenceUtils.java
│ │ ├── EntityDamageByEntityListener.java
│ │ ├── MobDamage.java
│ │ ├── NewWeaponDamage.java
│ │ ├── OCMEntityDamageByEntityEvent.java
│ │ └── WeaponDamages.java
│ ├── potions/
│ │ ├── PotionDurations.java
│ │ ├── PotionEffects.java
│ │ ├── PotionKey.java
│ │ └── WeaknessCompensation.java
│ ├── reflection/
│ │ ├── Reflector.java
│ │ ├── SpigotFunctionChooser.java
│ │ └── VersionCompatUtils.java
│ └── storage/
│ ├── ModesetListener.java
│ ├── PlayerData.java
│ ├── PlayerDataCodec.java
│ └── PlayerStorage.java
└── resources/
├── config.yml
└── plugin.yml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CONTRIBUTING.md
================================================
<!--
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
# Contributing
All contributions should be in the form of [pull requests](https://github.com/kernitus/BukkitOldCombatMechanics/pulls), and should use the formatting profiles provided at the bottom of this page. All pull requests must be fully functional and able to compile, and should be fully tested. Please submit **monofocal** pull requests, i.e.if you're making unrelated changes to two different modules, or decide to also update the version of a dependency, *those should be separate pull requests*.
## Testing expectations
Contributions are expected to include automated test coverage within the existing test framework where practical. For behaviour changes, bug fixes, and new features, prefer adding or updating tests in the existing Kotlin integration-test harness rather than relying on manual testing alone.
## Language expectations
New classes should be written in Kotlin by default. If you are modifying an existing Java class, keep the surrounding file consistent unless there is a clear reason to migrate it as part of the same change.
## Module system
OldCombatMechanics uses a modular system to make sure each feature is completely independent from any other, and can be toggled off and have no impact on server performance. This means each new module must extend the `Module` class, and implement a public constructor which takes an instance of the plugin and passes the module name to the superconstructor. The `Module` class also provides an overloadable `reload()` method which is called whenever the plugin is reloaded. If you are using any class-level variables they should probably be updated in here. This should also be used for any initialisation that might need to be done if the config section is changed and `ocm reload` is called. You may then call `reload()` from the constructor.
## Module naming
The name specified in the module constructor must be the same as the one used in the config.yml. The module name must meaningfully describe the purpose of the module, for example `disable-offhand` or `old-burn-delay`. The module classes should thus be respectively named `ModuleDisableOffhand` and `ModuleOldBurnDelay`. As you can see, kebab case is used for the constructor and config name, while pascal case prefixed by `Module` is used for class names.
## Module configuration
All module configuration is done through the Module class in a subsection of the config.yml for each module. To access config variables under the module section, you can use the methods provided by `module()`, such as `module().getBoolean("enableBlue")`. The module config section must contain an `enabled` boolean key which is used by the module system to selectively register/unregister the module, and, if applicable, a `worlds: []` list key to configure which worlds your module will work in. It is the responsibility of the module to use the `module().isEnabled(world)` to make sure this is enforced.
## Code style
There is relative freedom when it codes to coding style, but please adhere to the following guidelines:
* Use `final` for variables whenever possible. This makes it much less likely to accidentally change a variable and improves code readability.
* Use `if !condition return;` pattern to avoid eccessive nested if-statements. That is, to check multiple prerequisite conditions, invert the condition and return, and after returning continue with the code.
* Use internal utilies where possible. There is a `Messenger` class for sending messages to users or console, a `Reflector` class for simple reflection, and `DualVersionedMaterial` for support across minecraft versions where the Bukkit item names have changed. There are many more in the `utilities` package, the usage of which can be found in already existing modules.
## Formatting profiles
Please use the [default formatting profile](https://www.jetbrains.com/help/idea/reformat-and-rearrange-code.html) provided with IntelliJ. If using a different IDE, here is the [.editconfig](https://www.jetbrains.com/help/idea/configuring-code-style.html#editorconfig) file for those same settings:
```
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 120
tab_width = 4
ij_continuation_indent_size = 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
[*.css]
ij_css_align_closing_brace_with_properties = false
ij_css_blank_lines_around_nested_selector = 1
ij_css_blank_lines_between_blocks = 1
ij_css_brace_placement = end_of_line
ij_css_enforce_quotes_on_format = false
ij_css_hex_color_long_format = false
ij_css_hex_color_lower_case = false
ij_css_hex_color_short_format = false
ij_css_hex_color_upper_case = false
ij_css_keep_blank_lines_in_code = 2
ij_css_keep_indents_on_empty_lines = false
ij_css_keep_single_line_blocks = false
ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
ij_css_space_after_colon = true
ij_css_space_before_opening_brace = true
ij_css_use_double_quotes = true
ij_css_value_alignment = do_not_align
[*.feature]
indent_size = 2
ij_gherkin_keep_indents_on_empty_lines = false
[*.gsp]
ij_gsp_keep_indents_on_empty_lines = false
[*.haml]
indent_size = 2
ij_haml_keep_indents_on_empty_lines = false
[*.java]
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_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_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = off
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 5
ij_java_class_names_in_javadoc = 1
ij_java_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 = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = true
ij_java_doc_align_param_comments = true
ij_java_doc_do_not_wrap_if_one_line = false
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_entity_dd_suffix = EJB
ij_java_entity_eb_suffix = Bean
ij_java_entity_hi_suffix = Home
ij_java_entity_lhi_prefix = Local
ij_java_entity_lhi_suffix = Home
ij_java_entity_li_prefix = Local
ij_java_entity_pk_class = java.lang.String
ij_java_entity_vo_suffix = VO
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 = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_at_first_column = true
ij_java_message_dd_suffix = EJB
ij_java_message_eb_suffix = Bean
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 = 3
ij_java_new_line_after_lparen_in_record_header = false
ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
ij_java_parameter_annotation_wrap = off
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = 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_session_dd_suffix = EJB
ij_java_session_eb_suffix = Bean
ij_java_session_hi_suffix = Home
ij_java_session_lhi_prefix = Local
ij_java_session_lhi_suffix = Home
ij_java_session_li_prefix = Local
ij_java_session_si_suffix = Service
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = false
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_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 = false
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 = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_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
[.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,*.qrc,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.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
ij_xml_use_custom_settings = false
[{*.ft,*.vm,*.vsl}]
ij_vtl_keep_indents_on_empty_lines = false
[{*.gant,*.gradle,*.groovy,*.gson,*.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_long_lines = false
[{*.gradle.kts,*.kt,*.kts,*.main.kts}]
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = false
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_assignment_wrap = normal
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = true
ij_kotlin_call_parameters_right_paren_on_new_line = true
ij_kotlin_call_parameters_wrap = on_every_item
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = false
ij_kotlin_continuation_indent_for_expression_bodies = false
ij_kotlin_continuation_indent_in_argument_lists = false
ij_kotlin_continuation_indent_in_elvis = false
ij_kotlin_continuation_indent_in_if_conditions = false
ij_kotlin_continuation_indent_in_parameter_lists = false
ij_kotlin_continuation_indent_in_supertype_lists = false
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = normal
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = true
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = normal
ij_kotlin_method_parameters_new_line_after_left_paren = true
ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = on_every_item
ij_kotlin_name_count_to_use_star_import = 5
ij_kotlin_name_count_to_use_star_import_for_members = 3
ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.**
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false
[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
ij_html_align_attributes = true
ij_html_align_text = false
ij_html_attribute_wrap = normal
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
ij_html_enforce_quotes = false
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 2
ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = double
ij_html_remove_new_line_before_tags = br
ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
ij_html_uniform_ident = false
[{*.markdown,*.md}]
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_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
[{*.properties,spring.handlers,spring.schemas}]
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
[{*.yaml,*.yml}]
indent_size = 2
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_space_before_colon = true
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true
```
================================================
FILE: .github/ISSUE_TEMPLATE/1-bug-report.yaml
================================================
name: Bug Report
description: Report a bug in the plugin
labels: ["bug", "investigate"]
body:
- type: markdown
attributes:
value: |
## ⚠️ PLEASE READ BEFORE SUBMITTING ⚠️
1. Try the [latest test version](https://hangar.papermc.io/kernitus/OldCombatMechanics/versions?channel=Snapshot&platform=PAPER) first
2. Complete ALL fields below - incomplete reports will be CLOSED
3. This is a volunteer project - we have no obligation to help incomplete reports
4. Have a question? Please use the Question template instead
- type: input
id: server-version
attributes:
label: Server Version
description: Version of the server, e.g. Spigot 1.14.1 or Paper 1.19.3
placeholder: e.g. Paper 1.20.4
validations:
required: true
- type: input
id: ocm-version
attributes:
label: OldCombatMechanics Version
description: "EXACT version of OldCombatMechanics, e.g. 1.7.2 or 2.1.1-beta+e2f0369. DO NOT write 'latest' - versions change often."
placeholder: e.g. 2.1.1-beta+e2f0369
validations:
required: true
- type: textarea
id: server-log
attributes:
label: Server Log File
description: Console log from the server. DO NOT use an external service like pastebin, as these expire.
placeholder: Paste your log here
render: console
validations:
required: true
- type: textarea
id: config
attributes:
label: OldCombatMechanics config.yml
description: Your config file. DO NOT use an external service like pastebin, as these expire.
placeholder: Paste your config.yml here
render: yaml
validations:
required: true
- type: textarea
id: other-plugins
attributes:
label: Other Plugins
description: List of other plugins installed (conflicts are common)
placeholder: |
- ViaVersion 1.0.0
- AnotherPlugin 2.3.1
validations:
required: true
- type: textarea
id: problem-description
attributes:
label: Problem Description
description: A clear and concise description of what the bug is
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to Reproduce
description: What you would do in order for the problem to occur
placeholder: |
1. Hit a mob with a stick
2.
3.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behaviour
description: What do you think should happen when you perform the above steps?
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual Behaviour
description: What does happen when you perform the above steps?
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional Context
description: Add any other context, screenshots, or videos here
- type: checkboxes
id: confirmation
attributes:
label: Pre-submission checklist
options:
- label: I have tried the latest test/snapshot version
required: true
- label: I have filled out all required fields below
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/2-question.yaml
================================================
name: Question
description: Ask a question about the plugin
labels: ["question"]
body:
- type: markdown
attributes:
value: |
## ⚠️ PLEASE READ BEFORE SUBMITTING ⚠️
1. Check the readme and wiki first: https://github.com/kernitus/BukkitOldCombatMechanics/wiki
2. Search existing issues to see if your question has been answered
3. This is a volunteer project - we have no obligation to help incomplete reports
4. If you've found a bug, use the Bug Report template instead
5. If you want a new feature, use the Feature Request template instead
- type: checkboxes
id: confirmation
attributes:
label: Pre-submission checklist
options:
- label: I have checked the wiki and readme
required: true
- label: I have searched existing issues
required: true
- type: textarea
id: question
attributes:
label: Your Question
description: Clearly describe what you want to know
validations:
required: true
- type: textarea
id: tried
attributes:
label: What I've Tried
description: Have you checked documentation? Tried anything? Searched for similar issues?
validations:
required: true
- type: input
id: server-version
attributes:
label: Server Version
placeholder: e.g. Paper 1.20.4
validations:
required: true
- type: input
id: ocm-version
attributes:
label: OldCombatMechanics Version
description: "DO NOT write 'latest' - specify the exact version number"
placeholder: e.g. 2.1.1
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional Context
description: Any other relevant information, screenshots, config snippets, etc.
================================================
FILE: .github/ISSUE_TEMPLATE/3-feature.yaml
================================================
name: Feature Request
description: Suggest an idea for this project
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
## ⚠️ PLEASE READ BEFORE SUBMITTING ⚠️
1. Search for existing enhancement requests first
2. Have a question? Please use the Question template instead
3. This plugin is about combat mechanics changes from 1.9+ - not general Minecraft features
4. Incomplete requests WILL be closed - this is a volunteer project
Remember that this plugin is about changes in combat mechanics following 1.8, anything else is out of scope.
- type: checkboxes
id: confirmation
attributes:
label: Pre-submission checklist
options:
- label: I have searched for existing enhancement requests
required: true
- label: This relates to combat mechanics changes from 1.9+
required: true
- type: textarea
id: problem
attributes:
label: Problem
description: Describe the problem you are facing, as a consequence of a lack of features rather than a bug
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed Solution
description: The solution you think best for the problem
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternative Solutions
description: Describe alternative solutions you have considered
validations:
required: false
- type: textarea
id: evidence
attributes:
label: Evidence of Mechanic Change
description: If your enhancement is about a combat feature that changed between 1.8 and now, provide evidence (wiki links, videos, etc.)
placeholder: Links to Minecraft wiki, videos demonstrating the mechanic, etc.
validations:
required: false
- type: textarea
id: additional
attributes:
label: Additional Context
description: Any other context or screenshots that could be relevant
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
================================================
FILE: .github/release-please-config.json
================================================
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"include-component-in-tag": false,
"bump-minor-pre-major": true,
"bootstrap-sha": "56dd66a4bf88f252bb063b91835d89a1a1a2e442",
"packages": {
".": {
"package-name": "OldCombatMechanics",
"release-type": "simple",
"extra-files": [
{
"type": "generic",
"path": "build.gradle.kts"
}
],
"changelog-sections": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "perf", "section": "Performance"}
]
}
}
}
================================================
FILE: .github/release-please-manifest.json
================================================
{".":"2.4.0"}
================================================
FILE: .github/workflows/build-upload-release.yml
================================================
name: Build and Release
on:
release:
types: [published]
jobs:
build:
# This permission is still required by the new action
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'adopt'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: wrapper
# Build Step - No changes needed here
- name: Run Gradle Build
run: |
if [ "${{ github.event_name }}" == "release" ]; then
VERSION=${{ github.event.release.tag_name }}
VERSION=${VERSION#v} # Strip 'v' if present
./gradlew clean build -Pversion=$VERSION
else
./gradlew clean build
fi
- name: Upload Artifact to GitHub Release
if: ${{ github.event_name == 'release' }}
uses: softprops/action-gh-release@v2
with:
# The 'files' input takes a path to the asset(s) you want to upload.
# Keep a stable filename for external download links.
files: ./build/libs/OldCombatMechanics.jar
- name: Read game versions from gradle.properties
run: |
RAW=$(grep ^gameVersions gradle.properties | cut -d'=' -f2-)
# Convert "1.21, 1.20.6" -> "1:1.21,1:1.20.6" to select Bukkit-compatible typeId 1 entries via the minecraft endpoint
GAME_VERSIONS=$(echo "$RAW" | tr -d ' ' | awk -F',' '{
out="";
for (i=1; i<=NF; i++) {
v=$i;
if (v!= "") {
if (out!= "") out=out ",";
out=out "1:" v;
}
}
print out;
}')
echo "GAME_VERSIONS=$GAME_VERSIONS" >> "$GITHUB_ENV"
- name: Upload to CurseForge (Bukkit)
if: ${{ github.event_name == 'release' }}
uses: itsmeow/curseforge-upload@v3
with:
token: ${{ secrets.DBO_UPLOAD_API_TOKEN }}
project_id: '98233'
game_endpoint: 'minecraft'
file_path: './build/libs/OldCombatMechanics.jar'
changelog: ${{ github.event.release.body }}
changelog_type: 'markdown'
release_type: 'release'
display_name: 'OldCombatMechanics ${{ github.event.release.tag_name }}'
game_versions: ${{ env.GAME_VERSIONS }}
- name: Publish to Hangar
env:
HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }}
HANGAR_CHANGELOG: ${{ github.event.release.body }}
run: ./gradlew build publishPluginPublicationToHangar --stacktrace
================================================
FILE: .github/workflows/dev-builds.yml
================================================
name: Dev builds
on:
push:
branches:
- master
pull_request:
branches-ignore:
- 'ingametesting'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'adopt'
cache: gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: wrapper
- name: Determine if this is a release version
run: |
IS_RELEASE=$(./gradlew -q printIsRelease)
echo "Is release? $IS_RELEASE"
echo "IS_RELEASE=$IS_RELEASE" >> $GITHUB_ENV
- name: Run Gradle Build
run: |
./gradlew clean build
- name: Archive jar file
uses: actions/upload-artifact@v4
with:
name: OldCombatMechanics
path: build/libs/OldCombatMechanics.jar
- name: Publish Snapshot to Hangar
if: ${{ github.event_name == 'push' && github.event.pull_request == null && env.IS_RELEASE != 'true' }}
env:
HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }}
run: |
./gradlew publishPluginPublicationToHangar
================================================
FILE: .github/workflows/release-please.yml
================================================
name: Release Please
on:
push:
branches:
- master
permissions:
contents: write
pull-requests: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: release-please
uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
config-file: .github/release-please-config.json
manifest-file: .github/release-please-manifest.json
================================================
FILE: .github/workflows/wrap-issue-form-codeblocks.yml
================================================
name: Wrap issue form code blocks
on:
issues:
types: [opened, edited]
permissions:
issues: write
jobs:
wrap:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v8
with:
script: |
// Avoid loops: when this workflow updates the issue, it triggers "edited" again.
if (context.actor === "github-actions[bot]") return;
const issue = context.payload.issue;
if (!issue || !issue.body) return;
// On edits, bail unless the body actually changed
if (context.payload.action === "edited") {
const changed = context.payload.changes && context.payload.changes.body;
if (!changed) return;
}
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function wrapFirstCodeBlockAfterHeading(body, headingText, summaryText) {
const re = new RegExp(
`(^###\\s+${escapeRegExp(headingText)}\\s*\\n+)(\`\`\`[\\s\\S]*?\\n\`\`\`)(?=\\n|$)`,
"m"
);
return body.replace(re, (_m, heading, codeblock) => {
return (
`${heading}` +
`<details>\n` +
`<summary>${summaryText}</summary>\n\n` +
`${codeblock}\n\n` +
`</details>`
);
});
}
let body = issue.body;
body = wrapFirstCodeBlockAfterHeading(body, "Server Log File", "Server log");
body = wrapFirstCodeBlockAfterHeading(body, "OldCombatMechanics config.yml", "config.yml");
if (body === issue.body) return;
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body
});
================================================
FILE: .gitignore
================================================
# ===
# == IDE settings files
# ===
# IntelliJ
/*.eml
/*.iml
/.idea
# Eclipse
/.project
/.classpath
/.settings
# ===
# == Compilation output / working files
# ===
out
builds
target
META-INF
/build/
/gradle/
/.gradle/
.gradle-user/
/.gradle-cache/
/.gradle-local/
kls-classpath
kls_database.db
/run/
/bin/
.opencode/
================================================
FILE: AGENTS.md
================================================
# AGENTS.md
**Prime directive:** If you are reading this file, it is your responsibility as an agent to keep it up to date with any changes you make in this repository.
This file captures repo-specific context discovered while working on this branch.
## Repo overview
- Project: OldCombatMechanics (Bukkit/Paper plugin)
- Branch context: working from `kotlin-tests` branch
- Build tool: Gradle (wrapper currently at 9.2.1)
- JDKs used locally: 8, 11, 17, 25
## Integration test harness (Kotlin)
- Integration tests live in `src/integrationTest/kotlin` and are packaged into `OldCombatMechanics-<version>-tests.jar`.
- Entrypoint plugin class: `kernitus.plugin.OldCombatMechanics.OCMTestMain`.
- Tests run inside a real Paper server started by the Gradle `run-paper` plugin.
- RunServer output is redirected to `build/integration-test-logs/<version>.log`; `checkTestResults<version>` prints only a compact summary/failures to the console.
- `KotestRunner` writes a compact `plugins/OldCombatMechanicsTest/test-failures.txt` file (up to 25 failures) so CI can surface failure reasons without opening the full server log.
- Token hygiene: do **not** open/read `build/integration-test-logs/*.log` unless the user explicitly asks for log inspection; rely on the compact console summary by default and ask for permission before digging into full logs.
- PacketEvents is shaded into the plugin; integration tests do not inject external packet libraries.
- `relocateIntegrationTestClasses` (ShadowJar) relocates PacketEvents references in test classes only.
- `integrationTestJar` is a plain `Jar` task that embeds the relocated test classes plus runtime deps, excluding PacketEvents so the test plugin resolves PacketEvents from the main plugin’s shaded copy.
- `PacketCancellationIntegrationTest` now uses a cancellable `CompletableFuture.await()` so `withTimeout` actually aborts when no packet arrives.
- `PacketCancellationIntegrationTest` now drives PacketEvents via `PacketEventsImplHelper.handleClientBoundPacket` with a synthetic `User` instead of relying on live channel injection; this avoids timeouts when PacketEvents does not emit send events for fake channels.
- `PacketCancellationIntegrationTest` sets the Bukkit player on the synthetic PacketEvents event so module listeners can match `PacketSendEvent#getPlayer`.
- Matrix task: `integrationTest` depends on `integrationTestMatrix` which runs per-version tasks like:
- `integrationTest1_19_2`, `integrationTest1_21_11`, `integrationTest1_12`, `integrationTest1_9`
- Test result handoff:
- `OCMTestMain` writes `plugins/OldCombatMechanicsTest/test-results.txt` containing `PASS` or `FAIL`.
- `KotestRunner` also writes `plugins/OldCombatMechanicsTest/test-failures.txt` (small failure summary).
- Gradle `checkTestResults<version>` fails build if file missing, or content is not `PASS`.
## Version matrix + Java selection
- Config is in `build.gradle.kts`:
- `integrationTestVersions` from property or defaults.
- `requiredJavaVersion` selects Java based on version.
- Pre-1.13 versions use `integrationTestJavaVersionLegacyPre13` (default 8).
- Modern versions use Java 25 if `>=1.20.5` else Java 17.
- Legacy vanilla jar cache:
- `downloadVanilla<version>` task downloads Mojang server jars for <=1.12 and writes `run/<version>/cache/mojang_<version>.jar`.
## Kotlin test runner split (Java 11+ vs Java 8)
- Kotest 6 (Java 11+) is used for 1.19.2 and 1.21.11.
- Kotest runner class: `kernitus.plugin.OldCombatMechanics.KotestRunner`
- Project config: `KotestProjectConfig`
- Java 8 uses a separate runner:
- `kernitus.plugin.OldCombatMechanics.LegacyTestRunner`
- Currently a **smoke test** only (verifies plugin enabled + `WeaponDamages` loaded).
- This is a placeholder and **not a full integration test**.
## Fake player implementation notes
- Primary fake player implementation is `src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/FakePlayer.kt`.
- It relies on `xyz.jpenilla:reflection-remapper` to map modern NMS names.
- On legacy servers (1.12), reflection remapper mappings are unavailable; code falls back to `ReflectionRemapper.noop()`.
- As-is, `FakePlayer` uses modern NMS class names (`net.minecraft.server.MinecraftServer`, etc.).
- This fails on 1.12 which uses versioned NMS (`net.minecraft.server.v1_12_R1.*`).
- 1.12 requires a dedicated fake player path or version-aware class mapping.
## Java 8 compatibility work
- Java 8 compatibility backports already done in main code (records/pattern matching removed).
- Java 8-incompatible APIs replaced:
- `Stream.toList()` -> `collect(Collectors.toList())`
- `Set.of`/`List.of` -> `Collections.unmodifiableSet(new HashSet<>(Arrays.asList(...)))` etc.
- Added missing import in `PotionTypeCompat` for `PotionData`.
- Build config sets `options.release.set(8)` for Java, Kotlin `jvmTarget = 1.8`.
## Dependency / build updates
- Kotlin: 2.3.0
- Kotest: 6.0.7
- run-paper plugin: 3.0.2
- Hangar publish plugin: 0.1.4
- Other deps updated (bstats, netty, BSON, XSeries, authlib, reflection-remapper, adventure).
- PacketEvents is now used for packet interception (shaded and relocated in the main jar).
- PacketEvents dependency moved to `2.11.2-SNAPSHOT` (CodeMC snapshots) for 1.21.11 support.
- JSR-305 added for `javax.annotation.Nullable` (compileOnly).
## Current failing area
- Paper 1.12 integration tests **do not run real fake player tests yet**.
- Desired fix: add proper 1.12 fake player implementation (e.g., version-specific NMS path like `v1_12_R1`).
- Example 1.12 fake player implementation provided by user (NMS, PlayerList manipulation, packet send, etc.).
- We should integrate a conditional path in `FakePlayer` for 1.12 using versioned NMS classes or an alternate helper.
## Local test commands
- Run full matrix:
- `./gradlew integrationTest`
- Change matrix:
- `./gradlew integrationTest -PintegrationTestVersions=1.19.2,1.21.11,1.12`
- Set Java toolchain paths (example):
- `ORG_GRADLE_JAVA_INSTALLATIONS_PATHS=/path/to/jdk8:/path/to/jdk17:/path/to/jdk25 ./gradlew integrationTest`
## Notes
- Removed the dead reflection utility `ClassType` and the unused `Reflector#getClass(ClassType, String)` overload; `Reflector#getClass(String)` remains the supported class-resolution helper.
- `SpigotFunctionChooser` now only falls back for compatibility-style failures (LinkageError family, missing-method/class reflection failures, and explicit compatibility-signalled `UnsupportedOperationException` via `compat`/`compatibility` markers), and rethrows ordinary runtime logic failures instead of silently selecting fallback (for example generic "incompatible" wording does not trigger fallback).
- `AttackCompat` now only treats Bukkit `Player#attack` as success when it yields an observable living-target hit (health/lastDamage/noDamageTicks signal); otherwise it falls back to NMS attack candidates.
- `AttackCompat` now treats boolean-return NMS attack methods that return `false` as failed attempts and continues trying other candidates, with expanded failure diagnostics including false-result and exception counts.
- Entity-click dedupe in `ModuleSwordBlocking` now uses a taskless lazy-prune timestamp map (`System.nanoTime`) instead of a one-tick scheduled set clear, with a short dedupe window and periodic/size-triggered expiry pruning.
- `ModuleSwordBlocking` now handles `PlayerInteractEntityEvent` and `PlayerInteractAtEntityEvent` for main-hand sword blocking, with short time-window dedupe to prevent duplicate side effects when both events fire for the same click.
- Removed the unused main-source Java tester package (`kernitus.plugin.OldCombatMechanics.tester`), deleted stale commented test-command code from `OCMCommandHandler`, and dropped the now-obsolete Gradle source-set exclusion for that package.
- Paper 1.12 sometimes fails to download legacy vanilla jar from old Mojang endpoint. The custom `downloadVanilla` task fixes that by using the v2 manifest.
- 1.21.11 servers log hostname warnings and Unsafe warnings; tests still pass.
- 1.9 integration tests are currently on hold per user request.
- Kotest filters (`kotest.filter.specs`, `kotest.filter.tests`) are now passed through Gradle into the run-paper JVM args for integration tests.
- Reflection should be used only as a fallback (performance cost); prefer direct API/code paths when available.
- The Hangar publish workflow exports `HANGAR_API_TOKEN` to match the Gradle Hangar publish configuration.
- `ModuleFishingRodVelocity` uses a single shared per-tick task (1.14+) to adjust hook gravity for all active hooks, instead of one scheduled task per hook.
- `AttackCooldownTracker#getLastCooldown` is safe to call when the tracker is not registered (returns null) and uses a `HashMap` rather than a `WeakHashMap`.
- `AttackCooldownTracker` defensively feature-detects `HumanEntity#getAttackCooldown` and avoids scheduling its per-tick sampler when the API exists (modern/backported servers).
- `ModulePlayerKnockback` uses a `HashMap` + a single shared 1-tick expiry cleaner for pending velocity overrides, instead of `WeakHashMap` + one scheduled task per hit.
- `ModuleShieldDamageReduction` uses a `HashMap` + a single shared 1-tick expiry cleaner for the “fully blocked” armour-durability suppression, instead of `WeakHashMap` + one scheduled task per hit.
- `ModuleOldArmourDurability` uses a `HashMap` + a single shared 1-tick expiry cleaner for the “explosion damaged armour” suppression, instead of `WeakHashMap` + one scheduled task per explosion.
- `ModulePlayerRegen` uses tick-based interval tracking (1.8-like; TPS aware) with a `HashMap` and a single shared tick counter task that runs only while players are tracked.
- `ModuleDisableEnderpearlCooldown` uses a `HashMap` and lazily drops expired cooldown entries during checks (wall-clock cooldown; no recurring task).
- `ModuleSwordBlocking` no longer version-gates Paper support; it feature-detects Paper data component APIs and avoids ConcurrentModificationException by iterating legacy tick state over a snapshot.
- Do not gate behaviour on hard-coded Minecraft version numbers; use feature detection (class/method presence) because some servers backport APIs.
- Weapon/armour unknown-enchantment warnings only fire for non-`minecraft` namespaces; legacy servers fall back to a known vanilla-enchantment list to avoid warning on built-ins.
- For NMS access, prefer the project Reflector helpers (`utilities.reflection.Reflector`) over ad-hoc reflection, and avoid hard-coded versioned class names where heuristics (signatures/fields) can locate methods safely.
- Added integration tests in `OldPotionEffectsIntegrationTest` for strength addend scaling (Strength II and III), a distinct modifier value check, and strength multiplier scaling.
- Added integration test ensuring vanilla strength addend applies when `old-potion-effects` is disabled.
- Strength modifier in `OCMEntityDamageByEntityEvent` now stores per-level value (3) and applies level when reconstructing base damage.
- Added `OldToolDamageMobIntegrationTest` to assert old-tool-damage config affects vindicator iron-axe hits.
- `KotestRunner` class list updated to include `OldToolDamageMobIntegrationTest`.
- `ModuleOldToolDamage` now adjusts mob weapon damage by shifting base damage with the configured-vs-vanilla delta for non-player damagers.
- `ModuleOldToolDamage` documents that mob custom weapons are not detected; the delta is always applied for mobs and may conflict with other plugins.
- Added `WeaponDurabilityIntegrationTest` covering tool durability vs hit counts during invulnerability and after it expires (FakePlayer attacker vs FakePlayer victim); registered in `KotestRunner`.
- `WeaponDurabilityIntegrationTest` writes debug summaries to `build/weapon-durability-debug-<version>.txt`.
- `WeaponDurabilityIntegrationTest` and `OldToolDamageMobIntegrationTest` now resolve debug output paths relative to the repo root (based on the server run directory), avoiding hard-coded home paths.
- `OldToolDamageMobIntegrationTest` uses a Villager victim and waits for a real Vindicator hit by setting a target and retrying with tick delays (plus a best-effort NMS attackCompat call) before asserting the mob tool-damage delta.
- Module assignment is strict for configurable modules: every non-internal module must appear in exactly one of `always_enabled_modules`, `disabled_modules`, or a modeset. Internal modules (`modeset-listener`, `attack-cooldown-tracker`, `entity-damage-listener`) are always enabled and must not be listed; reload/enable fails if they are configured.
- Use British English spelling and phraseology at all times.
- DO NOT use American English spelling or phraseology under any circumstances.
- Never hard-code absolute filesystem paths in tests or production code; resolve locations relative to the repo root or server run directory.
- Added `DisableOffhandIntegrationTest` to assert the disable-offhand modeset-change handler does not clear the offhand when the module is not enabled for the player.
- `KotestRunner` now includes `DisableOffhandIntegrationTest` in its explicit class list.
- When adding new integration test specs, add them to the explicit `.withClasses(...)` list in `KotestRunner` because autoscan is disabled.
- Added `ToolDamageTooltipIntegrationTest` (in `KotestRunner` list) to define behaviour for an opt-in “configured tool damage tooltip” feature (lore line) under `old-tool-damage.tooltip` (`enabled`, `prefix`).
- `old-tool-damage.tooltip.enabled` is now enabled by default in the bundled config so players can see the configured damage in-game.
- Modules are enabled/disabled solely via `always_enabled_modules`, `disabled_modules`, and `modesets` (no per-module `enabled:` toggle).
- `SwordBlockingIntegrationTest` uses synthetic `PlayerInteractEvent` right-clicks; on legacy (offhand-shield) path this cannot reliably assert `isBlocking`/`isHandRaised` (client-driven), so tests treat “blocking applied” as either a shield injection or a raised hand depending on path.
- Added `PacketCancellationIntegrationTest` to cover PacketEvents sweep-particle and attack-sound cancellation using PacketEvents wrappers/listeners (registered in `KotestRunner`).
- Added `ModesetRulesIntegrationTest` to cover always-enabled, disabled, and modeset-scoped module rules plus reload failures for invalid assignments.
- Added `ConfigMigrationIntegrationTest` to cover config upgrade migration into always/disabled module lists and preservation of custom modesets.
- Added a `weakness should not store negative last damage values` test in `InvulnerabilityDamageIntegrationTest` that forces `old-potion-effects.weakness.modifier = -10`, consumes a weakness potion (amplifier -1), attacks once, and asserts the stored last damage is non-negative; passes on 1.12/1.19.2 after clamping, but 1.21.11 can still fail with `No stored last damage for victim (events=0)` (attack event not recorded).
- `EntityDamageByEntityListener.checkOverdamage` now clamps the stored `lastDamages` value to a minimum of 0 to avoid negative last-damage entries when weakness (or other modifiers) drives pre-clamp damage below zero.
- Weakness amplifier clamping changed in 1.20+: attempts to use amplifier `-1` are clamped to `0` (Weakness I). With low-damage weapons this can yield zero vanilla damage, and Paper 1.21 does not fire `EntityDamageByEntityEvent` for zero damage, so tests that rely on an EDBE hit must use a stronger weapon or account for the clamp.
- `OldPotionEffectsIntegrationTest` now disables the module by moving `old-potion-effects` into `disabled_modules` (and removing it from modesets/always lists), then uses `Config.reload()`; the `withConfig` helper restores module lists/modesets and saves+reloads config to keep state consistent.
- `FireAspectOverdamageIntegrationTest` includes afterburn-vs-environmental fire-tick checks for both player and zombie victims, with and without Protection IV armour (mirrors issue 707 MRE).
- Release workflow (`.github/workflows/build-upload-release.yml`) now uploads Bukkit files via `itsmeow/curseforge-upload@v3` against the `minecraft` endpoint, reusing `DBO_UPLOAD_API_TOKEN`, and prefixes `GAME_VERSIONS` as `1:<ver>` so CurseForge selects the Bukkit-compatible type-1 version entries.
## Test harness shortcuts (known non-realistic paths)
- Several integration tests manually construct and fire Bukkit events rather than triggering real in-world actions:
- `GoldenAppleIntegrationTest` (manual `PlayerItemConsumeEvent` and `PrepareItemCraftEvent`)
- `OldPotionEffectsIntegrationTest` (manual `PlayerItemConsumeEvent`, `PlayerInteractEvent`, `BlockDispenseEvent`)
- `OldArmourDurabilityIntegrationTest` (manual `PlayerItemDamageEvent`, `EntityDamageEvent`)
- `PlayerKnockbackIntegrationTest` (manual `EntityDamageByEntityEvent`, `PlayerVelocityEvent`)
- `SwordBlockingIntegrationTest` (manual `PlayerInteractEvent`)
- `SwordSweepIntegrationTest` (manual `EntityDamageByEntityEvent`)
- Some tests directly invoke module handlers instead of going through the event bus:
- `PlayerKnockbackIntegrationTest` (direct `module.onEntityDamageEntity`)
- `SwordSweepIntegrationTest` (direct `module.onEntityDamaged`)
- `AttributeModifierCompat` synthesises a fallback attack-damage modifier from `NewWeaponDamage` when API attributes are missing.
- Fake player implementations use simulated login/network plumbing (EmbeddedChannel + manual login/join/quit events), not a real networked client.
- FakePlayer uses a plain `EmbeddedChannel` (not an anonymous subclass) so PacketEvents treats it as fake and does not disallow login; dummy `decoder`/`encoder` handlers are still added to the pipeline.
- FakePlayer now schedules a manual NMS tick for non-legacy servers (prefers `doTick`, then `tick`, falls back to `baseTick`) to drive vanilla ticking like fire and passive effects.
- FakePlayer tick shim invokes `baseTick` whenever `remainingFireTicks > 0` (burning) to ensure fire tick damage events still occur on Paper 1.21+ (which can short-circuit `doTick`/`tick` for fake players). When the fake player is in water, it prefers `doTick`/`tick` so the server can properly clear fire ticks (extinguish) before any fire damage is applied.
- FakePlayer now prefers `PlayerList.placeNewPlayer` over the legacy `load`/manual list insertion path to better mirror vanilla login initialisation (helps player fire-tick damage on modern servers).
- FakePlayer does not emulate fire-tick damage; fire ticks should be driven by the NMS tick path.
- FakePlayer now forces a world add when the Bukkit world does not report the fake player entity after `placeNewPlayer` to keep PvP interactions reliable.
- FakePlayer now clears invulnerability/instabuild abilities after spawn (plus a legacy fallback) to improve PvP interactions between fake players.
- EntityDamageByEntityListener now logs extra debug about lastDamage restoration for non-entity damage, and documents the vanilla 1.12 damage flow in checkOverdamage.
- EntityDamageByEntityListener no longer overwrites the stored last-damage baseline when cancelling “fake overdamage” (e.g. cancelled fire tick during invulnerability), preventing subsequent hits from incorrectly bypassing immunity.
- Stored last-damage baselines now use a single lightweight expiry sweeper (tick-based TTL) instead of scheduling one Bukkit task per damage event; this keeps the hot path allocation-free. Expiry is monotonic (only extended, never shortened) and has a small minimum TTL to tolerate `maximumNoDamageTicks = 0`.
- `WeaponDurabilityIntegrationTest` now uses a Zombie victim (fake-player attacker) and prefers the Bukkit `Player#attack` API before falling back to reflective NMS attack resolution, to make hit delivery reliable on modern servers.
- `ModuleSwordSweepParticles` and `ModuleAttackSounds` now use PacketEvents listeners/wrappers instead of ProtocolLib.
- `PacketCancellationIntegrationTest` now builds a `PacketSendEvent` directly (reflection) from the transformed buffer, sets the packet id/type explicitly, and dispatches it via the PacketEvents `eventManager` so module listeners can cancel reliably.
- README now includes a licence note: source remains MPL‑2.0, but pre-built jars bundling PacketEvents are distributed under GPLv3; builds without PacketEvents can remain MPL‑2.0.
- Legacy fake player (1.9) now uses a plain `EmbeddedChannel` with dummy `decoder`/`encoder` handlers, mirroring the modern fake player setup so PacketEvents treats it as fake.
- `ModuleChorusFruit` now reimplements the chorus teleport search (16 attempts, world-border aware, passable feet/head, solid ground) for custom teleport distances; falls back to vanilla target if no safe spot found.
- Added `ChorusFruitIntegrationTest` (in KotestRunner list) to assert custom chorus teleport distance lands on a safe block within the configured radius.
- Chorus fruit safety test now handles legacy 1.12 by using solid/non-solid checks when `Block#isPassable` is absent; passes on 1.12, 1.19.2, and 1.21.11.
- `ModuleOldToolDamage` now supports configurable TRIDENT (melee), TRIDENT_THROWN, and MACE damage; mace preserves its vanilla fall bonus while overriding base damage. New defaults added to `old-tool-damage.damages`.
- Added `ModuleAttackRange` (Paper 1.21.11+ only) to apply a configurable attack_range data component (default 1.8-like: 0–3 range, creative 0–4, margin 0.1, mob factor 1.0); auto-disables on Spigot/older versions. `attack-range` module listed in `disabled_modules` by default.
- `ModuleSwordBlocking` now only strips the Paper `CONSUMABLE` component from sword items, preventing food and other consumables from inheriting a `!minecraft:consumable` patch when inventory events fire on 1.20.5+.
- Added `DisableOffhandReflectionIntegrationTest` (in `KotestRunner` list) to ensure reflective access to `InventoryView#getBottomInventory`/`getTopInventory` works on non-public CraftBukkit view implementations.
- `Reflector.getMethod` overloads now include declared methods and call `setAccessible(true)` to avoid `IllegalAccessException` when CraftBukkit uses non-public view classes (e.g. `CraftContainer$1` on 1.20.1).
- Added `AttackRangeIntegrationTest` (1.21.11+) to assert vanilla hits at ~3.6 blocks and 1.8-style attack_range reduces reach so the same hit misses; registered in `KotestRunner`.
- Removed the cancelled S3-only `AttackRangeIntegrationTest` case `swap hand keeps attack-range off offhand sword (Paper 1.21.11+)` so ongoing S6 validation is not blocked by a parked slice artefact.
- InvulnerabilityDamageIntegrationTest adds a case asserting environmental damage above the baseline applies during invulnerability (manual EntityDamageEvent).
- `gradle.properties` gameVersions list now includes 1.21.11 down to 1.21.1 (plus 1.21) ahead of existing entries.
- GitHub release asset now keeps a stable filename `OldCombatMechanics.jar` (no version suffix); the CurseForge upload uses the same path.
- Expanded `SwordBlockingIntegrationTest` to cover right-click blocking, non-sword handling, offhand restoration (hotbar change and drop cancel), permission gating, and preserving an existing real shield.
- Added `PaperSwordBlockingDamageReductionIntegrationTest` (in KotestRunner list) to regression-test that Paper sword blocking is recognised server-side (via `ModuleSwordBlocking.isPaperSwordBlocking`) and produces a non-zero 1.8-style reduction from `applyPaperBlockingReduction`. This uses a synthetic damage event to avoid flakiness from mob AI / PvP settings.
- Paper-only sword blocking uses a runtime-gated helper (`kernitus.plugin.OldCombatMechanics.paper.PaperSwordBlocking`) that applies consumable + blocking components via cached reflection (lookup once, MethodHandle invoke thereafter). 1.8-style reduction is applied via `EntityDamageByEntityListener` using the `BLOCKING` damage modifier, while legacy/non-Paper keeps the shield swap path.
- Fixed legacy (1.9.x) sweep detection flakiness by tracking priming per-attacker UUID (not `Location`, which is unstable due to yaw/pitch) and clearing the set on module reload; stabilises `SwordSweepIntegrationTest` on 1.9.4.
- `ModuleSwordSweep` now uses a one-shot next-tick clear (single scheduled task guarded by `pendingClearTask`) instead of a repeating every-tick task, to avoid doing any work on ticks where no sword hits occurred.
- `ModuleSwordBlocking` legacy (offhand shield) path no longer schedules per-player repeating tasks; it now uses a single shared tick runner with per-player tick deadlines (10-tick warmup + 2-tick poll + restoreDelay), which reduces task churn under heavy right-click spam.
- `ModuleLoader` now clears the static module list on initialise to prevent duplicate registrations after hot reloads.
- Added `ConsumableComponentIntegrationTest` coverage to assert the Paper sword-blocking consumable cleaner leaves swords untouched when the module is disabled (modeset/world) and does not mutate swords when no component change is required.
- `ConsumableComponentIntegrationTest` now seeds and asserts the CONSUMABLE component via NMS reflection (not Paper API) and uses standalone CraftItemStacks for cursor/current items to avoid classloader mismatches and fake-player cursor side effects.
- `ModuleSwordBlocking#onModesetChange` now strips the Paper CONSUMABLE component from the player’s main hand/offhand and stored swords when sword-blocking is disabled for that player, preventing component “taint” lingering after mode changes.
- `ModuleSwordBlocking#reload` now strips Paper sword-blocking consumable components from online players when the module is disabled globally (disabled_modules) to avoid lingering taint after config reloads.
- `ModuleSwordBlocking` now gates the Paper consumable animation by PacketEvents client version (>=1.20.5); older clients fall back to the offhand shield and unknown versions default to the animation path.
- `config.yml` now documents that Paper 1.20.5+ uses the consumable-based sword-blocking animation, with older/Paperless servers falling back to an offhand shield.
- `config.yml` now notes that ViaVersion clients older than 1.20.5 also fall back to the shield behaviour.
- Added `ConsumableComponentIntegrationTest` coverage for disabling sword-blocking via `disabled_modules` and asserting the consumable component is cleared after config reload.
- Extended `ConsumableComponentIntegrationTest` to cover disabled-module right-click suppression, reload toggling, stored-inventory cleanup, offhand stability, and modeset-change behaviour after a disabled reload.
- Added `ConsumableComponentIntegrationTest` coverage for forcing an older client version and asserting sword-blocking falls back to an offhand shield without applying consumable components.
- `ConsumableComponentIntegrationTest` now uses PacketEvents reflection to seed a User/client version for fake players when PacketEvents has not registered one yet.
- `ModuleSwordBlocking#restore` no longer skips offhand restoration just because Paper support is present; older clients using the shield fallback now restore their original offhand item correctly.
- Added `ConsumableComponentIntegrationTest` coverage asserting the older-client shield fallback restores the offhand item on hotbar change.
- Added `ConsumableComponentIntegrationTest` coverage for inventory-glitch regressions: unknown PacketEvents client versions should use shield fallback, middle-clicking custom GUIs should not mutate held sword components, and custom-GUI drag events should not rewrite top-inventory swords.
- Those inventory-glitch regressions now pass on 1.21.11 after the unknown-client fallback + click/drag scope fixes.
- `ModuleSwordBlocking#supportsPaperAnimation` now treats unknown PacketEvents client versions as shield-fallback only when a PacketEvents `User` exists; if no user is registered yet (early login/synthetic test player), it keeps animation support to avoid regressing normal modern-client behaviour.
- `ModuleSwordBlocking.ConsumableCleaner#onInventoryClickPost` now re-applies the main-hand consumable component only for click paths that can actually affect the selected hotbar slot (selected NUMBER_KEY swaps or direct selected-slot player-inventory clicks), and ignores middle-clicks.
- `ModuleSwordBlocking.ConsumableCleaner#onInventoryDrag` now ignores top-inventory raw slots and skips main-hand re-application when the drag only touched top inventory slots, preventing custom-GUI slot rewrites.
- Added `ConsumableComponentIntegrationTest` coverage for legacy fallback shield-guard scope: custom GUI shield-icon clicks and unrelated shield drops should not be cancelled while fallback is active; temporary offhand shield swap blocking should remain enforced.
- `ModuleSwordBlocking#onInventoryClick` legacy shield-guard scope now only cancels direct offhand temporary-shield interactions (player inventory slot 40), avoiding cancellation of unrelated custom-GUI shield clicks.
- `ModuleSwordBlocking#onInventoryClick` also blocks `ClickType.SWAP_OFFHAND` while temporary legacy shield state is active, preventing offhand shield extraction via inventory swap-clicks.
- `ModuleSwordBlocking#onItemDrop` no longer cancels shield drops while legacy fallback state is active; it now force-restores the temporary shield state immediately to avoid trapping unrelated shield drops.
- `ModuleSwordBlocking#isPlayerBlocking` now requires an actual offhand shield before treating `isBlocking`/`isHandRaised` as legacy shield-blocking, preventing stale hand-use state from suppressing fallback shield injection.
- `ModuleSwordBlocking#supportsPaperAnimation` now falls back to `User#getClientVersion` when `PlayerManager#getClientVersion` is null, improving old-client fallback stability in synthetic/integration scenarios.
- `ModuleSwordBlocking#supportsPaperAnimation` now fails safe to legacy shield fallback when PacketEvents client-version resolution is unavailable (resolver initialisation missing, resolver methods/objects null, or reflection errors), while preserving existing behaviour for normal early-login/synthetic-player cases.
- `ModuleSwordBlocking#onPlayerSwapHandItems` now treats stale legacy stored-shield state as non-authoritative for Paper-animation players: it clears stale legacy entries instead of cancelling swaps, so synthetic swap listeners still run.
- `ModuleSwordBlocking#onPlayerSwapHandItems` now restores any stale legacy stored offhand item (via `restore(..., true)`) before clearing legacy state on the Paper-animation path, so stale entries are not silently discarded.
- `ConsumableCleaner#onSwap` now snapshots the held hotbar slot and only reapplies the Paper consumable component when the slot is unchanged on the deferred tick; offhand stripping remains deferred as before.
- The new legacy-scope regressions now pass on 1.19.2 and 1.21.11.
- `ModuleSwordBlocking.ConsumableCleaner#onInventoryClickPost` now snapshots click context (held slot and open inventory top/bottom) and skips next-tick reapply when that context is stale, preventing deferred reapply from tainting a newly selected main-hand sword.
- Added `ConsumableComponentIntegrationTest` coverage for stale `PlayerSwapHandItemsEvent` deferred reapply: if held slot changes before next tick, the newly selected main-hand sword must not gain a consumable component, while swapped-offhand sword cleanup still occurs.
- Added `ConsumableComponentIntegrationTest` regression coverage for stale `PlayerSwapHandItemsEvent` deferred reapply when the open inventory view changes before next tick: the new main-hand context must not be tainted, while swapped-offhand consumable cleanup must still run.
- `ModuleSwordBlocking.ConsumableCleaner#onSwap` now snapshots open-inventory top/bottom identity at swap time and requires a view match only for deferred main-hand reapply; deferred offhand consumable cleanup still runs even when the view changed.
- `ModuleSwordBlocking.ConsumableCleaner` click/drag handlers now follow a minimal-mutation policy (no proactive consumable strip/apply in `InventoryClickEvent` or `InventoryDragEvent` paths); cleanup is handled opportunistically via lifecycle/transition handlers (`onModesetChange`/`reload`/world-change, held-slot, and swap paths) using a shared per-player consumable sweep helper.
- `ModuleSwordBlocking#onModesetChange` now force-restores stale legacy fallback shield state (`restore(..., true)`) when sword-blocking becomes disabled for a player, before sweeping Paper consumable components.
- `ModuleSwordBlocking.ConsumableCleaner#onWorldChange` now strips consumable components from main hand, offhand, and stored swords without re-applying, so world changes clear stale consumable state consistently.
- `ModuleSwordBlocking.ConsumableCleaner#onQuit` now uses the same strip-only full sweep as world-change cleanup (main hand, offhand, and stored swords), ensuring logout clears stale consumable state from storage as well.
- `ModuleSwordBlocking` now handles `PlayerJoinEvent` with a force legacy restore (`restore(..., true)`) followed by strip-only consumable cleanup (main hand, offhand, and stored swords), so join clears stale state without re-applying consumable components.
- `ModuleSwordBlocking` inner listener `ConsumableCleaner` was renamed to `ConsumableLifecycleHandler` (registration updated; behaviour unchanged).
- Added `sword-blocking.paper-animation` config (default `true`) to hard-disable the Paper consumable animation path; when false, sword-blocking always uses the legacy shield fallback and reload/lifecycle cleanup strips stale consumable state without re-applying it.
- Legacy sword-blocking now marks injected temporary offhand shields when marker APIs are available so death handling can identify the temporary drop path reliably.
- `ModuleSwordBlocking#onPlayerDeath` now pops stored offhand exactly once, clears legacy state deterministically, respects `keepInventory` without rewriting drops, rewrites at most one temporary shield drop, and adds the stored offhand drop when no temporary shield drop is found.
- Legacy shield-drop reconciliation now uses a strict safety-first fallback when marker APIs are unavailable: it does not guess temporary shields from plain shield shape, avoiding accidental replacement of legitimate shields (stored offhand is appended instead).
- For synthetic/manual death events where drop metadata may not preserve the temporary-shield marker, `ModuleSwordBlocking#onPlayerDeath` allows a single shield-drop rewrite only when the player offhand was verified as marker-tagged at death time.
- `ModuleAttackCooldown` now supports `disable-attack-cooldown.held-item-attack-speeds.<MATERIAL>` overrides with XMaterial/Material matching, falls back to `generic-attack-speed`, reconciles on join/world/modeset/hotbar/swap, restores vanilla base ATTACK_SPEED 4.0 when disabled, and no longer calls `player.saveData()` on attack-speed updates. The bundled config defaults keep most items on the no-cooldown fallback, with `TRIDENT`, `MACE`, and spear variants listed as the built-in slower exceptions; keys for newer-version materials are ignored safely on older servers.
- `ModuleAttackCooldown` hotbar/swap handling is immediate best-effort only: at `EventPriority.HIGHEST`, it re-reads the live inventory only when the event is already cancelled, otherwise it trusts the event payload and applies straight away; later plugin mutations may win until the next ordinary reconcile trigger.
- `.github/CONTRIBUTING.md` now states that contributions should include automated coverage within the existing test framework where practical, and that new classes should be written in Kotlin by default.
## Fire aspect / fire tick test notes
- `FireAspectOverdamageIntegrationTest` now uses a Zombie victim for real fire tick sampling, with max health boosted (via MAX_HEALTH attribute) to survive rapid clicking.
- The first two tests fire a synthetic `EntityDamageEvent` with `FIRE_TICK` to control timing and make the baseline check deterministic.
- Paper 1.12 applies attack-cooldown scaling before the Bukkit damage event fires; fake players can start with a low initial cooldown, producing a weak first hit and allowing a stronger second hit as legitimate “overdamage”. `FireAspectOverdamageIntegrationTest` now waits a few ticks before the first attack to make the first hit stable on 1.12.
- Added extra fire edge-case coverage in `FireAspectOverdamageIntegrationTest`: fire resistance + fire-immune victim rapid-click parity, water extinguish behaviour (no fire tick damage while submerged), fire protection vs protection comparisons (uses a Zombie victim for consistency), and alternating attackers during invulnerability.
- Legacy (1.12) fake player behaviour differs for player-specific fire tick sampling, so the player afterburn-vs-environmental comparisons are no-ops on legacy; the Zombie variants still run across all versions.
## TDAID reminders (this repo)
- Plan → Red → Green → Refactor → Validate.
- Red phase: only touch tests. Do not modify production code.
- Green phase: only touch production code. Do not modify tests.
- Refactor phase: cleanups only; keep behavior unchanged and tests green.
- Validate phase: rerun tests and do a human sanity check before declaring done.
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## [2.4.0](https://github.com/kernitus/BukkitOldCombatMechanics/compare/v2.3.0...v2.4.0) (2026-03-08)
### Features
* togglable paper sword blocking ([5d3887d](https://github.com/kernitus/BukkitOldCombatMechanics/commit/5d3887d5b18849ec150cabbd459e66104708dda3))
### Bug Fixes
* don't overwrite swords on every click [#843](https://github.com/kernitus/BukkitOldCombatMechanics/issues/843) ([4323853](https://github.com/kernitus/BukkitOldCombatMechanics/commit/432385320c418c0e46df6869e56af051629bfdab))
* **inventory:** harden stale deferred item mutation paths ([a92664d](https://github.com/kernitus/BukkitOldCombatMechanics/commit/a92664d293af0dd5fd445ca7db47e078386affd7))
* **reflection:** tighten chooser compatibility fallback ([f383340](https://github.com/kernitus/BukkitOldCombatMechanics/commit/f3833406e4b471a3d89f5e1c5ed8ada9d1d3e1ae))
* strip sword consumable component ([4624ac0](https://github.com/kernitus/BukkitOldCombatMechanics/commit/4624ac0c422d7f05835cd511b701026769917292))
* **sword-blocking:** clear sword consumable components on reload when disabled ([b887a28](https://github.com/kernitus/BukkitOldCombatMechanics/commit/b887a28a95b07efa5ec641660aee590207a0d50d)), closes [#845](https://github.com/kernitus/BukkitOldCombatMechanics/issues/845)
* **sword-blocking:** fall back to shield for pre-1.20.5 clients ([388bee5](https://github.com/kernitus/BukkitOldCombatMechanics/commit/388bee573169a45a27c41558a946e493880677e7)), closes [#842](https://github.com/kernitus/BukkitOldCombatMechanics/issues/842)
* **sword-blocking:** harden inventory fail-safes to prevent ghosting ([8889bfe](https://github.com/kernitus/BukkitOldCombatMechanics/commit/8889bfeb467c9f64f4d35a2d35e8456f179c35d1))
* **sword-blocking:** harden legacy death shield drop reconciliation ([2bb730e](https://github.com/kernitus/BukkitOldCombatMechanics/commit/2bb730e5c223da5c1cd73c91a093c86d3dedefbb))
* **sword-blocking:** prevent GUI click/drag item rewrites & fallback unknown clients to shield ([cfc596c](https://github.com/kernitus/BukkitOldCombatMechanics/commit/cfc596c3d674fd99a9c07b4c7c07c354d58fc755))
* **sword-blocking:** prevent legacy fallback from cancelling unrelated shield interactions ([727fa97](https://github.com/kernitus/BukkitOldCombatMechanics/commit/727fa97b4e904ac06514170aae27ba1bc91dbb8b))
* **sword-blocking:** restore offhand item for pre-1.20.5 fallback clients ([68073e8](https://github.com/kernitus/BukkitOldCombatMechanics/commit/68073e876dffa853329c414d156086fe1c76b3fd))
* **sword-blocking:** sweep stale consumable state on join/quit/world ([96d6981](https://github.com/kernitus/BukkitOldCombatMechanics/commit/96d698145e9d8f1543b272adf87d5026076de927))
## [2.3.0](https://github.com/kernitus/BukkitOldCombatMechanics/compare/v2.2.0...v2.3.0) (2026-01-24)
### Features
* 1.8 hitbox ([5cbd93d](https://github.com/kernitus/BukkitOldCombatMechanics/commit/5cbd93d89dde101dd7e750d4ee13e733b6dfd4a2)), closes [#69](https://github.com/kernitus/BukkitOldCombatMechanics/issues/69)
* always & disabled modules lists ([5f7bf12](https://github.com/kernitus/BukkitOldCombatMechanics/commit/5f7bf127412e2675294d27703ac6562a4c8e0c9d))
* always & disabled modules lists ([18dd6e1](https://github.com/kernitus/BukkitOldCombatMechanics/commit/18dd6e17aeb5e391fbb5704c0726d05e0b676971))
* copper tools support [#822](https://github.com/kernitus/BukkitOldCombatMechanics/issues/822) [#823](https://github.com/kernitus/BukkitOldCombatMechanics/issues/823) ([47ffa96](https://github.com/kernitus/BukkitOldCombatMechanics/commit/47ffa963ecc8619f064b98054d35df81f89a682d))
* custom trident & mace damage ([be70c5b](https://github.com/kernitus/BukkitOldCombatMechanics/commit/be70c5bb7756699fe4e3cb5bced450df3308f195)), closes [#757](https://github.com/kernitus/BukkitOldCombatMechanics/issues/757)
* item damage lore ([0ca855a](https://github.com/kernitus/BukkitOldCombatMechanics/commit/0ca855a21634c933ab0626770a74f756e57849fe)), closes [#775](https://github.com/kernitus/BukkitOldCombatMechanics/issues/775)
* kotlin integration tests ([c63c940](https://github.com/kernitus/BukkitOldCombatMechanics/commit/c63c940fcc292d4db3a27185a613a01e7c5c04f0))
* switch from protocollib to packetevents ([44afce1](https://github.com/kernitus/BukkitOldCombatMechanics/commit/44afce1a0f01a0ad54b39662e3597a8e371c5454)), closes [#790](https://github.com/kernitus/BukkitOldCombatMechanics/issues/790)
* sword blocking animation [#769](https://github.com/kernitus/BukkitOldCombatMechanics/issues/769) ([8596c9d](https://github.com/kernitus/BukkitOldCombatMechanics/commit/8596c9da58dc2167d05ac878d4a7809014ff02d8))
* warn on unknown effects, enchants, etc ([fa828d3](https://github.com/kernitus/BukkitOldCombatMechanics/commit/fa828d3469ca18b51ffd0871bd8e510f0c831d1c))
### Bug Fixes
* 'disable-offhand' module working even if disabled ([ecca0b5](https://github.com/kernitus/BukkitOldCombatMechanics/commit/ecca0b56d6f8c1d9b2e96d190ae2098dfeb10fc4))
* `disable-offhand` handling on modeset change ([54aaf0c](https://github.com/kernitus/BukkitOldCombatMechanics/commit/54aaf0c1a0a812c89c39ae0c49fe6300760a8ecc))
* apply old tool damage to all mobs ([91b121f](https://github.com/kernitus/BukkitOldCombatMechanics/commit/91b121ff009971586c877778e6a75309088ba667)), closes [#735](https://github.com/kernitus/BukkitOldCombatMechanics/issues/735)
* chorus fruit tp into blocks ([bba1ecb](https://github.com/kernitus/BukkitOldCombatMechanics/commit/bba1ecb62ec9faf1526189f308798d3b25f43cf9)), closes [#748](https://github.com/kernitus/BukkitOldCombatMechanics/issues/748)
* clear modules list on reload ([ebdfd10](https://github.com/kernitus/BukkitOldCombatMechanics/commit/ebdfd101ae1393dbf97c431726b09ea797cc267d))
* double strength effect when old-potion-effects disabled ([4cecb64](https://github.com/kernitus/BukkitOldCombatMechanics/commit/4cecb649c8e03c0c2fd593b907da778e3f7dc453)), closes [#781](https://github.com/kernitus/BukkitOldCombatMechanics/issues/781)
* fire damage overwriting lastDamage ([9af8a0f](https://github.com/kernitus/BukkitOldCombatMechanics/commit/9af8a0fa796513ab88588612f5551c5bf582db32)), closes [#707](https://github.com/kernitus/BukkitOldCombatMechanics/issues/707)
* legacy (pre-1.11) sweep detection ([2825b35](https://github.com/kernitus/BukkitOldCombatMechanics/commit/2825b35b4c6b0b879389da170e6d85b9441d9799))
* negative last damage ([1321717](https://github.com/kernitus/BukkitOldCombatMechanics/commit/1321717288ec9624065d86b00aa441f9a1404b52)), closes [#765](https://github.com/kernitus/BukkitOldCombatMechanics/issues/765)
* only strip consumable on swords ([e01faea](https://github.com/kernitus/BukkitOldCombatMechanics/commit/e01faea183d8387371dbd62e89fb847deb2fc38e)), closes [#841](https://github.com/kernitus/BukkitOldCombatMechanics/issues/841)
* reflection error on weapon enchant ([10ff4ce](https://github.com/kernitus/BukkitOldCombatMechanics/commit/10ff4ceead5147b87c13ffea1401ef716596b80c)), closes [#840](https://github.com/kernitus/BukkitOldCombatMechanics/issues/840)
* skip unknown sound packets ([cfa1e58](https://github.com/kernitus/BukkitOldCombatMechanics/commit/cfa1e58b5b5e481e1427458d22a98e487b32a621))
* unknown particles. ([1408720](https://github.com/kernitus/BukkitOldCombatMechanics/commit/140872008929ecc49918bdf0cc8e40d226957054)), closes [#825](https://github.com/kernitus/BukkitOldCombatMechanics/issues/825)
* weakness calc on >=1.20 ([f502adb](https://github.com/kernitus/BukkitOldCombatMechanics/commit/f502adbbece03875e491bd1f39e4a45d1f498802))
* weakness calculations for amplifier > 1 ([d866015](https://github.com/kernitus/BukkitOldCombatMechanics/commit/d86601504eb6d16481e18946cf07e7432039cd92))
### Performance
* **attack-cooldown-tracker:** gate sampler by API presence and use HashMap for stable caching ([b605501](https://github.com/kernitus/BukkitOldCombatMechanics/commit/b605501b40d350457d5227ce24c9c9a9522ed1f9))
* **disable-enderpearl-cooldown:** use HashMap and lazily drop expired cooldown entries ([83ff726](https://github.com/kernitus/BukkitOldCombatMechanics/commit/83ff726c5a9a2c2e6cc16571ce6e2d37123341ab))
* **fishing-rod-velocity:** replace per-hook gravity tasks with single shared tick runner ([5856bed](https://github.com/kernitus/BukkitOldCombatMechanics/commit/5856bed51830c51b0b6c205cb010fcb8dd9be0ac))
* **old-armour-durability:** replace per-explosion suppression task with shared 1-tick expiry cleaner ([c7932d6](https://github.com/kernitus/BukkitOldCombatMechanics/commit/c7932d6f0b32966ad38cdb81f8c83b14ca438478))
* **old-player-regen:** switch to tick-based interval tracking with shared counter task ([4f8df3c](https://github.com/kernitus/BukkitOldCombatMechanics/commit/4f8df3c5d0781240a46a4ed7e1ce10043aafb8d1))
* **player-knockback:** replace per-hit cleanup tasks with shared 1-tick expiry cleaner ([9667ef5](https://github.com/kernitus/BukkitOldCombatMechanics/commit/9667ef539e033ede2b97fadf309cd805df6900c5))
* **shield-damage-reduction:** replace per-hit fully-blocked cleanup tasks with shared 1-tick expiry cleaner ([a18b1ef](https://github.com/kernitus/BukkitOldCombatMechanics/commit/a18b1efec039c35b12f2c7c53dcc2be875545b29))
* **sword-block:** reduce amount of recurring tasks ([a08b5b4](https://github.com/kernitus/BukkitOldCombatMechanics/commit/a08b5b47bc5c1645db885ffca794e3ac7d9592ba))
* use one task to clear EDBEE map ([11ec51b](https://github.com/kernitus/BukkitOldCombatMechanics/commit/11ec51bd609339b8e41d74ef2d8697c3083a7d5f))
## [2.2.0](https://github.com/kernitus/BukkitOldCombatMechanics/compare/v2.1.0...v2.2.0) (2025-10-14)
### Features
* add fallback sound reflection logic ([1592dc2](https://github.com/kernitus/BukkitOldCombatMechanics/commit/1592dc249870ad3113b924cdebd41cbc36b68ad5))
### Bug Fixes
* ocm mode permissions ([e2f0369](https://github.com/kernitus/BukkitOldCombatMechanics/commit/e2f0369f294e250e8cfc474bbd1121498ecf09fe)), closes [#818](https://github.com/kernitus/BukkitOldCombatMechanics/issues/818)
* update checker versioning logic ([8d88a49](https://github.com/kernitus/BukkitOldCombatMechanics/commit/8d88a49d0fa1e8c7dfa7c48cf66c8399c766d6e0))
* use reflection for inventory view ([462e536](https://github.com/kernitus/BukkitOldCombatMechanics/commit/462e536628f3df544c9cf0fe42705eff46b7d4f6)), closes [#812](https://github.com/kernitus/BukkitOldCombatMechanics/issues/812)
### Documentation
* update issue templates ([5927729](https://github.com/kernitus/BukkitOldCombatMechanics/commit/5927729ea5fcca44a6218b10f40cec3f8ce3d4a3))
* update issue templates ([09fd3c5](https://github.com/kernitus/BukkitOldCombatMechanics/commit/09fd3c556ad02d25caa875e7dfe5854888a920cf))
* use yaml issue forms ([fc51924](https://github.com/kernitus/BukkitOldCombatMechanics/commit/fc51924504e3718b9829f4a7deca7a8701000f9f))
## [2.1.0](https://github.com/kernitus/BukkitOldCombatMechanics/compare/v2.0.4...v2.1.0) (2025-08-21)
### Features
* compat with 1.21.8 enums ([68c51ab](https://github.com/kernitus/BukkitOldCombatMechanics/commit/68c51ab8803da56f477660af247c37e5171bc581))
* make config upgrader remove deprecated keys ([e728374](https://github.com/kernitus/BukkitOldCombatMechanics/commit/e72837462fb8c512c9971c1e7d9376c82f37e741))
* remove deprecated modules ([da3d5f0](https://github.com/kernitus/BukkitOldCombatMechanics/commit/da3d5f0b28990d8f654b8557e68f54f41e8b5a60))
### Bug Fixes
* check fishing knockback module enabled when reeling in ([31e3877](https://github.com/kernitus/BukkitOldCombatMechanics/commit/31e3877330cdd820173a5d89e021919b153c6988)), closes [#803](https://github.com/kernitus/BukkitOldCombatMechanics/issues/803)
* disable attack sounds not working in >1.21 ([b355322](https://github.com/kernitus/BukkitOldCombatMechanics/commit/b355322f9b3f5e1c2b1e889684d6242f08ceee92)), closes [#794](https://github.com/kernitus/BukkitOldCombatMechanics/issues/794)
* ExceptionInInitialiserError in enchantment compat ([b2379cc](https://github.com/kernitus/BukkitOldCombatMechanics/commit/b2379cc17e309b035c2acb396392723f15ed3ee2)), closes [#782](https://github.com/kernitus/BukkitOldCombatMechanics/issues/782)
* improve sound packet compatibility ([4ef28fb](https://github.com/kernitus/BukkitOldCombatMechanics/commit/4ef28fb7bd63631fa4b6f0366d23dd1e159aa115)), closes [#780](https://github.com/kernitus/BukkitOldCombatMechanics/issues/780)
* null pointer in potion compat ([b30e27d](https://github.com/kernitus/BukkitOldCombatMechanics/commit/b30e27ddb32d210371152feee8d337f27ea8f495)), closes [#791](https://github.com/kernitus/BukkitOldCombatMechanics/issues/791)
* unmentioned worlds modesets not allowed [#792](https://github.com/kernitus/BukkitOldCombatMechanics/issues/792) ([95c9446](https://github.com/kernitus/BukkitOldCombatMechanics/commit/95c9446edbd0fe56bce0864798cf8d1c70865f8b))
### Refactoring
* improve fishing knockback cross-version compat ([d119c9f](https://github.com/kernitus/BukkitOldCombatMechanics/commit/d119c9f35e1a89be8fb8d03573041cfc2ae2d418))
## [2.0.4](https://github.com/kernitus/BukkitOldCombatMechanics/compare/2.0.3...v2.0.4) (2024-10-28)
### Bug Fixes
* avoid ghost items after sword block restore ([822fb1f](https://github.com/kernitus/BukkitOldCombatMechanics/commit/822fb1fa147fc49266cb9f0668869959e341982e)), closes [#749](https://github.com/kernitus/BukkitOldCombatMechanics/issues/749)
* don't prevent moving shield to chests in disable offhand module ([b299df2](https://github.com/kernitus/BukkitOldCombatMechanics/commit/b299df2d21ace1c7e88b1ee8fafb297e2a9347e8))
* error when right clicking air while holding block in <1.13 ([cbc0c4b](https://github.com/kernitus/BukkitOldCombatMechanics/commit/cbc0c4bc8bf0afd56005699ce70f86ec9b637646)), closes [#754](https://github.com/kernitus/BukkitOldCombatMechanics/issues/754)
* listen to dynamically loaded worlds for modesets ([f5b59d7](https://github.com/kernitus/BukkitOldCombatMechanics/commit/f5b59d7537d410fac35fbb4e0181a61a485ae1a5)), closes [#747](https://github.com/kernitus/BukkitOldCombatMechanics/issues/747)
* resolve elytras always unequipped by removing out-of-scope module ([07106e6](https://github.com/kernitus/BukkitOldCombatMechanics/commit/07106e61a220ec4137a3de200a393cf6aaa50be7)), closes [#725](https://github.com/kernitus/BukkitOldCombatMechanics/issues/725)
* fix sword blocking shield ending up in inventory on world change ([8aa3fa3](https://github.com/kernitus/BukkitOldCombatMechanics/commit/8aa3fa33081c1e1b1a48baa484fd6946b275362b)), closes [#753](https://github.com/kernitus/BukkitOldCombatMechanics/issues/753)
================================================
FILE: LICENCE
================================================
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
================================================
FILE: README.md
================================================
<!--
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<p align="center">
<img src="res/ocm-icon.png" width=320>
<img src="res/ocm-banner.png" width=1000>
</p>
## by kernitus and Rayzr522
Fine‑tune Minecraft combat, movement, and item balance without breaking your server. OldCombatMechanics is a free, open‑source toolkit for Spigot & Paper that lets you mix 1.8‑style snappiness with modern features, per world and per player.
**Why servers pick OCM** ✨
- 🧩 **Modular:** enable only what you need: cooldowns, tool damage, knockback, shields, potions, reach, sounds, more.
- 🚀 **Performant:** lean listeners only enabled as needed; reflection lookups are cached and recurring tasks are minimised (shared where possible) to keep tick time low on busy PvP servers.
- 🗺️ **Modesets:** ship different rules for different worlds or players; perfect for mixed PvP/PvE, minigames, or duels.
- ⏪ **Backwards‑friendly:** runs on Java 8+, supports 1.9 to latest; integrates cleanly with PlaceholderAPI and PacketEvents.
- ✅ **Tested for you:** live integration tests run real Paper servers across multiple versions every build.
- 💸 **Zero cost:** fully open source, optional basic telemetry (bStats only), no paywalls.
**Quick start** ⚡
1. Drop the jar into `plugins/` (Spigot or Paper-derivatives 1.9+).
2. Restart and edit `config.yml` to pick your modules and modesets.
3. Use `/ocm reload` to apply changes instantly.
4. Hand players `/ocm modeset <name>` to let them choose their ruleset.
<p align="center">
<a href="https://hangar.papermc.io/kernitus/OldCombatMechanics">
<img src="res/paper.png" alt="Paper" height="100">
</a>
<a href="https://www.spigotmc.org/resources/19510/">
<img src="res/spigot.png" alt="Spigot" height="100">
</a>
<a href="https://dev.bukkit.org/projects/oldcombatmechanics">
<img src="res/bukkit.png" alt="Bukkit" height="100">
</a>
</p>
<hr/>
## 🧰 Modesets
- Per-player/per-world presets that decide which features are active; each world has an allowed list and a default modeset.
- Let players pick ( `/ocm modeset <name>` ) to run, for example, 1.8-style PvP in an arena world while keeping vanilla rules in survival.
## ⚙ Configurable Features
Features are grouped in `module`s as listed below, and can be individually configured and disabled. Disabled modules will have no impact on server performance.
#### ⚔ Combat
*Tweak timing, damage, and reach.*
- **Attack cooldown:** adjust or remove 1.9+ cooldown
- **Attack frequency:** set global hit delay
- **Tool damage:** pre-1.9 weapon values
- **Attack range (Paper 1.21.11+):** 1.8-style reach
- **Critical hits:** control crit multiplier
- **Player regen:** tune regen rates
#### 🤺 Armour
*Balance defence and wear.*
- **Armour strength:** scale armour protection
- **Armour durability:** change durability loss
#### 🛡 Swords & Shields
*Control block and sweep behaviour.*
- **Sword blocking:** restore old right-click block; on Paper 1.21.2+ we also add the native sword blocking animation via the consumable component
- **Shield damage reduction:** scale shield protection
- **Sword sweep:** enable or disable sweeps
- **Sword sweep particles:** hide or show sweep visuals
#### 🌬 Knockback
*Shape knockback per source.*
- **Player knockback:** adjust PvP knockback
- **Fishing knockback:** fishing-rod knockback
- **Fishing rod velocity:** pull speed
- **Projectile knockback:** arrows and other projectiles
#### 🧙 Gapples & Potions
*Change consumable power.*
- **Golden apple crafting and effects:** notch and normal
- **Potion effects and duration:** old-style values
- **Chorus fruit:** teleport behaviour and range
#### ❌ New feature disabling
*Toggle later-version mechanics.*
- **Item crafting:** block selected recipes
- **Offhand:** disable offhand use
- **New attack sounds:** mute new swing sounds
- **Enderpearl cooldown:** enable or remove cooldown
- **Brewing stand refuel:** alter fuel use
- **Burn delay:** adjust fire tick delay
## 🔌 Compatibility & Testing
- OCM targets Spigot 1.9+ and runs on Java 8 and up.
- We stick to Spigot/Paper APIs for forward compatibility; NMS/reflection is used only when necessary.
- Integration tests boot real servers on 1.9.4, 1.12, 1.19.2, and 1.21.11 each build to verify behaviour.
- Most plugins work fine with OCM. Explicitly tested integrations include PlaceholderAPI (see [wiki](https://github.com/kernitus/BukkitOldCombatMechanics/wiki/PlaceholderAPI)).
## 🧾 Licence
- Source code in this repository is under the Mozilla Public License 2.0 (MPL‑2.0).
- Pre-built jars bundle PacketEvents (GPLv3). Those binary distributions are provided under GPLv3 terms due to the included dependency.
- If you build a jar without PacketEvents, you may distribute that build under MPL‑2.0, subject to its terms.
## ⚡ Development Builds
Oftentimes a particular bug fix or feature has already been implemented, but a new version of OCM has not been released
yet. You can find the most up-to-date version of the plugin
on [Hangar](https://hangar.papermc.io/kernitus/OldCombatMechanics/versions?channel=Snapshot&platform=PAPER).
## 🤝 Contributions
If you are interested in contributing, please [check this page first](.github/CONTRIBUTING.md).
<hr/>
<a href="https://bstats.org/plugin/bukkit/OldCombatMechanics">
<img src="https://bstats.org/signatures/bukkit/OldCombatMechanics.svg" alt="bStats">
</a>
================================================
FILE: build.gradle.kts
================================================
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import groovy.json.JsonSlurper
import io.papermc.hangarpublishplugin.model.Platforms
import org.gradle.api.Action
import org.gradle.api.attributes.java.TargetJvmVersion
import org.gradle.api.file.FileCopyDetails
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import xyz.jpenilla.runpaper.task.RunServer
import java.io.ByteArrayOutputStream
import java.io.Closeable
import java.io.Serializable
import java.net.URI
import java.nio.file.Files
import java.security.MessageDigest
val paperVersion: List<String> =
(property("gameVersions") as String)
.split(",")
.map { it.trim() }
plugins {
`java-library`
kotlin("jvm") version "2.3.0"
id("com.gradleup.shadow") version "9.3.0"
id("xyz.jpenilla.run-paper") version "3.0.2"
idea
id("io.papermc.hangar-publish-plugin") version "0.1.4"
}
// Make sure javadocs are available to IDE
idea {
module {
isDownloadJavadoc = true
isDownloadSources = true
}
}
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
// Spigot API
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://oss.sonatype.org/content/repositories/central")
// PacketEvents
maven("https://repo.codemc.io/repository/maven-releases/")
maven("https://repo.codemc.io/repository/maven-snapshots/")
// Placeholder API
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
// CodeMC Repo for bStats
maven("https://repo.codemc.org/repository/maven-public/")
// Auth library from Minecraft
maven("https://libraries.minecraft.net/")
}
group = "kernitus.plugin.OldCombatMechanics"
version = "2.5.0-beta" // x-release-please-version
description = "OldCombatMechanics"
java {
toolchain {
// We can build with Java 17 but still support MC >=1.9
// This is because MC >=1.9 server can be run with higher Java versions
languageVersion.set(JavaLanguageVersion.of(17))
}
}
sourceSets {
val integrationTest by creating {
kotlin.setSrcDirs(listOf("src/integrationTest/kotlin"))
resources.setSrcDirs(listOf("src/integrationTest/resources"))
compileClasspath += main.get().output
runtimeClasspath += output + main.get().output
}
}
configurations {
val integrationTestImplementation by getting {
extendsFrom(configurations.implementation.get())
}
create("integrationTestServerPlugins") {
isCanBeConsumed = false
isCanBeResolved = true
}
}
configurations.named("compileClasspath") {
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
configurations.named("integrationTestCompileClasspath") {
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
dependencies {
implementation("org.bstats:bstats-bukkit:3.1.0")
// Shaded in by Bukkit
compileOnly("io.netty:netty-all:4.1.130.Final")
// Placeholder API
compileOnly("me.clip:placeholderapi:2.11.6")
// For BSON file serialisation
implementation("org.mongodb:bson:5.6.2")
// Spigot
compileOnly("org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT")
// JSR-305 annotations (javax.annotation.Nullable)
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
// PacketEvents
implementation("com.github.retrooper:packetevents-spigot:2.11.2")
// XSeries
implementation("com.github.cryptomorin:XSeries:13.6.0")
// For ingametesting
// Mojang mappings for NMS
/*
compileOnly("com.mojang:authlib:6.0.54")
paperweight.paperDevBundle("1.19.2-R0.1-SNAPSHOT")
// For reflection remapping
implementation("xyz.jpenilla:reflection-remapper:0.1.3")
*/
// Integration test dependencies
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.3.0")
add("integrationTestImplementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.3.0")
add("integrationTestImplementation", "org.jetbrains.kotlin:kotlin-test:2.3.0")
add("integrationTestImplementation", "org.jetbrains.kotlin:kotlin-reflect:2.3.0")
add("integrationTestImplementation", "io.kotest:kotest-runner-junit5-jvm:5.9.1")
add("integrationTestImplementation", "io.kotest:kotest-assertions-core-jvm:5.9.1")
add("integrationTestImplementation", "net.kyori:adventure-api:4.26.1")
add("integrationTestImplementation", "xyz.jpenilla:reflection-remapper:0.1.3")
add("integrationTestCompileOnly", "org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT")
add("integrationTestCompileOnly", "com.mojang:authlib:6.0.54")
add("integrationTestCompileOnly", "io.netty:netty-all:4.1.130.Final")
}
// Substitute ${pluginVersion} in plugin.yml with version defined above
class ExpandPluginVersionAction(
private val version: String,
) : Action<FileCopyDetails>,
Serializable {
override fun execute(details: FileCopyDetails) {
details.expand(mapOf("pluginVersion" to version))
}
}
val pluginVersion = project.version.toString()
val expandPluginVersionAction = ExpandPluginVersionAction(pluginVersion)
tasks.named<Copy>("processResources") {
inputs.property("pluginVersion", pluginVersion)
filesMatching("plugin.yml", expandPluginVersionAction)
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.release.set(8)
}
val shadowJarTask =
tasks.named<ShadowJar>("shadowJar") {
dependsOn("jar")
archiveFileName.set("${project.name}.jar")
dependencies {
exclude(dependency("org.jetbrains.kotlin:.*"))
relocate("org.bstats", "kernitus.plugin.OldCombatMechanics.lib.bstats")
relocate("com.cryptomorin.xseries", "kernitus.plugin.OldCombatMechanics.lib.xseries")
relocate("com.github.retrooper.packetevents", "kernitus.plugin.OldCombatMechanics.lib.packetevents.api")
relocate("io.github.retrooper.packetevents", "kernitus.plugin.OldCombatMechanics.lib.packetevents.impl")
}
}
// For ingametesting
/*
tasks.reobfJar {
outputJar.set(File(buildDir, "libs/${project.name}.jar"))
}
*/
tasks.assemble {
// For ingametesting
// dependsOn("reobfJar")
dependsOn("shadowJar")
}
kotlin {
jvmToolchain(17)
}
tasks.withType<KotlinCompile>().configureEach {
compilerOptions.jvmTarget.set(JvmTarget.JVM_1_8)
}
val relocateIntegrationTestClasses =
tasks.register<ShadowJar>("relocateIntegrationTestClasses") {
archiveClassifier.set("tests-relocated")
dependsOn("compileIntegrationTestKotlin")
configurations = emptyList()
from(sourceSets["integrationTest"].output)
relocate("com.github.retrooper.packetevents", "kernitus.plugin.OldCombatMechanics.lib.packetevents.api")
relocate("io.github.retrooper.packetevents", "kernitus.plugin.OldCombatMechanics.lib.packetevents.impl")
}
val integrationTestJarTask =
tasks.register<Jar>("integrationTestJar") {
archiveClassifier.set("tests")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn(relocateIntegrationTestClasses)
from(relocateIntegrationTestClasses.flatMap { it.archiveFile }.map { zipTree(it.asFile) })
project.configurations["integrationTestRuntimeClasspath"].forEach { file: File ->
if (file.name.contains("packetevents", ignoreCase = true)) {
return@forEach
}
from(if (file.isDirectory) file else zipTree(file))
}
exclude("META-INF/*.SF")
exclude("META-INF/*.DSA")
exclude("META-INF/*.RSA")
exclude("META-INF/*.EC")
exclude("META-INF/*.MF")
exclude("module-info.class")
exclude("META-INF/versions/**/module-info.class")
}
val integrationTestMinecraftVersion =
(findProperty("integrationTestMinecraftVersion") as String?) ?: "1.19.2"
val defaultIntegrationTestVersions =
listOf(integrationTestMinecraftVersion, "1.21.11", "1.12", "1.9.4")
.distinct()
val integrationTestVersions: List<String> =
(findProperty("integrationTestVersions") as String?)
?.split(",")
?.map { it.trim() }
?.filter { it.isNotEmpty() }
?.ifEmpty { defaultIntegrationTestVersions }
?: defaultIntegrationTestVersions
val integrationTestJavaVersionLegacy =
(findProperty("integrationTestJavaVersionLegacy") as String?)?.toInt() ?: 17
val integrationTestJavaVersionLegacyPre13 =
(findProperty("integrationTestJavaVersionLegacyPre13") as String?)?.toInt() ?: 8
val integrationTestJavaVersionLegacy16 =
(findProperty("integrationTestJavaVersionLegacy16") as String?)?.toInt() ?: 11
val integrationTestJavaVersionModern =
(findProperty("integrationTestJavaVersionModern") as String?)?.toInt() ?: 25
fun parseMinecraftVersion(version: String): Triple<Int, Int, Int> {
val parts = version.split(".")
val major = parts.getOrNull(0)?.toIntOrNull() ?: 0
val minor = parts.getOrNull(1)?.toIntOrNull() ?: 0
val patch = parts.getOrNull(2)?.toIntOrNull() ?: 0
return Triple(major, minor, patch)
}
fun needsLegacyVanillaJar(version: String): Boolean {
val (major, minor, _) = parseMinecraftVersion(version)
return major == 1 && minor <= 12
}
fun requiresModernJava(version: String): Boolean {
val (major, minor, patch) = parseMinecraftVersion(version)
if (major > 1) return true
if (minor > 20) return true
return minor == 20 && patch >= 5
}
fun requiredJavaVersion(version: String): Int {
if (needsLegacyVanillaJar(version)) return integrationTestJavaVersionLegacyPre13
val (_, minor, _) = parseMinecraftVersion(version)
if (minor <= 16) return integrationTestJavaVersionLegacy16
return if (requiresModernJava(version)) integrationTestJavaVersionModern else integrationTestJavaVersionLegacy
}
data class KotestSummary(
val specsPassed: Int?,
val specsFailed: Int?,
val specsTotal: Int?,
val testsPassed: Int?,
val testsFailed: Int?,
val testsIgnored: Int?,
val testsTotal: Int?,
val failures: List<String>,
val failureDetails: List<String>,
)
fun parseKotestSummary(logFile: File): KotestSummary? {
if (!logFile.exists()) return null
val lines = logFile.readLines()
var inFailures = false
var blockContext: String? = null // "specs" or "tests"
var specsPassed: Int? = null
var specsFailed: Int? = null
var specsTotal: Int? = null
var testsPassed: Int? = null
var testsFailed: Int? = null
var testsIgnored: Int? = null
var testsTotal: Int? = null
val failures = mutableListOf<String>()
val failureDetails = mutableListOf<String>()
val numberLine = Regex("^(\\d+) (passed|failed|ignored|total)$")
val inlineSpecsLine = Regex("^Specs:\\s*(\\d+) passed,\\s*(\\d+) failed,\\s*(\\d+) total$")
val inlineTestsLine = Regex("^Tests:\\s*(\\d+) passed,\\s*(\\d+) failed,\\s*(\\d+) ignored,\\s*(\\d+) total$")
val stackTopLine = Regex("^\\s*[^\\s].*\\(([^)]+:\\d+)\\)\\s*$")
var lastTestName: String? = null
var pendingFailureName: String? = null
var pendingFailureMessage: String? = null
for (raw in lines) {
val line = raw.substringAfter("]:", raw).trim()
when {
line.startsWith(">> There were test failures") -> {
inFailures = true
blockContext = null
}
line.startsWith("Specs:") -> {
inFailures = false
val inline = inlineSpecsLine.matchEntire(line)
if (inline != null) {
specsPassed = inline.groupValues[1].toInt()
specsFailed = inline.groupValues[2].toInt()
specsTotal = inline.groupValues[3].toInt()
blockContext = null
} else {
blockContext = "specs"
}
}
line.startsWith("Tests:") -> {
inFailures = false
val inline = inlineTestsLine.matchEntire(line)
if (inline != null) {
testsPassed = inline.groupValues[1].toInt()
testsFailed = inline.groupValues[2].toInt()
testsIgnored = inline.groupValues[3].toInt()
testsTotal = inline.groupValues[4].toInt()
blockContext = null
} else {
blockContext = "tests"
}
}
inFailures -> {
val cleaned = line.removePrefix("-").trim()
if (cleaned.isNotBlank()) {
failures.add(cleaned)
}
}
line.startsWith("- ") -> {
lastTestName = line.removePrefix("-").trim()
}
line == "FAILED" -> {
pendingFailureName = lastTestName
pendingFailureMessage = null
}
pendingFailureName != null && pendingFailureMessage == null && line.isNotBlank() -> {
// First line after FAILED is usually the assertion message.
pendingFailureMessage = line
}
pendingFailureName != null && pendingFailureMessage != null -> {
val match = stackTopLine.matchEntire(line)
if (match != null) {
val location = match.groupValues[1]
val name = pendingFailureName!!
val message = pendingFailureMessage!!
failureDetails.add("$name: $message ($location)")
pendingFailureName = null
pendingFailureMessage = null
}
}
blockContext != null -> {
val match = numberLine.matchEntire(line)
if (match != null) {
val value = match.groupValues[1].toInt()
when (blockContext) {
"specs" -> {
when (match.groupValues[2]) {
"passed" -> specsPassed = value
"failed" -> specsFailed = value
"total" -> specsTotal = value
}
}
"tests" -> {
when (match.groupValues[2]) {
"passed" -> testsPassed = value
"failed" -> testsFailed = value
"ignored" -> testsIgnored = value
"total" -> testsTotal = value
}
}
}
}
}
}
}
if (
specsPassed == null && specsFailed == null && specsTotal == null &&
testsPassed == null && testsFailed == null && testsIgnored == null && testsTotal == null &&
failures.isEmpty()
) {
return null
}
return KotestSummary(
specsPassed,
specsFailed,
specsTotal,
testsPassed,
testsFailed,
testsIgnored,
testsTotal,
failures,
failureDetails.distinct(),
)
}
fun sha1(file: File): String {
val digest = MessageDigest.getInstance("SHA-1")
file.inputStream().use { input ->
val buffer = ByteArray(8192)
while (true) {
val read = input.read(buffer)
if (read <= 0) break
digest.update(buffer, 0, read)
}
}
return digest.digest().joinToString("") { "%02x".format(it) }
}
tasks.register("integrationTest") {
group = "verification"
description = "Runs integration tests against all configured Paper versions."
dependsOn("integrationTestMatrix")
}
tasks.named<RunServer>("runServer") {
enabled = false
description = "Disabled. Use integrationTest/integrationTestMatrix instead."
}
tasks.withType<RunServer>().configureEach {
notCompatibleWithConfigurationCache("run-paper tasks access Project at execution time.")
}
val integrationTestMatrixTasks = mutableListOf<TaskProvider<Task>>()
var previousMatrixTask: TaskProvider<Task>? = null
val kotestSpecFilterProvider = providers.systemProperty("kotest.filter.specs")
val kotestTestFilterProvider = providers.systemProperty("kotest.filter.tests")
fun versionTaskSuffix(version: String): String = version.replace(Regex("[^A-Za-z0-9]"), "_")
for (version in integrationTestVersions) {
val suffix = versionTaskSuffix(version)
val runDir = file("run/$version")
val resultFile = runDir.resolve("plugins/OldCombatMechanicsTest/test-results.txt")
val failuresFile = runDir.resolve("plugins/OldCombatMechanicsTest/test-failures.txt")
val vanillaCacheFile = runDir.resolve("cache/mojang_$version.jar")
val logFile = layout.buildDirectory.file("integration-test-logs/$suffix.log")
val writePropsTask =
tasks.register<WriteProperties>("writeProperties$suffix") {
encoding = "UTF-8"
property("online-mode", false)
destinationFile.set(runDir.resolve("server.properties"))
}
val downloadVanillaTask =
if (needsLegacyVanillaJar(version)) {
tasks.register("downloadVanilla$suffix") {
outputs.file(vanillaCacheFile)
notCompatibleWithConfigurationCache("Downloads vanilla server jar for legacy Paper versions.")
doLast {
val slurper = JsonSlurper()
val manifestText =
URI("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json")
.toURL()
.readText()
val manifest = slurper.parseText(manifestText) as Map<*, *>
val versionsList =
manifest["versions"] as? List<Map<*, *>>
?: throw GradleException("Invalid Mojang manifest format: missing 'versions' list.")
val versionEntry =
versionsList.firstOrNull { it["id"] == version }
?: throw GradleException("Minecraft version '$version' not found in Mojang manifest.")
val versionUrl = versionEntry["url"] as String
val versionMetaText = URI(versionUrl).toURL().readText()
val versionMeta = slurper.parseText(versionMetaText) as Map<*, *>
val downloads = versionMeta["downloads"] as Map<*, *>
val serverInfo = downloads["server"] as Map<*, *>
val serverUrl = serverInfo["url"] as String
val serverSha1 = serverInfo["sha1"] as String
if (vanillaCacheFile.exists()) {
val existingSha1 = sha1(vanillaCacheFile)
if (existingSha1.equals(serverSha1, ignoreCase = true)) {
return@doLast
}
} else {
vanillaCacheFile.parentFile.mkdirs()
}
val tmpFile = Files.createTempFile("mc-server-$version-", ".jar").toFile()
URI(serverUrl).toURL().openStream().use { input ->
tmpFile.outputStream().use { output -> input.copyTo(output) }
}
val downloadedSha1 = sha1(tmpFile)
if (!downloadedSha1.equals(serverSha1, ignoreCase = true)) {
tmpFile.delete()
throw GradleException(
"Downloaded Minecraft server jar hash mismatch for $version. Expected $serverSha1, got $downloadedSha1.",
)
}
tmpFile.copyTo(vanillaCacheFile, overwrite = true)
tmpFile.delete()
}
}
} else {
null
}
val runServerTask =
tasks.register<RunServer>("runServer$suffix") {
dependsOn(writePropsTask)
downloadVanillaTask?.let { dependsOn(it) }
runDirectory.set(runDir)
minecraftVersion(version)
jvmArgs("-Dcom.mojang.eula.agree=true")
kotestSpecFilterProvider.orNull?.takeIf { it.isNotBlank() }?.let {
jvmArgs("-Dkotest.filter.specs=$it")
}
kotestTestFilterProvider.orNull?.takeIf { it.isNotBlank() }?.let {
jvmArgs("-Dkotest.filter.tests=$it")
}
if (needsLegacyVanillaJar(version)) {
// Skip the legacy Paper "outdated build" startup sleep.
jvmArgs("-DIReallyKnowWhatIAmDoingISwear=true")
}
javaLauncher.set(
javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(requiredJavaVersion(version)))
},
)
pluginJars.from(shadowJarTask.flatMap { it.archiveFile })
pluginJars.from(integrationTestJarTask.flatMap { it.archiveFile })
pluginJars.from(configurations["integrationTestServerPlugins"])
doFirst {
val log = logFile.get().asFile
log.parentFile.mkdirs()
val stream = log.outputStream()
standardOutput = stream
errorOutput = stream
}
doLast {
(standardOutput as? Closeable)?.close()
}
doFirst {
if (resultFile.exists()) {
resultFile.delete()
}
if (failuresFile.exists()) {
failuresFile.delete()
}
val ocmConfigFile = runDir.resolve("plugins/OldCombatMechanics/config.yml")
if (ocmConfigFile.exists()) {
ocmConfigFile.delete()
}
}
}
val checkTask =
tasks.register("checkTestResults$suffix") {
doLast {
if (!resultFile.exists()) {
throw GradleException("Test results file not found for $version. Tests may not have run correctly.")
}
val result = resultFile.readText().trim()
val log = logFile.get().asFile
val summary = parseKotestSummary(log)
summary?.let {
val parts = mutableListOf<String>()
if (it.specsTotal != null) {
parts.add("Specs: ${it.specsPassed ?: "?"} passed, ${it.specsFailed ?: "?"} failed, ${it.specsTotal} total")
}
if (it.testsTotal != null) {
parts.add(
"Tests: ${it.testsPassed ?: "?"} passed, ${it.testsFailed ?: "?"} failed, ${it.testsIgnored ?: 0} ignored, ${it.testsTotal} total",
)
}
if (it.failures.isNotEmpty()) {
parts.add("Failures: ${it.failures.joinToString(", ")}")
}
if (it.failureDetails.isNotEmpty()) {
parts.add("Reasons: ${it.failureDetails.take(2).joinToString("; ")}")
}
if (parts.isNotEmpty()) {
logger.lifecycle("[$version] ${parts.joinToString(" | ")}")
}
} ?: run {
val rel = log.relativeToOrNull(project.layout.projectDirectory.asFile)?.path ?: log.absolutePath
logger.lifecycle("[$version] No Kotest summary parsed. Full log: $rel")
}
run {
val rel = log.relativeToOrNull(project.layout.projectDirectory.asFile)?.path ?: log.absolutePath
logger.lifecycle("[$version] Log: $rel")
}
if (failuresFile.exists()) {
val lines = failuresFile.readLines().map { it.trim() }.filter { it.isNotEmpty() }
if (lines.isNotEmpty()) {
logger.lifecycle("[$version] Failure details: ${lines.take(5).joinToString(" | ")}")
}
}
if (result == "FAIL") {
throw GradleException("Integration tests failed for $version.")
} else if (result != "PASS") {
throw GradleException("Unknown test result for $version: $result")
}
logger.lifecycle("Integration tests passed for $version.")
}
}
val testTask =
tasks.register("integrationTest$suffix") {
group = "verification"
description = "Runs integration tests with a live Paper server ($version)."
dependsOn(shadowJarTask, integrationTestJarTask, runServerTask)
finalizedBy(checkTask)
}
val priorTask = previousMatrixTask
if (priorTask != null) {
// Chain tasks to enforce order and fail fast.
testTask.configure { dependsOn(priorTask) }
}
previousMatrixTask = testTask
integrationTestMatrixTasks.add(testTask)
}
tasks.register("integrationTestMatrix") {
group = "verification"
description = "Runs integration tests against multiple Paper versions."
dependsOn(integrationTestMatrixTasks)
}
val versionStringProvider = providers.provider { project.version.toString() }
val isReleaseProvider = versionStringProvider.map { !it.contains('-') }
val gitShortHashProvider =
providers
.exec {
commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText
.map { it.trim() }
val gitChangelogProvider =
providers
.exec {
commandLine("git", "log", "-1", "--pretty=%B")
}.standardOutput.asText
.map { it.trim() }
val suffixedVersionProvider =
providers.provider {
val version = project.version.toString()
if (!version.contains('-')) {
version
} else {
"$version+${gitShortHashProvider.get()}"
}
}
val changelogProvider = providers.environmentVariable("HANGAR_CHANGELOG").orElse(gitChangelogProvider)
tasks.register("printIsRelease") {
doLast {
println(if (!project.version.toString().contains('-')) "true" else "false")
}
}
hangarPublish {
publications.register("plugin") {
version.set(suffixedVersionProvider)
channel.set(isReleaseProvider.map { if (it) "Release" else "Snapshot" })
id.set("OldCombatMechanics")
apiKey.set(System.getenv("HANGAR_API_TOKEN"))
changelog.set(changelogProvider)
platforms {
register(Platforms.PAPER) {
jar.set(tasks.shadowJar.flatMap { it.archiveFile })
platformVersions.set(paperVersion)
}
}
}
}
================================================
FILE: gradle.properties
================================================
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
gameVersions=1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.2, 1.21.1, 1.21, 1.20.6, 1.20.5, 1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17, 1.16, 1.15, 1.14, 1.13, 1.12, 1.11, 1.10, 1.9
# Disable configuration cache (run-paper tasks are not compatible).
org.gradle.configuration-cache=false
================================================
FILE: gradlew
================================================
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle.kts
================================================
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
rootProject.name = "OldCombatMechanics"
================================================
FILE: src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackCompat.kt
================================================
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package kernitus.plugin.OldCombatMechanics
import kernitus.plugin.OldCombatMechanics.utilities.reflection.Reflector
import org.bukkit.entity.Entity
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import java.lang.reflect.Method
import java.util.concurrent.ConcurrentHashMap
fun attackCompat(attacker: Player, target: Entity) {
val apiAttack = attacker.javaClass.methods.firstOrNull { method ->
method.name == "attack" &&
method.parameterCount == 1 &&
Entity::class.java.isAssignableFrom(method.parameterTypes[0])
}
val useApiAttack = Reflector.versionIsNewerOrEqualTo(1, 12, 0)
if (useApiAttack && apiAttack != null) {
val beforeApiAttack = captureLivingAttackSignal(target)
try {
val apiResult = apiAttack.invoke(attacker, target)
if (apiAttack.returnType == java.lang.Boolean.TYPE && apiResult == false) {
// Explicit attack failure; continue with NMS candidates.
} else if (beforeApiAttack == null) {
return
} else {
val afterApiAttack = captureLivingAttackSignal(target)
if (hasObservableHit(beforeApiAttack, afterApiAttack)) {
return
}
}
} catch (ignored: Exception) {
// Fall through to NMS-based attack.
}
}
// Fall back to NMS attack on legacy servers.
val handleMethod = attacker.javaClass.methods.firstOrNull { method ->
method.name == "getHandle" && method.parameterCount == 0
} ?: error("Failed to resolve CraftPlayer#getHandle for ${attacker.javaClass.name}")
val attackerHandle = handleMethod.invoke(attacker)
?: error("CraftPlayer#getHandle returned null for ${attacker.javaClass.name}")
val targetHandle = target.javaClass.methods.firstOrNull { method ->
method.name == "getHandle" && method.parameterCount == 0
}?.invoke(target) ?: error("Failed to resolve CraftEntity#getHandle for ${target.javaClass.name}")
val nmsAttackMethods = resolveNmsAttackMethods(attackerHandle.javaClass, targetHandle.javaClass)
var falseResultCount = 0
var exceptionCount = 0
val attemptedMethods = ArrayList<String>(nmsAttackMethods.size)
for (method in nmsAttackMethods) {
attemptedMethods += "${method.declaringClass.simpleName}#${method.name}:${method.returnType.simpleName}"
try {
val result = method.invoke(attackerHandle, targetHandle)
if (method.returnType == java.lang.Boolean.TYPE && result == false) {
falseResultCount++
continue
}
return
} catch (ignored: Exception) {
exceptionCount++
// Try the next candidate.
}
}
// Legacy fallback: try Bukkit API even if we prefer NMS (helps 1.12 fake players)
if (!useApiAttack && apiAttack != null) {
runCatching { apiAttack.invoke(attacker, target); return }
}
error(
"Failed to invoke NMS attack for attacker=${attackerHandle.javaClass.name} " +
"target=${targetHandle.javaClass.name} (candidates=${nmsAttackMethods.size}, " +
"falseResults=$falseResultCount, exceptions=$exceptionCount, attempted=$attemptedMethods)"
)
}
private data class LivingAttackSignal(
val health: Double,
val lastDamage: Double,
val noDamageTicks: Int,
)
private fun captureLivingAttackSignal(entity: Entity): LivingAttackSignal? {
val living = entity as? LivingEntity ?: return null
return LivingAttackSignal(
health = living.health,
lastDamage = living.lastDamage,
noDamageTicks = living.noDamageTicks,
)
}
private fun hasObservableHit(before: LivingAttackSignal, after: LivingAttackSignal?): Boolean {
if (after == null) return false
if (after.health < before.health) return true
if (after.noDamageTicks > before.noDamageTicks) return true
return after.lastDamage > 0.0 && after.lastDamage != before.lastDamage
}
private val attackMethodCache = ConcurrentHashMap<Class<*>, List<Method>>()
private fun resolveNmsAttackMethods(attackerHandleClass: Class<*>, targetHandleClass: Class<*>): List<Method> {
return attackMethodCache.computeIfAbsent(attackerHandleClass) {
buildAttackMethodCandidates(attackerHandleClass, targetHandleClass)
}
}
private fun buildAttackMethodCandidates(attackerHandleClass: Class<*>, targetHandleClass: Class<*>): List<Method> {
// Prefer explicit names if they exist, then fall back to signature-based heuristics.
val explicit = listOfNotNull(
Reflector.getMethodAssignable(attackerHandleClass, "attack", targetHandleClass),
Reflector.getMethodAssignable(attackerHandleClass, "a", targetHandleClass),
Reflector.getMethodAssignable(attackerHandleClass, "B", targetHandleClass) // legacy 1.12 variants
)
if (explicit.isNotEmpty()) {
explicit.forEach { it.isAccessible = true }
return explicit
}
val candidates = collectAllMethods(attackerHandleClass)
.asSequence()
.filter { it.parameterCount == 1 }
.filter { it.parameterTypes[0].isAssignableFrom(targetHandleClass) }
.filter { it.returnType == Void.TYPE || it.returnType == java.lang.Boolean.TYPE }
.map { method -> method to scoreAttackMethod(method) }
.sortedByDescending { it.second }
.map { it.first }
.toList()
candidates.forEach { it.isAccessible = true }
return candidates
}
private fun collectAllMethods(start: Class<*>): List<Method> {
val methods = LinkedHashMap<String, Method>()
var current: Class<*>? = start
while (current != null) {
current.declaredMethods.forEach { method ->
methods.putIfAbsent(methodSignature(method), method)
}
current = current.superclass
}
start.methods.forEach { method ->
methods.putIfAbsent(methodSignature(method), method)
}
return methods.values.toList()
}
private fun methodSignature(method: Method): String {
val params = method.parameterTypes.joinToString(",") { it.name }
return "${method.declaringClass.name}#${method.name}($params):${method.returnType.name}"
}
private fun scoreAttackMethod(method: Method): Int {
var score = 0
val name = method.name
val param = method.parameterTypes[0]
val declaring = method.declaringClass.simpleName
if (name == "attack") score += 100
if (name == "a") score += 80
if (param.simpleName == "Entity") score += 40
if (param.simpleName.contains("Entity")) score += 10
if (method.returnType == Void.TYPE) score += 10
if (method.returnType == java.lang.Boolean.TYPE) score += 8
if (declaring.contains("EntityHuman")) score += 25
if (declaring.contains("EntityPlayer")) score += 20
return score
}
================================================
FILE: src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackCooldownHeldItemIntegrationTest.kt
================================================
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package kernitus.plugin.OldCombatMechanics
import com.cryptomorin.xseries.XAttribute
import io.kotest.common.ExperimentalKotest
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.doubles.plusOrMinus
import io.kotest.matchers.shouldBe
import kernitus.plugin.OldCombatMechanics.module.ModuleAttackCooldown
import kernitus.plugin.OldCombatMechanics.utilities.Config
import kernitus.plugin.OldCombatMechanics.utilities.storage.PlayerStorage
import kotlinx.coroutines.delay
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.World
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerChangedWorldEvent
import org.bukkit.event.player.PlayerItemHeldEvent
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerSwapHandItemsEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.java.JavaPlugin
import java.util.concurrent.Callable
@OptIn(ExperimentalKotest::class)
class AttackCooldownHeldItemIntegrationTest :
FunSpec({
val testPlugin = JavaPlugin.getPlugin(OCMTestMain::class.java)
val ocm = JavaPlugin.getPlugin(OCMMain::class.java)
val module = ModuleLoader.getModules().filterIsInstance<ModuleAttackCooldown>().firstOrNull()
extensions(MainThreadDispatcherExtension(testPlugin))
fun <T> runSync(action: () -> T): T =
if (Bukkit.isPrimaryThread()) {
action()
} else {
Bukkit.getScheduler().callSyncMethod(testPlugin, Callable { action() }).get()
}
fun currentAttackSpeed(player: Player): Double {
val attackSpeedAttribute = checkNotNull(XAttribute.ATTACK_SPEED.get()) { "Missing attack speed attribute type" }
val attribute = player.getAttribute(attackSpeedAttribute) ?: error("Missing attack speed attribute")
return attribute.baseValue
}
fun setModeset(
player: Player,
world: World,
modeset: String,
) {
val data = PlayerStorage.getPlayerData(player.uniqueId)
data.setModesetForWorld(world.uid, modeset)
PlayerStorage.setPlayerData(player.uniqueId, data)
}
fun fireJoin(player: Player) {
Bukkit.getPluginManager().callEvent(PlayerJoinEvent(player, "test"))
}
fun switchHotbar(
player: Player,
from: Int,
to: Int,
) {
player.inventory.heldItemSlot = to
Bukkit.getPluginManager().callEvent(PlayerItemHeldEvent(player, from, to))
}
data class SpawnedPlayer(
val fake: FakePlayer,
val player: Player,
)
fun spawnFake(world: World): SpawnedPlayer {
lateinit var fake: FakePlayer
lateinit var player: Player
runSync {
fake = FakePlayer(testPlugin)
fake.spawn(Location(world, 0.0, 100.0, 0.0, 0f, 0f))
player = checkNotNull(Bukkit.getPlayer(fake.uuid))
player.inventory.clear()
player.inventory.heldItemSlot = 0
player.activePotionEffects.forEach { player.removePotionEffect(it.type) }
setModeset(player, world, "old")
}
return SpawnedPlayer(fake, player)
}
fun cleanup(spawnedPlayer: SpawnedPlayer) {
runSync { spawnedPlayer.fake.removePlayer() }
}
suspend fun waitForPossibleDeferredWork() {
delay(2 * 50L)
}
suspend fun withAttackCooldownConfig(
genericAttackSpeed: Double,
heldItemAttackSpeeds: Map<String, Double>,
block: suspend () -> Unit,
) {
val disabledModules = ocm.config.getStringList("disabled_modules")
val alwaysEnabledModules = ocm.config.getStringList("always_enabled_modules")
val modesetsSection = ocm.config.getConfigurationSection("modesets") ?: error("Missing 'modesets' section")
val modesetSnapshot =
modesetsSection.getKeys(false).associateWith { key ->
ocm.config.getStringList("modesets.$key")
}
val genericSnapshot = ocm.config.get("disable-attack-cooldown.generic-attack-speed")
val heldItemSnapshot =
ocm.config.getConfigurationSection("disable-attack-cooldown.held-item-attack-speeds")?.getValues(false)
?: emptyMap<String, Any?>()
fun reloadAll() {
ocm.saveConfig()
Config.reload()
ModuleLoader.toggleModules()
module?.reload()
}
try {
ocm.config.set("disable-attack-cooldown.generic-attack-speed", genericAttackSpeed)
ocm.config.set("disable-attack-cooldown.held-item-attack-speeds", null)
heldItemAttackSpeeds.forEach { (key, value) ->
ocm.config.set("disable-attack-cooldown.held-item-attack-speeds.$key", value)
}
ocm.config.set("disabled_modules", disabledModules.filterNot { it == "disable-attack-cooldown" })
ocm.config.set("always_enabled_modules", alwaysEnabledModules.filterNot { it == "disable-attack-cooldown" })
val oldModeset =
ocm.config.getStringList("modesets.old").toMutableList().apply {
if (!contains("disable-attack-cooldown")) add("disable-attack-cooldown")
}
val newModeset =
ocm.config.getStringList("modesets.new").toMutableList().apply {
remove("disable-attack-cooldown")
}
ocm.config.set("modesets.old", oldModeset)
ocm.config.set("modesets.new", newModeset)
reloadAll()
block()
} finally {
ocm.config.set("disabled_modules", disabledModules)
ocm.config.set("always_enabled_modules", alwaysEnabledModules)
modesetSnapshot.forEach { (key, list) -> ocm.config.set("modesets.$key", list) }
ocm.config.set("disable-attack-cooldown.generic-attack-speed", genericSnapshot)
ocm.config.set("disable-attack-cooldown.held-item-attack-speeds", null)
heldItemSnapshot.forEach { (key, value) ->
ocm.config.set("disable-attack-cooldown.held-item-attack-speeds.$key", value)
}
reloadAll()
}
}
test("applies configured held-item attack speeds and falls back to the generic value on hotbar switch") {
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
try {
runSync {
spawned.player.inventory.setItem(0, ItemStack(Material.IRON_SWORD))
spawned.player.inventory.setItem(1, ItemStack(Material.STICK))
spawned.player.inventory.heldItemSlot = 0
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
runSync { switchHotbar(spawned.player, from = 0, to = 1) }
runSync { currentAttackSpeed(spawned.player) } shouldBe (12.0 plusOrMinus 0.01)
} finally {
cleanup(spawned)
}
}
}
test("materials without an explicit held-item entry use disable-attack-cooldown.generic-attack-speed") {
withAttackCooldownConfig(
genericAttackSpeed = 13.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
try {
runSync {
spawned.player.inventory.setItemInMainHand(ItemStack(Material.STICK))
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (13.0 plusOrMinus 0.01)
} finally {
cleanup(spawned)
}
}
}
test("world and modeset transitions restore vanilla 4.0 when disabled and reapply the held-item target when re-enabled") {
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val otherWorld = checkNotNull(Bukkit.getWorld("world_nether"))
val spawned = spawnFake(world)
try {
runSync {
spawned.player.inventory.setItemInMainHand(ItemStack(Material.IRON_SWORD))
setModeset(spawned.player, world, "old")
setModeset(spawned.player, otherWorld, "new")
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
runSync {
setModeset(spawned.player, world, "new")
module?.onModesetChange(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (4.0 plusOrMinus 0.01)
runSync {
setModeset(spawned.player, world, "old")
module?.onModesetChange(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
runSync {
spawned.player.teleport(Location(otherWorld, 0.0, 100.0, 0.0, 0f, 0f))
Bukkit.getPluginManager().callEvent(PlayerChangedWorldEvent(spawned.player, world))
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (4.0 plusOrMinus 0.01)
runSync {
spawned.player.teleport(Location(world, 0.0, 100.0, 0.0, 0f, 0f))
Bukkit.getPluginManager().callEvent(PlayerChangedWorldEvent(spawned.player, otherWorld))
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
} finally {
cleanup(spawned)
}
}
}
test("user-added material keys are accepted when the running server recognises the material") {
val material = Material.matchMaterial("MACE") ?: return@test
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf(material.name to 7.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
try {
runSync {
spawned.player.inventory.setItemInMainHand(ItemStack(material))
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (7.0 plusOrMinus 0.01)
} finally {
cleanup(spawned)
}
}
}
test("main-hand attack speed follows hand swaps and uses the newly held item") {
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
try {
runSync {
spawned.player.inventory.setItemInMainHand(ItemStack(Material.STICK))
spawned.player.inventory.setItemInOffHand(ItemStack(Material.IRON_SWORD))
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (12.0 plusOrMinus 0.01)
runSync {
val swap =
PlayerSwapHandItemsEvent(
spawned.player,
spawned.player.inventory.itemInMainHand,
spawned.player.inventory.itemInOffHand,
)
Bukkit.getPluginManager().callEvent(swap)
spawned.player.inventory.setItemInMainHand(swap.offHandItem)
spawned.player.inventory.setItemInOffHand(swap.mainHandItem)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
} finally {
cleanup(spawned)
}
}
}
test("cancelled hotbar changes keep attack speed tied to the actually held item") {
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
val canceller =
object : Listener {
@EventHandler(priority = EventPriority.LOWEST)
fun onHeld(event: PlayerItemHeldEvent) {
if (event.player.uniqueId == spawned.player.uniqueId) {
event.isCancelled = true
}
}
}
try {
runSync {
Bukkit.getPluginManager().registerEvents(canceller, testPlugin)
spawned.player.inventory.setItem(0, ItemStack(Material.IRON_SWORD))
spawned.player.inventory.setItem(1, ItemStack(Material.STICK))
spawned.player.inventory.heldItemSlot = 0
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
runSync {
val event = PlayerItemHeldEvent(spawned.player, 0, 1)
Bukkit.getPluginManager().callEvent(event)
}
runSync { spawned.player.inventory.heldItemSlot } shouldBe 0
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
} finally {
runSync { HandlerList.unregisterAll(canceller) }
cleanup(spawned)
}
}
}
test("cancelled hand swaps keep attack speed tied to the actual main-hand item") {
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
val canceller =
object : Listener {
@EventHandler(priority = EventPriority.LOWEST)
fun onSwap(event: PlayerSwapHandItemsEvent) {
if (event.player.uniqueId == spawned.player.uniqueId) {
event.isCancelled = true
}
}
}
try {
runSync {
Bukkit.getPluginManager().registerEvents(canceller, testPlugin)
spawned.player.inventory.setItemInMainHand(ItemStack(Material.IRON_SWORD))
spawned.player.inventory.setItemInOffHand(ItemStack(Material.STICK))
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
runSync {
val event =
PlayerSwapHandItemsEvent(
spawned.player,
spawned.player.inventory.itemInMainHand,
spawned.player.inventory.itemInOffHand,
)
Bukkit.getPluginManager().callEvent(event)
}
runSync { spawned.player.inventory.itemInMainHand.type } shouldBe Material.IRON_SWORD
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
} finally {
runSync { HandlerList.unregisterAll(canceller) }
cleanup(spawned)
}
}
}
test("later inventory changes after a hotbar event do not trigger deferred attack-speed reconciliation") {
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
try {
runSync {
spawned.player.inventory.setItem(0, ItemStack(Material.IRON_SWORD))
spawned.player.inventory.setItem(1, ItemStack(Material.STICK))
spawned.player.inventory.heldItemSlot = 0
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
runSync {
val event = PlayerItemHeldEvent(spawned.player, 0, 1)
Bukkit.getPluginManager().callEvent(event)
spawned.player.inventory.heldItemSlot = 1
spawned.player.inventory.heldItemSlot = 0
}
waitForPossibleDeferredWork()
runSync { spawned.player.inventory.heldItemSlot } shouldBe 0
runSync { spawned.player.inventory.itemInMainHand.type } shouldBe Material.IRON_SWORD
runSync { currentAttackSpeed(spawned.player) } shouldBe (12.0 plusOrMinus 0.01)
} finally {
cleanup(spawned)
}
}
}
test("unchanged post-swap inventory keeps the swap-applied attack speed without deferred re-checking") {
withAttackCooldownConfig(
genericAttackSpeed = 12.0,
heldItemAttackSpeeds = mapOf("IRON_SWORD" to 19.0),
) {
val world = checkNotNull(Bukkit.getWorld("world"))
val spawned = spawnFake(world)
try {
runSync {
spawned.player.inventory.setItemInMainHand(ItemStack(Material.STICK))
spawned.player.inventory.setItemInOffHand(ItemStack(Material.IRON_SWORD))
fireJoin(spawned.player)
}
runSync { currentAttackSpeed(spawned.player) } shouldBe (12.0 plusOrMinus 0.01)
runSync {
val event =
PlayerSwapHandItemsEvent(
spawned.player,
spawned.player.inventory.itemInMainHand,
spawned.player.inventory.itemInOffHand,
)
Bukkit.getPluginManager().callEvent(event)
spawned.player.inventory.setItemInMainHand(event.offHandItem)
spawned.player.inventory.setItemInOffHand(event.mainHandItem)
}
waitForPossibleDeferredWork()
runSync { spawned.player.inventory.itemInMainHand.type } shouldBe Material.IRON_SWORD
runSync { currentAttackSpeed(spawned.player) } shouldBe (19.0 plusOrMinus 0.01)
} finally {
cleanup(spawned)
}
}
}
})
================================================
FILE: src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackCooldownTrackerIntegrationTest.kt
================================================
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package kernitus.plugin.OldCombatMechanics
import io.kotest.common.ExperimentalKotest
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.doubles.shouldBeGreaterThanOrEqual
import io.kotest.matchers.doubles.shouldBeLessThanOrEqual
import io.kotest.matchers.shouldBe
import kernitus.plugin.OldCombatMechanics.utilities.damage.AttackCooldownTracker
import kernitus.plugin.OldCombatMechanics.utilities.reflection.Reflector
import kotlinx.coroutines.delay
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.plugin.java.JavaPlugin
import java.util.concurrent.Callable
@OptIn(ExperimentalKotest::class)
class AttackCooldownTrackerIntegrationTest : FunSpec({
val testPlugin = JavaPlugin.getPlugin(OCMTestMain::class.java)
fun <T> runSync(action: () -> T): T {
return if (Bukkit.isPrimaryThread()) action() else Bukkit.getScheduler().callSyncMethod(testPlugin, Callable {
action()
}).get()
}
extensions(MainThreadDispatcherExtension(testPlugin))
test("attack cooldown tracking is only active where required") {
val isModern = Reflector.versionIsNewerOrEqualTo(1, 16, 0)
val uuid = runSync {
val world = Bukkit.getWorld("world") ?: error("world not loaded")
val location = Location(world, 0.0, 120.0, 0.0, 0f, 0f)
val fp = FakePlayer(testPlugin)
fp.spawn(location)
fp.uuid
}
try {
// Let at least one tick elapse so any scheduled tracker has a chance to populate.
delay(2 * 50L)
val last = runSync { AttackCooldownTracker.getLastCooldown(uuid) }
if (isModern) {
last shouldBe null
} else {
val value = (last ?: error("Expected a cached cooldown value on legacy servers")).toDouble()
value.shouldBeGreaterThanOrEqual(0.0)
value.shouldBeLessThanOrEqual(1.0)
}
} finally {
// Ensure we remove the fake player regardless of assertions.
runSync {
val player = Bukkit.getPlayer(uuid)
player?.let {
// FakePlayer removal fires a quit event; this is enough to validate map cleanup on legacy servers.
it.kickPlayer("test")
}
}
delay(2 * 50L)
// Legacy servers: tracker should remove on quit. Modern servers: tracker should remain inactive and return null.
val afterQuit = runSync { AttackCooldownTracker.getLastCooldown(uuid) }
afterQuit shouldBe null
}
}
})
================================================
FILE: src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackRangeIntegrationTest.kt
================================================
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package kernitus.plugin.OldCombatMechanics
import io.kotest.common.ExperimentalKotest
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.doubles.shouldBeLessThan
import io.kotest.matchers.shouldBe
import kernitus.plugin.OldCombatMechanics.module.ModuleAttackRange
import kernitus.plugin.OldCombatMechanics.utilities.reflection.Reflector
import org.bukkit.Bukkit
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.entity.EntityType
import org.bukkit.entity.Player
import org.bukkit.entity.Zombie
import org.bukkit.event.player.PlayerDropItemEvent
import org.bukkit.event.player.PlayerItemHeldEvent
import org.bukkit.event.player.PlayerSwapHandItemsEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.java.JavaPlugin
import java.util.concurrent.Callable
/**
* Behavioural reach tests for the attack-range module.
*
* The module extends melee reach; we assert a hit just outside vanilla reach succeeds
* when enabled, but the same swing misses when disabled. A control swing inside vanilla
* reach must hit in both cases.
*/
@OptIn(ExperimentalKotest::class)
class AttackRangeIntegrationTest :
FunSpec({
val testPlugin = JavaPlugin.getPlugin(OCMTestMain::class.java)
val ocm = JavaPlugin.getPlugin(OCMMain::class.java)
val attackRangeModule = ModuleLoader.getModules().filterIsInstance<ModuleAttackRange>().firstOrNull()
val applyMethod =
attackRangeModule?.javaClass?.getDeclaredMethod("applyToHeld", Player::class.java)?.apply {
isAccessible = true
}
fun hasAttackRange(stack: ItemStack?): Boolean =
try {
if (stack == null) return false
val dctClass = Class.forName("io.papermc.paper.datacomponent.DataComponentTypes")
val typeField = dctClass.getField("ATTACK_RANGE")
val type = typeField.get(null)
val baseTypeClass = Class.forName("io.papermc.paper.datacomponent.DataComponentType")
val getter = ItemStack::class.java.getMethod("getData", baseTypeClass)
getter.invoke(stack, type) != null
} catch (t: Throwable) {
false
}
extensions(MainThreadDispatcherExtension(testPlugin))
fun runSync(action: () -> Unit) {
if (Bukkit.isPrimaryThread()) {
action()
} else {
Bukkit
.getScheduler()
.callSyncMethod(
testPlugin,
gitextract_5hk29j6x/
├── .github/
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-bug-report.yaml
│ │ ├── 2-question.yaml
│ │ ├── 3-feature.yaml
│ │ └── config.yml
│ ├── release-please-config.json
│ ├── release-please-manifest.json
│ └── workflows/
│ ├── build-upload-release.yml
│ ├── dev-builds.yml
│ ├── release-please.yml
│ └── wrap-issue-form-codeblocks.yml
├── .gitignore
├── AGENTS.md
├── CHANGELOG.md
├── LICENCE
├── README.md
├── build.gradle.kts
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src/
├── integrationTest/
│ ├── kotlin/
│ │ └── kernitus/
│ │ └── plugin/
│ │ └── OldCombatMechanics/
│ │ ├── AttackCompat.kt
│ │ ├── AttackCooldownHeldItemIntegrationTest.kt
│ │ ├── AttackCooldownTrackerIntegrationTest.kt
│ │ ├── AttackRangeIntegrationTest.kt
│ │ ├── AttributeModifierCompat.kt
│ │ ├── ChorusFruitIntegrationTest.kt
│ │ ├── ConfigMigrationIntegrationTest.kt
│ │ ├── ConsumableComponentIntegrationTest.kt
│ │ ├── CopperToolsIntegrationTest.kt
│ │ ├── CustomWeaponDamageIntegrationTest.kt
│ │ ├── DisableOffhandIntegrationTest.kt
│ │ ├── DisableOffhandReflectionIntegrationTest.kt
│ │ ├── EnderpearlCooldownIntegrationTest.kt
│ │ ├── FakePlayer.kt
│ │ ├── FireAspectOverdamageIntegrationTest.kt
│ │ ├── FishingRodVelocityIntegrationTest.kt
│ │ ├── GoldenAppleIntegrationTest.kt
│ │ ├── InGameTester.kt
│ │ ├── InGameTesterIntegrationTest.kt
│ │ ├── InvulnerabilityDamageIntegrationTest.kt
│ │ ├── KotestRunner.kt
│ │ ├── LegacyFakePlayer12.kt
│ │ ├── LegacyFakePlayer9.kt
│ │ ├── ModesetRulesIntegrationTest.kt
│ │ ├── OCMTest.kt
│ │ ├── OCMTestMain.kt
│ │ ├── OldArmourDurabilityIntegrationTest.kt
│ │ ├── OldCriticalHitsIntegrationTest.kt
│ │ ├── OldPotionEffectsIntegrationTest.kt
│ │ ├── OldToolDamageMobIntegrationTest.kt
│ │ ├── PacketCancellationIntegrationTest.kt
│ │ ├── PaperSwordBlockingDamageReductionIntegrationTest.kt
│ │ ├── PlayerKnockbackIntegrationTest.kt
│ │ ├── PlayerRegenIntegrationTest.kt
│ │ ├── SpigotFunctionChooserIntegrationTest.kt
│ │ ├── SwordBlockingIntegrationTest.kt
│ │ ├── SwordSweepIntegrationTest.kt
│ │ ├── Tally.kt
│ │ ├── TestResultWriter.kt
│ │ ├── TesterUtils.kt
│ │ ├── ToolDamageTooltipIntegrationTest.kt
│ │ └── WeaponDurabilityIntegrationTest.kt
│ └── resources/
│ └── plugin.yml
└── main/
├── java/
│ └── kernitus/
│ └── plugin/
│ └── OldCombatMechanics/
│ ├── ModuleLoader.java
│ ├── OCMConfigHandler.java
│ ├── OCMMain.java
│ ├── UpdateChecker.java
│ ├── commands/
│ │ ├── OCMCommandCompleter.java
│ │ └── OCMCommandHandler.java
│ ├── hooks/
│ │ ├── PlaceholderAPIHook.java
│ │ └── api/
│ │ └── Hook.java
│ ├── module/
│ │ ├── ModuleAttackCooldown.java
│ │ ├── ModuleAttackFrequency.java
│ │ ├── ModuleAttackRange.java
│ │ ├── ModuleAttackSounds.java
│ │ ├── ModuleChorusFruit.java
│ │ ├── ModuleDisableCrafting.java
│ │ ├── ModuleDisableEnderpearlCooldown.java
│ │ ├── ModuleDisableOffHand.java
│ │ ├── ModuleFishingKnockback.java
│ │ ├── ModuleFishingRodVelocity.java
│ │ ├── ModuleGoldenApple.java
│ │ ├── ModuleOldArmourDurability.java
│ │ ├── ModuleOldArmourStrength.java
│ │ ├── ModuleOldBrewingStand.java
│ │ ├── ModuleOldBurnDelay.java
│ │ ├── ModuleOldCriticalHits.java
│ │ ├── ModuleOldPotionEffects.java
│ │ ├── ModuleOldToolDamage.java
│ │ ├── ModulePlayerKnockback.java
│ │ ├── ModulePlayerRegen.java
│ │ ├── ModuleProjectileKnockback.java
│ │ ├── ModuleShieldDamageReduction.java
│ │ ├── ModuleSwordBlocking.java
│ │ ├── ModuleSwordSweep.java
│ │ ├── ModuleSwordSweepParticles.java
│ │ └── OCMModule.java
│ ├── paper/
│ │ └── PaperSwordBlocking.java
│ ├── updater/
│ │ ├── ModuleUpdateChecker.java
│ │ ├── SpigetUpdateChecker.java
│ │ └── VersionChecker.java
│ └── utilities/
│ ├── Config.java
│ ├── ConfigUtils.java
│ ├── EventRegistry.java
│ ├── MathsHelper.java
│ ├── Messenger.java
│ ├── TextUtils.java
│ ├── damage/
│ │ ├── AttackCooldownTracker.java
│ │ ├── DamageUtils.java
│ │ ├── DefenceUtils.java
│ │ ├── EntityDamageByEntityListener.java
│ │ ├── MobDamage.java
│ │ ├── NewWeaponDamage.java
│ │ ├── OCMEntityDamageByEntityEvent.java
│ │ └── WeaponDamages.java
│ ├── potions/
│ │ ├── PotionDurations.java
│ │ ├── PotionEffects.java
│ │ ├── PotionKey.java
│ │ └── WeaknessCompensation.java
│ ├── reflection/
│ │ ├── Reflector.java
│ │ ├── SpigotFunctionChooser.java
│ │ └── VersionCompatUtils.java
│ └── storage/
│ ├── ModesetListener.java
│ ├── PlayerData.java
│ ├── PlayerDataCodec.java
│ └── PlayerStorage.java
└── resources/
├── config.yml
└── plugin.yml
SYMBOL INDEX (662 symbols across 63 files)
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/ModuleLoader.java
class ModuleLoader (line 15) | public class ModuleLoader {
method initialise (line 20) | public static void initialise(OCMMain plugin) {
method toggleModules (line 25) | public static void toggleModules() {
method setState (line 29) | private static void setState(OCMModule module, boolean state) {
method addModule (line 41) | public static void addModule(OCMModule module) {
method getModules (line 45) | public static List<OCMModule> getModules() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/OCMConfigHandler.java
class OCMConfigHandler (line 26) | public class OCMConfigHandler {
method OCMConfigHandler (line 30) | public OCMConfigHandler(OCMMain instance) {
method upgradeConfig (line 34) | public void upgradeConfig() {
method setupConfigIfNotPresent (line 95) | public void setupConfigIfNotPresent() {
method migrateModuleLists (line 102) | private void migrateModuleLists(YamlConfiguration oldConfig, YamlConfi...
method getConfig (line 207) | public YamlConfiguration getConfig(String fileName) {
method getFile (line 211) | public File getFile(String fileName) {
method doesConfigExist (line 215) | public boolean doesConfigExist() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/OCMMain.java
class OCMMain (line 43) | public class OCMMain extends JavaPlugin {
method OCMMain (line 51) | public OCMMain() {
method onLoad (line 55) | @Override
method onEnable (line 61) | @Override
method onDisable (line 164) | @Override
method registerModules (line 200) | private void registerModules() {
method registerHooks (line 262) | private void registerHooks() {
method upgradeConfig (line 267) | public void upgradeConfig() {
method doesConfigExist (line 271) | public boolean doesConfigExist() {
method addDisableListener (line 280) | public void addDisableListener(Runnable action) {
method addEnableListener (line 289) | public void addEnableListener(Runnable action) {
method getFile (line 298) | @NotNull
method getInstance (line 304) | public static OCMMain getInstance() {
method getVersion (line 308) | public static String getVersion() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/UpdateChecker.java
class UpdateChecker (line 20) | public class UpdateChecker {
method UpdateChecker (line 25) | public UpdateChecker(OCMMain plugin) {
method performUpdate (line 36) | public void performUpdate() {
method performUpdate (line 40) | public void performUpdate(@Nullable Player player) {
method update (line 47) | private void update(Consumer<String> target) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/commands/OCMCommandCompleter.java
class OCMCommandCompleter (line 28) | public class OCMCommandCompleter implements TabCompleter {
method onTabComplete (line 30) | @Nullable
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/commands/OCMCommandHandler.java
class OCMCommandHandler (line 27) | public class OCMCommandHandler implements CommandExecutor {
type Subcommand (line 32) | enum Subcommand {
method OCMCommandHandler (line 36) | public OCMCommandHandler(OCMMain instance) {
method help (line 40) | private void help(OCMMain plugin, CommandSender sender) {
method reload (line 57) | private void reload(CommandSender sender) {
method mode (line 62) | private void mode(CommandSender sender, String[] args) {
method onCommand (line 149) | public boolean onCommand(@NotNull CommandSender sender, @NotNull Comma...
class CommandNotRecognisedException (line 179) | private static class CommandNotRecognisedException extends IllegalArgu...
method checkPermissions (line 182) | static boolean checkPermissions(CommandSender sender, Subcommand subco...
method checkPermissions (line 186) | static boolean checkPermissions(CommandSender sender, Subcommand subco...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/hooks/PlaceholderAPIHook.java
class PlaceholderAPIHook (line 18) | public class PlaceholderAPIHook implements Hook {
method init (line 21) | @Override
method deinit (line 93) | @Override
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/hooks/api/Hook.java
type Hook (line 10) | public interface Hook {
method init (line 11) | void init(OCMMain plugin);
method deinit (line 13) | void deinit(OCMMain plugin);
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackCooldown.java
class ModuleAttackCooldown (line 30) | public class ModuleAttackCooldown extends OCMModule {
method ModuleAttackCooldown (line 37) | public ModuleAttackCooldown(OCMMain plugin) {
method reload (line 41) | @Override
method onPlayerLogin (line 53) | @EventHandler(priority = EventPriority.HIGH)
method onWorldChange (line 58) | @EventHandler(priority = EventPriority.HIGH)
method onHotbarChange (line 63) | @EventHandler(priority = EventPriority.HIGHEST)
method onSwapHandItems (line 72) | @EventHandler(priority = EventPriority.HIGHEST)
method onPlayerQuit (line 81) | @EventHandler(priority = EventPriority.HIGH)
method adjustAttackSpeed (line 92) | private void adjustAttackSpeed(Player player) {
method adjustAttackSpeed (line 96) | private void adjustAttackSpeed(Player player, ItemStack mainHand) {
method onModesetChange (line 104) | @Override
method getConfiguredAttackSpeed (line 109) | private double getConfiguredAttackSpeed(ItemStack itemStack) {
method setAttackSpeed (line 123) | public void setAttackSpeed(Player player, double attackSpeed) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackFrequency.java
class ModuleAttackFrequency (line 23) | public class ModuleAttackFrequency extends OCMModule {
method ModuleAttackFrequency (line 28) | public ModuleAttackFrequency(OCMMain plugin) {
method reload (line 33) | @Override
method onPlayerJoin (line 46) | @EventHandler
method onPlayerLogout (line 52) | @EventHandler
method onPlayerChangeWorld (line 57) | @EventHandler
method onPlayerRespawn (line 63) | @EventHandler
method setDelay (line 69) | private void setDelay(Player player, int delay) {
method onCreatureSpawn (line 74) | @EventHandler
method onEntityTeleportEvent (line 81) | @EventHandler
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackRange.java
class ModuleAttackRange (line 36) | public class ModuleAttackRange extends OCMModule implements Listener {
method ModuleAttackRange (line 50) | public ModuleAttackRange(OCMMain plugin) {
method initialiseReflection (line 57) | private void initialiseReflection() {
method reload (line 71) | @Override
method onJoin (line 86) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onHotbar (line 91) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onSwap (line 98) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method normaliseSwapEvent (line 104) | private void normaliseSwapEvent(PlayerSwapHandItemsEvent event) {
method reconcileSwapInventory (line 118) | private void reconcileSwapInventory(Player player) {
method applyToHeld (line 133) | private void applyToHeld(Player player) {
method applyToItem (line 140) | private void applyToItem(Player player, ItemStack item) {
method cleanHand (line 152) | private void cleanHand(Player player, int slot) {
method isWeapon (line 157) | private boolean isWeapon(Material material) {
method applyAttackRange (line 162) | private void applyAttackRange(ItemStack item) {
method stripComponent (line 166) | private void stripComponent(ItemStack item) {
method registerCleanerListener (line 171) | private void registerCleanerListener(Plugin plugin) {
class CleanerListener (line 179) | private class CleanerListener implements Listener {
method onHeldChange (line 180) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onSwap (line 185) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onDrop (line 190) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onDeath (line 195) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onQuit (line 200) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onWorldChange (line 206) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
class PaperAttackRangeAdapter (line 217) | private static class PaperAttackRangeAdapter {
method PaperAttackRangeAdapter (line 237) | PaperAttackRangeAdapter() throws Exception {
method findSetDataMethod (line 267) | private Method findSetDataMethod(Class<?> dctClass, Class<?> valueCl...
method apply (line 280) | void apply(ItemStack stack, float min, float max, float minCreative,...
method invokeSetter (line 300) | private void invokeSetter(Method setter, Object builder, float value...
method hasComponent (line 308) | boolean hasComponent(ItemStack stack) {
method clear (line 316) | void clear(ItemStack stack) {
method ensureServerConversions (line 327) | private void ensureServerConversions(ItemStack stack) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackSounds.java
class ModuleAttackSounds (line 30) | public class ModuleAttackSounds extends OCMModule {
method ModuleAttackSounds (line 35) | public ModuleAttackSounds(OCMMain plugin) {
method reload (line 41) | @Override
method getBlockedSounds (line 52) | private Collection<String> getBlockedSounds() {
class SoundListener (line 87) | private class SoundListener extends PacketListenerAbstract {
method onPacketSend (line 90) | @Override
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleChorusFruit.java
class ModuleChorusFruit (line 27) | public class ModuleChorusFruit extends OCMModule {
method ModuleChorusFruit (line 29) | public ModuleChorusFruit(OCMMain plugin) {
method onEat (line 33) | @EventHandler
method onTeleport (line 63) | @EventHandler
method getMaxTeleportationDistance (line 123) | private double getMaxTeleportationDistance() {
method isSafe (line 127) | private boolean isSafe(Location location) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleDisableCrafting.java
class ModuleDisableCrafting (line 24) | public class ModuleDisableCrafting extends OCMModule {
method ModuleDisableCrafting (line 29) | public ModuleDisableCrafting(OCMMain plugin) {
method reload (line 34) | @Override
method onPrepareItemCraft (line 40) | @EventHandler(priority = EventPriority.HIGHEST)
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleDisableEnderpearlCooldown.java
class ModuleDisableEnderpearlCooldown (line 28) | public class ModuleDisableEnderpearlCooldown extends OCMModule {
method ModuleDisableEnderpearlCooldown (line 40) | public ModuleDisableEnderpearlCooldown(OCMMain plugin) {
method reload (line 46) | public void reload() {
method getInstance (line 58) | public static ModuleDisableEnderpearlCooldown getInstance() {
method onPlayerShoot (line 62) | @EventHandler(priority = EventPriority.HIGHEST)
method isEnderPearl (line 120) | private boolean isEnderPearl(ItemStack itemStack) {
method onPlayerQuit (line 124) | @EventHandler
method getEnderpearlCooldown (line 134) | public long getEnderpearlCooldown(UUID playerUUID) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleDisableOffHand.java
class ModuleDisableOffHand (line 37) | public class ModuleDisableOffHand extends OCMModule {
method ModuleDisableOffHand (line 50) | public ModuleDisableOffHand(OCMMain plugin) {
method reload (line 55) | @Override
method sendDeniedMessage (line 62) | private void sendDeniedMessage(CommandSender sender) {
method onSwapHandItems (line 67) | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
method onInventoryClick (line 76) | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
method onInventoryDrag (line 156) | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
method onWorldChange (line 170) | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
method onModesetChange (line 175) | @Override
method isItemBlocked (line 191) | private boolean isItemBlocked(ItemStack item) {
type BlockType (line 199) | private enum BlockType {
method BlockType (line 205) | BlockType(BiPredicate<Collection<Material>, Material> filter) {
method isAllowed (line 216) | boolean isAllowed(Collection<Material> list, Material toCheck) {
method not (line 221) | private static <T, U> BiPredicate<T, U> not(BiPredicate<T, U> predicat...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleFishingKnockback.java
class ModuleFishingKnockback (line 23) | public class ModuleFishingKnockback extends OCMModule {
method ModuleFishingKnockback (line 29) | public ModuleFishingKnockback(OCMMain plugin) {
method reload (line 45) | @Override
method onRodLand (line 50) | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
method calculateKnockbackVelocity (line 106) | private Vector calculateKnockbackVelocity(Vector currentVelocity, Loca...
method onReelIn (line 141) | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleFishingRodVelocity.java
class ModuleFishingRodVelocity (line 31) | public class ModuleFishingRodVelocity extends OCMModule {
method ModuleFishingRodVelocity (line 43) | public ModuleFishingRodVelocity(OCMMain plugin) {
method reload (line 48) | @Override
method onFishEvent (line 62) | @EventHandler (ignoreCancelled = true)
method ensureGravityTask (line 106) | private void ensureGravityTask() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleGoldenApple.java
class ModuleGoldenApple (line 39) | public class ModuleGoldenApple extends OCMModule {
method ModuleGoldenApple (line 59) | public ModuleGoldenApple(OCMMain plugin) {
method reload (line 64) | @SuppressWarnings("deprecated")
method getInstance (line 94) | public static ModuleGoldenApple getInstance() {
method registerCrafting (line 98) | private void registerCrafting() {
method onPrepareItemCraft (line 107) | @EventHandler(priority = EventPriority.HIGH)
method onItemConsume (line 129) | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
method applyEffects (line 205) | private void applyEffects(LivingEntity target, List<PotionEffect> newE...
method createEnchantedGoldenApple (line 242) | @SuppressWarnings("deprecation")
method isEnchantedGoldenApple (line 253) | private boolean isEnchantedGoldenApple(ItemStack item) {
method getPotionEffects (line 257) | private List<PotionEffect> getPotionEffects(String path) {
method onPlayerQuit (line 281) | @EventHandler
method getGappleCooldown (line 295) | public long getGappleCooldown(UUID playerUUID) {
method getNappleCooldown (line 312) | public long getNappleCooldown(UUID playerUUID) {
class LastEaten (line 322) | private static class LastEaten {
method getForItem (line 326) | private Optional<Instant> getForItem(ItemStack item) {
method getNewestEatTime (line 332) | private Optional<Instant> getNewestEatTime() {
method setForItem (line 343) | private void setForItem(ItemStack item) {
class Cooldown (line 352) | private static class Cooldown {
method Cooldown (line 357) | private Cooldown(long normal, long enchanted, boolean sharedCooldown) {
method getCooldownForItem (line 363) | private long getCooldownForItem(ItemStack item) {
method isOnCooldown (line 367) | private boolean isOnCooldown(ItemStack item, LastEaten lastEaten) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldArmourDurability.java
class ModuleOldArmourDurability (line 24) | public class ModuleOldArmourDurability extends OCMModule {
method ModuleOldArmourDurability (line 38) | public ModuleOldArmourDurability(OCMMain plugin) {
method onItemDamage (line 42) | @EventHandler(priority = EventPriority.LOWEST)
method onPlayerExplosionDamage (line 85) | @EventHandler(priority = EventPriority.MONITOR)
method ensureExplosionCleanupTaskRunning (line 102) | private void ensureExplosionCleanupTaskRunning() {
method stopExplosionCleanupTaskIfIdle (line 125) | private void stopExplosionCleanupTaskIfIdle() {
class ExplosionDamagedArmour (line 132) | private static final class ExplosionDamagedArmour {
method ExplosionDamagedArmour (line 136) | private ExplosionDamagedArmour(List<ItemStack> armour, long expiresA...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldArmourStrength.java
class ModuleOldArmourStrength (line 28) | public class ModuleOldArmourStrength extends OCMModule {
method ModuleOldArmourStrength (line 33) | public ModuleOldArmourStrength(OCMMain plugin) {
method reload (line 38) | @Override
method onEntityDamage (line 43) | @SuppressWarnings("deprecation")
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldBrewingStand.java
class ModuleOldBrewingStand (line 20) | public class ModuleOldBrewingStand extends OCMModule {
method ModuleOldBrewingStand (line 22) | public ModuleOldBrewingStand(OCMMain plugin) {
method onInventoryOpen (line 26) | @EventHandler
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldBurnDelay.java
class ModuleOldBurnDelay (line 16) | public class ModuleOldBurnDelay extends OCMModule {
method ModuleOldBurnDelay (line 20) | public ModuleOldBurnDelay(OCMMain plugin) {
method reload (line 25) | @Override
method onFireTick (line 30) | @EventHandler
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldCriticalHits.java
class ModuleOldCriticalHits (line 14) | public class ModuleOldCriticalHits extends OCMModule {
method ModuleOldCriticalHits (line 19) | public ModuleOldCriticalHits(OCMMain plugin) {
method reload (line 24) | @Override
method onOCMDamage (line 30) | @EventHandler
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldPotionEffects.java
class ModuleOldPotionEffects (line 52) | public class ModuleOldPotionEffects extends OCMModule {
method ModuleOldPotionEffects (line 80) | public ModuleOldPotionEffects(OCMMain plugin) {
method reload (line 86) | @Override
method onPlayerDrinksPotion (line 97) | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
method onPotionDispense (line 109) | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
method onPotionThrow (line 121) | @EventHandler(priority = EventPriority.HIGHEST)
method onModesetChange (line 137) | @Override
method updatePotionEffectListener (line 146) | private void updatePotionEffectListener() {
method ensurePotionEffectListener (line 155) | private void ensurePotionEffectListener() {
method unregisterPotionEffectListener (line 197) | private void unregisterPotionEffectListener() {
method resolveEntityPotionEffectEvent (line 208) | private Class<?> resolveEntityPotionEffectEvent() {
method handleEntityPotionEffectEvent (line 216) | private void handleEntityPotionEffectEvent(Event event) {
method extractEntity (line 242) | private Entity extractEntity(Event event) {
method extractPotionEffect (line 254) | private PotionEffect extractPotionEffect(Event event, Method accessor) {
method adjustPotion (line 271) | private void adjustPotion(ItemStack potionItem, boolean splash) {
method onDamageByEntity (line 332) | @EventHandler(ignoreCancelled = true)
method getPotionDuration (line 357) | private Integer getPotionDuration(PotionKey potionKey, boolean splash) {
method detectWeaknessAmplifierClamp (line 367) | private boolean detectWeaknessAmplifierClamp() {
method syncWeaknessCompensation (line 388) | private void syncWeaknessCompensation() {
method removeWeaknessCompensation (line 401) | private void removeWeaknessCompensation() {
method applyWeaknessCompensation (line 409) | private void applyWeaknessCompensation(LivingEntity entity) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldToolDamage.java
class ModuleOldToolDamage (line 35) | public class ModuleOldToolDamage extends OCMModule {
method ModuleOldToolDamage (line 58) | public ModuleOldToolDamage(OCMMain plugin) {
method reload (line 65) | @Override
method onEntityDamaged (line 78) | @EventHandler(ignoreCancelled = true)
method isWeapon (line 150) | private boolean isWeapon(Material material) {
method isOfType (line 156) | private boolean isOfType(Material mat, String type) {
method onTridentProjectile (line 160) | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
method shouldApplyTooltip (line 172) | private boolean shouldApplyTooltip(Player player) {
method formatDamage (line 177) | private String formatDamage(double value) {
method removeExistingTooltip (line 184) | private List<String> removeExistingTooltip(List<String> lore) {
method applyTooltip (line 197) | private void applyTooltip(Player player, ItemStack item) {
method stripTooltip (line 222) | private void stripTooltip(ItemStack item) {
class TooltipListener (line 240) | private class TooltipListener implements Listener {
method onJoin (line 241) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onHotbar (line 246) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onSwap (line 252) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method applyToHeld (line 260) | void applyToHeld(Player player) {
method cleanHand (line 265) | private void cleanHand(Player player, int slot) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModulePlayerKnockback.java
class ModulePlayerKnockback (line 40) | public class ModulePlayerKnockback extends OCMModule {
method ModulePlayerKnockback (line 59) | public ModulePlayerKnockback(OCMMain plugin) {
method reload (line 64) | @Override
method onPlayerQuit (line 75) | @EventHandler
method onPlayerVelocityEvent (line 84) | @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
method onEntityDamage (line 93) | @EventHandler
method onEntityDamageEntity (line 119) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method ensureCleanupTaskRunning (line 206) | private void ensureCleanupTaskRunning() {
method stopCleanupTaskIfIdle (line 230) | private void stopCleanupTaskIfIdle() {
class PendingKnockback (line 237) | private static final class PendingKnockback {
method PendingKnockback (line 241) | private PendingKnockback(Vector velocity, long expiresAtTick) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModulePlayerRegen.java
class ModulePlayerRegen (line 30) | public class ModulePlayerRegen extends OCMModule {
method ModulePlayerRegen (line 47) | public ModulePlayerRegen(OCMMain plugin) {
method reload (line 52) | @Override
method onRegen (line 66) | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
method onPlayerQuit (line 122) | @EventHandler
method ensureTickTaskRunning (line 128) | private void ensureTickTaskRunning() {
method stopTickTaskIfIdle (line 139) | private void stopTickTaskIfIdle() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleProjectileKnockback.java
class ModuleProjectileKnockback (line 20) | public class ModuleProjectileKnockback extends OCMModule {
method ModuleProjectileKnockback (line 22) | public ModuleProjectileKnockback(OCMMain plugin) {
method onEntityHit (line 26) | @EventHandler(priority = EventPriority.NORMAL)
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleShieldDamageReduction.java
class ModuleShieldDamageReduction (line 28) | public class ModuleShieldDamageReduction extends OCMModule {
method ModuleShieldDamageReduction (line 42) | public ModuleShieldDamageReduction(OCMMain plugin) {
method reload (line 47) | @Override
method onItemDamage (line 55) | @EventHandler(priority = EventPriority.LOWEST)
method onHit (line 77) | @EventHandler(priority = EventPriority.LOWEST)
method ensureFullyBlockedCleanupTaskRunning (line 112) | private void ensureFullyBlockedCleanupTaskRunning() {
method stopFullyBlockedCleanupTaskIfIdle (line 135) | private void stopFullyBlockedCleanupTaskIfIdle() {
class FullyBlockedArmour (line 142) | private static final class FullyBlockedArmour {
method FullyBlockedArmour (line 146) | private FullyBlockedArmour(List<ItemStack> armour, long expiresAtTic...
method getDamageReduction (line 152) | private double getDamageReduction(double damage, DamageCause damageCau...
method shieldBlockedDamage (line 170) | private boolean shieldBlockedDamage(double attackDamage, double blocki...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleSwordBlocking.java
class ModuleSwordBlocking (line 36) | public class ModuleSwordBlocking extends OCMModule {
method ModuleSwordBlocking (line 76) | public ModuleSwordBlocking(OCMMain plugin) {
method reload (line 90) | @Override
method onModesetChange (line 111) | @Override
method initialisePaperAdapter (line 124) | private void initialisePaperAdapter() {
method initialisePacketEventsClientVersion (line 159) | private void initialisePacketEventsClientVersion() {
method initialiseLegacyShieldMarker (line 185) | private void initialiseLegacyShieldMarker() {
method supportsPaperAnimation (line 218) | private boolean supportsPaperAnimation(Player player) {
method isPaperDataComponentApiPresent (line 255) | private boolean isPaperDataComponentApiPresent() {
method onBlockPlace (line 264) | @EventHandler(priority = EventPriority.HIGHEST)
method onRightClick (line 282) | @EventHandler(priority = EventPriority.HIGHEST)
method onRightClickEntity (line 305) | @EventHandler(priority = EventPriority.HIGHEST)
method onRightClickEntityAt (line 310) | @EventHandler(priority = EventPriority.HIGHEST)
method handleEntityRightClick (line 315) | private void handleEntityRightClick(Player player, org.bukkit.entity.E...
method markEntityInteractionHandled (line 323) | private boolean markEntityInteractionHandled(Player player, org.bukkit...
method lazyPruneHandledEntityInteractions (line 338) | private void lazyPruneHandledEntityInteractions(long nowNanos, boolean...
method doShieldBlock (line 350) | private void doShieldBlock(Player player) {
method onHotBarChange (line 402) | @EventHandler
method onWorldChange (line 407) | @EventHandler(priority = EventPriority.HIGHEST)
method onPlayerJoin (line 412) | @EventHandler(priority = EventPriority.HIGHEST)
method onPlayerLogout (line 418) | @EventHandler(priority = EventPriority.HIGHEST)
method onPlayerDeath (line 423) | @EventHandler(priority = EventPriority.HIGHEST)
method firstShieldDropIndex (line 472) | private int firstShieldDropIndex(List<ItemStack> drops) {
method onPlayerSwapHandItems (line 482) | @EventHandler(priority = EventPriority.HIGHEST)
method onInventoryClick (line 495) | @EventHandler(priority = EventPriority.HIGHEST)
method onItemDrop (line 511) | @EventHandler(priority = EventPriority.HIGHEST)
method isTemporaryOffhandShieldClick (line 523) | private boolean isTemporaryOffhandShieldClick(InventoryClickEvent even...
method isSwapOffhandClick (line 532) | private boolean isSwapOffhandClick(InventoryClickEvent event) {
method createTemporaryLegacyShield (line 540) | private ItemStack createTemporaryLegacyShield() {
method markTemporaryLegacyShield (line 546) | private void markTemporaryLegacyShield(ItemStack item) {
method canMarkTemporaryLegacyShield (line 565) | private boolean canMarkTemporaryLegacyShield() {
method isTemporaryLegacyShieldDrop (line 573) | private boolean isTemporaryLegacyShieldDrop(ItemStack item) {
method hasTemporaryLegacyShieldMarker (line 583) | private boolean hasTemporaryLegacyShieldMarker(ItemStack item) {
method restore (line 601) | private void restore(Player player) {
method restore (line 605) | private void restore(Player p, boolean force) {
method restore (line 609) | private void restore(Player p, boolean force, boolean fromLegacyTask) {
method scheduleLegacyRestore (line 639) | private void scheduleLegacyRestore(Player p) {
method areItemsStored (line 647) | private boolean areItemsStored(UUID uuid) {
method isPlayerBlocking (line 654) | private boolean isPlayerBlocking(Player player) {
method hasShield (line 661) | private boolean hasShield(PlayerInventory inventory) {
method isHoldingSword (line 665) | private boolean isHoldingSword(Material mat) {
method getInstance (line 669) | public static ModuleSwordBlocking getInstance() {
method applyPaperBlockingReduction (line 680) | public double applyPaperBlockingReduction(org.bukkit.event.entity.Enti...
method isPaperSwordBlocking (line 697) | public boolean isPaperSwordBlocking(Player player) {
method applyConsumableComponent (line 716) | private boolean applyConsumableComponent(Player player, ItemStack item) {
method startUsingMainHandIfSupported (line 729) | private void startUsingMainHandIfSupported(Player player) {
method startUsingItemIfSupported (line 733) | private void startUsingItemIfSupported(Player player, EquipmentSlot sl...
method startUsingItemNmsIfSupported (line 757) | private void startUsingItemNmsIfSupported(Player player, boolean offha...
method resolveNmsStartUsingItem (line 792) | private java.lang.reflect.Method resolveNmsStartUsingItem(Class<?> han...
method enumHasConstant (line 835) | private boolean enumHasConstant(Class<?> enumClass, String name) {
method enumConstantByName (line 839) | private Object enumConstantByName(Class<?> enumClass, String name) {
method stripConsumable (line 851) | private boolean stripConsumable(ItemStack item) {
method hasConsumableComponent (line 863) | private boolean hasConsumableComponent(ItemStack item) {
method shouldHandleConsumable (line 873) | private boolean shouldHandleConsumable(Player player) {
method isPaperAnimationEnabled (line 877) | private boolean isPaperAnimationEnabled() {
method sweepConsumableState (line 881) | private void sweepConsumableState(Player player, boolean includeStorag...
method stripConsumableState (line 933) | private void stripConsumableState(Player player, boolean includeStorag...
method isUnknownClientVersion (line 961) | private boolean isUnknownClientVersion(Object clientVersion) {
class ConsumableLifecycleHandler (line 967) | private class ConsumableLifecycleHandler implements Listener {
method onHeld (line 968) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onSwap (line 983) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onDrop (line 1016) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onDeath (line 1022) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onQuit (line 1028) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method onWorldChange (line 1033) | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
method ensureLegacyTaskRunning (line 1039) | private void ensureLegacyTaskRunning() {
method stopLegacyTaskIfIdle (line 1091) | private void stopLegacyTaskIfIdle() {
class LegacySwordBlockState (line 1098) | private static final class LegacySwordBlockState {
class EntityInteractionKey (line 1103) | private static final class EntityInteractionKey {
method EntityInteractionKey (line 1108) | private EntityInteractionKey(UUID playerId, UUID entityId, Equipment...
method equals (line 1114) | @Override
method hashCode (line 1124) | @Override
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleSwordSweep.java
class ModuleSwordSweep (line 30) | public class ModuleSwordSweep extends OCMModule {
method ModuleSwordSweep (line 38) | public ModuleSwordSweep(OCMMain plugin) {
method reload (line 51) | @Override
method onEntityDamaged (line 65) | @EventHandler(priority = EventPriority.LOWEST)
method onSwordAttack (line 88) | private void onSwordAttack(EntityDamageByEntityEvent e, Player attacke...
method scheduleClearNextTickIfNeeded (line 116) | private void scheduleClearNextTickIfNeeded() {
method isHoldingSword (line 124) | private boolean isHoldingSword(Material mat) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleSwordSweepParticles.java
class ModuleSwordSweepParticles (line 22) | public class ModuleSwordSweepParticles extends OCMModule {
method ModuleSwordSweepParticles (line 26) | public ModuleSwordSweepParticles(OCMMain plugin) {
method reload (line 32) | @Override
class ParticleListener (line 43) | private class ParticleListener extends PacketListenerAbstract {
method onPacketSend (line 47) | @Override
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/module/OCMModule.java
class OCMModule (line 28) | public abstract class OCMModule implements Listener {
method OCMModule (line 41) | protected OCMModule(OCMMain plugin, String configName) {
method isEnabled (line 52) | public boolean isEnabled() {
method isEnabled (line 62) | public boolean isEnabled(World world) {
method isEnabled (line 69) | public boolean isEnabled(@NotNull HumanEntity humanEntity) {
method isEnabled (line 90) | public boolean isEnabled(@NotNull Entity entity) {
method isEnabled (line 104) | public boolean isEnabled(@NotNull Entity attacker, @NotNull Entity def...
method isSettingEnabled (line 116) | public boolean isSettingEnabled(String name) {
method module (line 125) | public ConfigurationSection module() {
method reload (line 133) | public void reload() {
method onModesetChange (line 143) | public void onModesetChange(Player player) {
method debug (line 152) | protected void debug(String text) {
method debug (line 162) | protected void debug(String text, CommandSender sender) {
method toString (line 168) | @Override
method getModuleName (line 181) | public String getModuleName() {
method getConfigName (line 185) | public String getConfigName() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/paper/PaperSwordBlocking.java
class PaperSwordBlocking (line 25) | public class PaperSwordBlocking {
method PaperSwordBlocking (line 61) | public PaperSwordBlocking() throws Exception {
method resolveCraftItemStackHandleField (line 216) | private Field resolveCraftItemStackHandleField(ItemStack stack) throws...
method findPatchSetMethod (line 234) | private Method findPatchSetMethod(Class<?> builderClass) throws NoSuch...
method findPatchRemoveMethod (line 243) | private Method findPatchRemoveMethod(Class<?> builderClass) throws NoS...
method findItemStackIsMethod (line 252) | private Method findItemStackIsMethod(Class<?> nmsItemStackClass, Objec...
method applyComponents (line 274) | public void applyComponents(ItemStack stack) {
method applyComponentsInternal (line 278) | private void applyComponentsInternal(ItemStack stack, boolean allowTes...
method clearComponents (line 334) | public void clearComponents(ItemStack stack) {
method findItemStackSetMethod (line 360) | private Method findItemStackSetMethod(Class<?> nmsItemStackClass, Clas...
method findItemStackRemoveMethod (line 370) | private Method findItemStackRemoveMethod(Class<?> nmsItemStackClass, C...
method hasConsumableComponent (line 380) | public boolean hasConsumableComponent(ItemStack stack) {
method ensureServerConversions (line 405) | private void ensureServerConversions(ItemStack stack) {
method setPaperDataValue (line 416) | private boolean setPaperDataValue(ItemStack stack, Object value) {
method setPaperDataBuilder (line 431) | private boolean setPaperDataBuilder(ItemStack stack, Object builder) {
method buildPaperConsumableBuilder (line 446) | private Object buildPaperConsumableBuilder() {
method buildPaperConsumableValue (line 471) | private Object buildPaperConsumableValue() {
method syncTestInventories (line 481) | private void syncTestInventories(ItemStack stack) {
method isTestServer (line 502) | private boolean isTestServer() {
method isBlockingSword (line 506) | public boolean isBlockingSword(Player player) {
method isSword (line 519) | private boolean isSword(Material mat) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/updater/ModuleUpdateChecker.java
class ModuleUpdateChecker (line 16) | public class ModuleUpdateChecker extends OCMModule {
method ModuleUpdateChecker (line 18) | public ModuleUpdateChecker(OCMMain plugin) {
method onPlayerLogin (line 22) | @EventHandler
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/updater/SpigetUpdateChecker.java
class SpigetUpdateChecker (line 26) | public class SpigetUpdateChecker {
method isUpdateAvailable (line 40) | public boolean isUpdateAvailable() {
method getUpdateURL (line 61) | public String getUpdateURL() {
method getLatestVersion (line 80) | public String getLatestVersion() {
method downloadLatestVersion (line 91) | public boolean downloadLatestVersion(File updateFolderFile, String fil...
method getVersions (line 123) | private List<VersionPojo> getVersions(String urlString) {
method fetchPage (line 144) | private InputStreamReader fetchPage(String urlString) throws IOExcepti...
class VersionPojo (line 169) | private static class VersionPojo {
method getName (line 181) | String getName() {
method getId (line 190) | String getId() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/updater/VersionChecker.java
class VersionChecker (line 13) | public class VersionChecker {
method shouldUpdate (line 15) | public static boolean shouldUpdate(String remoteVersion) {
method isUpdateOut (line 19) | private static boolean isUpdateOut(String remoteVersion, String localV...
method getVersionNumbers (line 31) | private static int[] getVersionNumbers(String ver) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/Config.java
class Config (line 25) | public class Config {
method initialise (line 44) | public static void initialise(OCMMain plugin) {
method checkConfigVersion (line 53) | private static boolean checkConfigVersion() {
method reload (line 67) | public static void reload() {
method reloadModesets (line 107) | private static void reloadModesets() {
method reloadWorlds (line 219) | private static void reloadWorlds() {
method addWorld (line 235) | public static void addWorld(World world){
method addWorld (line 240) | public static void addWorld(World world, ConfigurationSection worldsSe...
method removeWorld (line 249) | public static void removeWorld(World world){
method getDefaultModeset (line 258) | public static @Nullable Set<String> getDefaultModeset(UUID worldId){
method moduleEnabled (line 280) | public static boolean moduleEnabled(String moduleName, World world) {
method moduleEnabled (line 301) | public static boolean moduleEnabled(String moduleName) {
method debugEnabled (line 305) | public static boolean debugEnabled() {
method moduleSettingEnabled (line 309) | public static boolean moduleSettingEnabled(String moduleName, String m...
method getConfig (line 318) | public static FileConfiguration getConfig() {
method getModesets (line 322) | public static Map<String, Set<String>> getModesets(){
method isModuleAlwaysEnabled (line 326) | public static boolean isModuleAlwaysEnabled(String moduleName) {
method isModuleDisabled (line 330) | public static boolean isModuleDisabled(String moduleName) {
method isModuleInAnyModeset (line 334) | public static boolean isModuleInAnyModeset(String moduleName) {
method getWorlds (line 338) | public static Map<UUID, Set<String>> getWorlds() {
method normaliseModuleName (line 342) | private static String normaliseModuleName(String moduleName) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/ConfigUtils.java
class ConfigUtils (line 24) | public class ConfigUtils {
method loadDoubleMap (line 35) | public static Map<String, Double> loadDoubleMap(ConfigurationSection s...
method loadMaterialDoubleMap (line 50) | public static Map<Material, Double> loadMaterialDoubleMap(Configuratio...
method loadMaterialList (line 80) | public static List<Material> loadMaterialList(ConfigurationSection sec...
method warnUnknownMaterial (line 103) | private static void warnUnknownMaterial(String fullKey, String name) {
method warnUnknownMaterialMap (line 110) | private static void warnUnknownMaterialMap(String fullKey, String name) {
method matchMaterial (line 117) | private static Material matchMaterial(String name) {
method isUnknownMaterialKey (line 126) | private static boolean isUnknownMaterialKey(String name) {
method loadPotionDurationsList (line 137) | public static HashMap<PotionKey, PotionDurations> loadPotionDurationsL...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/EventRegistry.java
class EventRegistry (line 18) | public class EventRegistry {
method EventRegistry (line 22) | public EventRegistry(Plugin plugin) {
method registerListener (line 32) | public boolean registerListener(Listener listener) {
method unregisterListener (line 46) | public boolean unregisterListener(Listener listener) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/MathsHelper.java
class MathsHelper (line 11) | public class MathsHelper {
method clamp (line 21) | public static double clamp(double value, double min, double max) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/Messenger.java
class Messenger (line 15) | public class Messenger {
method initialise (line 23) | public static void initialise(OCMMain plugin) {
method reloadConfig (line 27) | public static void reloadConfig(boolean debugEnabled, String prefix){
method info (line 32) | public static void info(String message, Object... args) {
method warn (line 36) | public static void warn(Throwable e, String message, Object... args) {
method warn (line 40) | public static void warn(String message, Object... args) {
method sendNoPrefix (line 53) | public static void sendNoPrefix(CommandSender sender, String message, ...
method sendWithPrefix (line 71) | private static void sendWithPrefix(CommandSender sender, String messag...
method send (line 77) | public static void send(CommandSender sender, String message, Object.....
method sendDebugMessage (line 81) | private static void sendDebugMessage(CommandSender sender, String mess...
method debug (line 85) | public static void debug(String message, Throwable throwable) {
method debug (line 89) | public static void debug(String message, Object... args) {
method debug (line 93) | public static void debug(CommandSender sender, String message, Object....
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/TextUtils.java
class TextUtils (line 10) | public class TextUtils {
method colourise (line 17) | public static String colourise(String text) {
method stripColour (line 27) | public static String stripColour(String text) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/AttackCooldownTracker.java
class AttackCooldownTracker (line 21) | public class AttackCooldownTracker extends OCMModule {
method AttackCooldownTracker (line 25) | public AttackCooldownTracker(OCMMain plugin) {
method onPlayerQuit (line 48) | @EventHandler
method getLastCooldown (line 53) | public static Float getLastCooldown(UUID uuid) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/DamageUtils.java
class DamageUtils (line 17) | public class DamageUtils {
method getAttackCooldown (line 46) | private static float getAttackCooldown(HumanEntity humanEntity){
method getNewSharpnessDamage (line 61) | public static double getNewSharpnessDamage(int level) {
method getOldSharpnessDamage (line 71) | public static double getOldSharpnessDamage(int level) {
method isCriticalHit1_8 (line 81) | public static boolean isCriticalHit1_8(HumanEntity humanEntity) {
method isCriticalHit1_9 (line 95) | public static boolean isCriticalHit1_9(Player player) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/DefenceUtils.java
class DefenceUtils (line 41) | public class DefenceUtils {
method calculateDefenceDamageReduction (line 88) | @SuppressWarnings("deprecation")
method getDamageAfterArmour1_8 (line 165) | public static double getDamageAfterArmour1_8(LivingEntity defender, do...
method getAttributeModifierSum (line 212) | private static double getAttributeModifierSum(Collection<AttributeModi...
method calculateArmourEnchantmentReductionFactor (line 231) | private static double calculateArmourEnchantmentReductionFactor(ItemSt...
method warnOnUnknownArmourEnchantments (line 263) | private static void warnOnUnknownArmourEnchantments(ItemStack armourIt...
method shouldWarnOnUnknownEnchantment (line 283) | private static boolean shouldWarnOnUnknownEnchantment(Enchantment ench...
method initVanillaEnchantments (line 295) | private static Set<Enchantment> initVanillaEnchantments() {
method isVanillaEnchantment (line 306) | private static boolean isVanillaEnchantment(Enchantment enchantment) {
method isModelledArmourEnchantment (line 310) | private static boolean isModelledArmourEnchantment(Enchantment enchant...
type EnchantmentType (line 319) | private enum EnchantmentType {
method EnchantmentType (line 370) | EnchantmentType(Supplier<Set<EntityDamageEvent.DamageCause>> protect...
method protectsAgainst (line 383) | public boolean protectsAgainst(EntityDamageEvent.DamageCause cause) {
method getEnchantment (line 392) | public Enchantment getEnchantment() {
method getEpf (line 402) | public int getEpf(int level) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/EntityDamageByEntityListener.java
class EntityDamageByEntityListener (line 24) | public class EntityDamageByEntityListener extends OCMModule {
method EntityDamageByEntityListener (line 35) | public EntityDamageByEntityListener(OCMMain plugin) {
method getINSTANCE (line 42) | public static EntityDamageByEntityListener getINSTANCE() {
method isEnabled (line 46) | @Override
method setEnabled (line 51) | public void setEnabled(boolean enabled) {
method startExpirySweeperIfNeeded (line 62) | private void startExpirySweeperIfNeeded() {
method stopExpirySweeperIfNeeded (line 71) | private void stopExpirySweeperIfNeeded() {
method touchExpiry (line 77) | private void touchExpiry(LivingEntity damagee) {
method sweepExpiredEntries (line 89) | private void sweepExpiredEntries() {
method onEntityDamage (line 100) | @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
method afterEntityDamage (line 257) | @EventHandler(priority = EventPriority.MONITOR)
method restoreLastDamage (line 292) | private void restoreLastDamage(LivingEntity damagee) {
method checkOverdamage (line 307) | private double checkOverdamage(LivingEntity livingDamagee, EntityDamag...
method resolveStoredDamage (line 358) | private Double resolveStoredDamage(LivingEntity damagee) {
method clearStoredDamage (line 369) | private void clearStoredDamage(LivingEntity damagee) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/MobDamage.java
class MobDamage (line 16) | public class MobDamage {
method getEntityEnchantmentsDamage (line 69) | public static double getEntityEnchantmentsDamage(EntityType entity, It...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/NewWeaponDamage.java
type NewWeaponDamage (line 13) | public enum NewWeaponDamage {
method NewWeaponDamage (line 37) | NewWeaponDamage(float damage) {
method getDamage (line 41) | public static float getDamage(String mat) {
method getDamage (line 45) | public static float getDamage(Material mat) {
method getDamageOrNull (line 49) | public static Float getDamageOrNull(String mat) {
method getDamageOrNull (line 57) | public static Float getDamageOrNull(Material mat) {
method getDamage (line 61) | public float getDamage() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/OCMEntityDamageByEntityEvent.java
class OCMEntityDamageByEntityEvent (line 30) | public class OCMEntityDamageByEntityEvent extends Event implements Cance...
method getHandlers (line 35) | @Override
method getHandlerList (line 40) | public static HandlerList getHandlerList() {
method OCMEntityDamageByEntityEvent (line 71) | public OCMEntityDamageByEntityEvent(Entity damager, Entity damagee, Da...
method warnOnUnknownWeaponEnchantments (line 203) | private static void warnOnUnknownWeaponEnchantments(ItemStack weapon) {
method shouldWarnOnUnknownEnchantment (line 226) | private static boolean shouldWarnOnUnknownEnchantment(Enchantment ench...
method initVanillaEnchantments (line 238) | private static Set<Enchantment> initVanillaEnchantments() {
method isVanillaEnchantment (line 249) | private static boolean isVanillaEnchantment(Enchantment enchantment) {
method getDamager (line 253) | public Entity getDamager() {
method getDamagee (line 257) | public Entity getDamagee() {
method getCause (line 261) | public DamageCause getCause() {
method getRawDamage (line 265) | public double getRawDamage() {
method getWeapon (line 269) | public ItemStack getWeapon() {
method getSharpnessLevel (line 273) | public int getSharpnessLevel() {
method getStrengthModifier (line 277) | public double getStrengthModifier() {
method setStrengthModifier (line 281) | public void setStrengthModifier(double strengthModifier) {
method getStrengthLevel (line 285) | public int getStrengthLevel() {
method hasWeakness (line 294) | public boolean hasWeakness() {
method getWeaknessLevel (line 298) | public int getWeaknessLevel() {
method getWeaknessModifier (line 302) | public double getWeaknessModifier() {
method setWeaknessModifier (line 306) | public void setWeaknessModifier(double weaknessModifier) {
method setWeaknessLevel (line 310) | public void setWeaknessLevel(int weaknessLevel) {
method isStrengthModifierMultiplier (line 314) | public boolean isStrengthModifierMultiplier() {
method setIsStrengthModifierMultiplier (line 318) | public void setIsStrengthModifierMultiplier(boolean isStrengthModifier...
method setIsStrengthModifierAddend (line 322) | public void setIsStrengthModifierAddend(boolean isStrengthModifierAdde...
method isWeaknessModifierMultiplier (line 326) | public boolean isWeaknessModifierMultiplier() {
method setIsWeaknessModifierMultiplier (line 330) | public void setIsWeaknessModifierMultiplier(boolean weaknessModifierMu...
method isStrengthModifierAddend (line 334) | public boolean isStrengthModifierAddend() {
method getBaseDamage (line 338) | public double getBaseDamage() {
method setBaseDamage (line 342) | public void setBaseDamage(double baseDamage) {
method getMobEnchantmentsDamage (line 346) | public double getMobEnchantmentsDamage() {
method setMobEnchantmentsDamage (line 350) | public void setMobEnchantmentsDamage(double mobEnchantmentsDamage) {
method getSharpnessDamage (line 354) | public double getSharpnessDamage() {
method setSharpnessDamage (line 358) | public void setSharpnessDamage(double sharpnessDamage) {
method getCriticalMultiplier (line 362) | public double getCriticalMultiplier() {
method setCriticalMultiplier (line 366) | public void setCriticalMultiplier(double criticalMultiplier) {
method isCancelled (line 370) | @Override
method setCancelled (line 375) | @Override
method wasSprinting (line 380) | public boolean wasSprinting() {
method setWasSprinting (line 384) | public void setWasSprinting(boolean wasSprinting) {
method was1_8Crit (line 388) | public boolean was1_8Crit() {
method setWas1_8Crit (line 392) | public void setWas1_8Crit(boolean was1_8Crit) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/WeaponDamages.java
class WeaponDamages (line 17) | public class WeaponDamages {
method initialise (line 23) | public static void initialise(OCMMain plugin) {
method reload (line 28) | private static void reload() {
method getDamage (line 33) | public static double getDamage(Material mat) {
method getDamage (line 38) | public static double getDamage(String key) {
method getMaterialDamages (line 42) | public static Map<Material, Double> getMaterialDamages() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/PotionDurations.java
class PotionDurations (line 11) | public final class PotionDurations {
method PotionDurations (line 15) | public PotionDurations(int drinkable, int splash) {
method drinkable (line 20) | public int drinkable() {
method splash (line 24) | public int splash() {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/PotionEffects.java
class PotionEffects (line 15) | public class PotionEffects {
method get (line 33) | public static Optional<PotionEffect> get(LivingEntity entity, PotionEf...
method getOrNull (line 44) | public static PotionEffect getOrNull(LivingEntity entity, PotionEffect...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/PotionKey.java
class PotionKey (line 20) | public final class PotionKey {
method PotionKey (line 28) | private PotionKey(XPotion potion, boolean strong, boolean extended) {
method getPotion (line 34) | public XPotion getPotion() {
method isStrong (line 38) | public boolean isStrong() {
method isExtended (line 42) | public boolean isExtended() {
method isPotion (line 46) | public boolean isPotion(XPotion target) {
method getDebugName (line 50) | public String getDebugName() {
method fromConfigKey (line 60) | public static Optional<PotionKey> fromConfigKey(String key) {
method fromPotionMeta (line 79) | public static Optional<PotionKey> fromPotionMeta(PotionMeta potionMeta) {
method fromPotionTypeName (line 96) | private static Optional<PotionKey> fromPotionTypeName(String name, boo...
method fromBaseName (line 112) | private static Optional<PotionKey> fromBaseName(String baseName, boole...
method equals (line 126) | @Override
method hashCode (line 138) | @Override
method toString (line 143) | @Override
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/WeaknessCompensation.java
class WeaknessCompensation (line 16) | public final class WeaknessCompensation {
method WeaknessCompensation (line 21) | private WeaknessCompensation() {
method hasModifier (line 24) | public static boolean hasModifier(LivingEntity entity) {
method apply (line 33) | public static void apply(LivingEntity entity) {
method remove (line 45) | public static void remove(LivingEntity entity) {
method getAttackDamageAttribute (line 55) | private static AttributeInstance getAttackDamageAttribute(LivingEntity...
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/reflection/Reflector.java
class Reflector (line 19) | public class Reflector {
method getVersion (line 41) | public static String getVersion() {
method versionIsNewerOrEqualTo (line 53) | public static boolean versionIsNewerOrEqualTo(int major, int minor, in...
method getMajorVersion (line 59) | private static int getMajorVersion() {
method getMinorVersion (line 63) | private static int getMinorVersion() {
method getPatchVersion (line 67) | private static int getPatchVersion() {
method getClass (line 71) | public static Class<?> getClass(String fqn) {
method getMethod (line 79) | public static Method getMethod(Class<?> clazz, String name) {
method getMethod (line 90) | public static Method getMethod(Class<?> clazz, String name, int parame...
method getMethod (line 101) | public static Method getMethod(Class<?> clazz, Class<?> returnType, St...
method getMethod (line 120) | public static Method getMethod(Class<?> clazz, String name, String... ...
method getMethodAssignable (line 137) | public static Method getMethodAssignable(Class<?> clazz, String name, ...
method getMethodByGenericReturnType (line 150) | public static Method getMethodByGenericReturnType(TypeVariable<?> type...
method invokeMethod (line 160) | public static <T> T invokeMethod(Method method, Object handle, Object....
method memoiseMethodInvocation (line 182) | public static <T, U, R> BiFunction<T, U, R> memoiseMethodInvocation(Cl...
method getField (line 194) | public static Field getField(Class<?> clazz, String fieldName) {
method getFieldByType (line 204) | public static Field getFieldByType(Class<?> clazz, String simpleClassN...
method getMapFieldWithTypes (line 214) | public static Field getMapFieldWithTypes(Class<?> clazz, Class<?> keyT...
method getFieldValueByType (line 237) | public static Object getFieldValueByType(Object object, String simpleC...
method getFieldValue (line 253) | public static Object getFieldValue(Field field, Object handle) {
method setFieldValue (line 262) | public static void setFieldValue(Field field, Object handle, Object va...
method getConstructor (line 271) | public static Constructor<?> getConstructor(Class<?> clazz, int numPar...
method getConstructor (line 282) | public static Constructor<?> getConstructor(Class<?> clazz, String... ...
method getConstructorAssignable (line 303) | public static Constructor<?> getConstructorAssignable(Class<?> clazz, ...
method getEnumConstant (line 318) | public static Object getEnumConstant(Class<?> enumClass, String... nam...
method areParametersAssignable (line 341) | private static boolean areParametersAssignable(Class<?>[] target, Clas...
method inheritsFrom (line 359) | public static boolean inheritsFrom(Class<?> toCheck, Class<?> inherite...
method getUnchecked (line 373) | public static <T> T getUnchecked(UncheckedReflectionSupplier<T> suppli...
method doUnchecked (line 381) | public static void doUnchecked(UncheckedReflectionRunnable runnable) {
type UncheckedReflectionSupplier (line 389) | public interface UncheckedReflectionSupplier<T> {
method get (line 390) | T get() throws ReflectiveOperationException;
type UncheckedReflectionRunnable (line 393) | public interface UncheckedReflectionRunnable {
method run (line 394) | void run() throws ReflectiveOperationException;
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/reflection/SpigotFunctionChooser.java
class SpigotFunctionChooser (line 26) | public class SpigotFunctionChooser<T, U, R> {
method SpigotFunctionChooser (line 41) | public SpigotFunctionChooser(BiFunction<T, U, Boolean> test, BiFunctio...
method apply (line 56) | public R apply(T target, U parameters) {
method apply (line 70) | @SuppressWarnings("unchecked")
method onException (line 92) | private static <T, U, R> SpigotFunctionChooser<T, U, R> onException(Ex...
method rootCause (line 111) | private static Throwable rootCause(Throwable throwable) {
method propagate (line 119) | private static RuntimeException propagate(Throwable throwable) {
method isCompatibilityFailure (line 129) | private static boolean isCompatibilityFailure(Throwable throwable) {
method isApprovedUnsupportedOperation (line 145) | private static boolean isApprovedUnsupportedOperation(UnsupportedOpera...
method apiCompatReflectionCall (line 176) | public static <T, U, R> SpigotFunctionChooser<T, U, R> apiCompatReflec...
method apiCompatCall (line 197) | public static <T, U, R> SpigotFunctionChooser<T, U, R> apiCompatCall(E...
type ExceptionalFunction (line 202) | @FunctionalInterface
method applyWithException (line 220) | R applyWithException(T t, U params) throws Throwable;
method apply (line 229) | @Override
class WrappedException (line 238) | class WrappedException extends RuntimeException {
method WrappedException (line 239) | WrappedException(Throwable cause) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/reflection/VersionCompatUtils.java
class VersionCompatUtils (line 20) | public class VersionCompatUtils {
method getCraftHandle (line 33) | private static Object getCraftHandle(Object spigotObject) {
method getAttackCooldown (line 43) | public static float getAttackCooldown(HumanEntity he) {
method getAbsorptionAmount (line 52) | public static float getAbsorptionAmount(LivingEntity livingEntity) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/ModesetListener.java
class ModesetListener (line 29) | public class ModesetListener extends OCMModule {
method ModesetListener (line 31) | public ModesetListener(OCMMain plugin) {
method onPlayerChangedWorld (line 35) | @EventHandler(priority = EventPriority.LOWEST)
method updateModeset (line 44) | private static void updateModeset(Player player, UUID worldId, String ...
method onPlayerJoin (line 78) | @EventHandler(priority = EventPriority.LOWEST)
method onWorldLoad (line 84) | @EventHandler(ignoreCancelled = false)
method onWorldUnload (line 91) | @EventHandler(ignoreCancelled = false)
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/PlayerData.java
class PlayerData (line 16) | public class PlayerData {
method PlayerData (line 19) | public PlayerData() {
method getModesetByWorld (line 23) | public Map<UUID, String> getModesetByWorld() {
method setModesetByWorld (line 27) | public void setModesetByWorld(Map<UUID, String> modesetByWorld) {
method setModesetForWorld (line 31) | public void setModesetForWorld(UUID worldId, String modeset) {
method getModesetForWorld (line 35) | public @Nullable String getModesetForWorld(UUID worldId) {
method fromDocument (line 39) | public static PlayerData fromDocument(Document doc) {
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/PlayerDataCodec.java
class PlayerDataCodec (line 20) | public class PlayerDataCodec implements Codec<PlayerData> {
method encode (line 22) | @Override
method getEncoderClass (line 33) | @Override
method decode (line 38) | @Override
FILE: src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/PlayerStorage.java
class PlayerStorage (line 37) | public class PlayerStorage {
method initialise (line 46) | public static void initialise(OCMMain plugin) {
method loadData (line 63) | private static Document loadData() {
method scheduleSave (line 76) | public static void scheduleSave() {
method instantSave (line 86) | public static void instantSave() {
method getPlayerData (line 101) | public static PlayerData getPlayerData(UUID uuid) {
method setPlayerData (line 113) | public static void setPlayerData(UUID uuid, PlayerData playerData) {
Condensed preview — 129 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,291K chars).
[
{
"path": ".github/CONTRIBUTING.md",
"chars": 34089,
"preview": "<!--\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL"
},
{
"path": ".github/ISSUE_TEMPLATE/1-bug-report.yaml",
"chars": 3333,
"preview": "name: Bug Report\ndescription: Report a bug in the plugin\nlabels: [\"bug\", \"investigate\"]\nbody:\n - type: markdown\n att"
},
{
"path": ".github/ISSUE_TEMPLATE/2-question.yaml",
"chars": 1843,
"preview": "name: Question\ndescription: Ask a question about the plugin\nlabels: [\"question\"]\nbody:\n - type: markdown\n attributes"
},
{
"path": ".github/ISSUE_TEMPLATE/3-feature.yaml",
"chars": 2075,
"preview": "name: Feature Request\ndescription: Suggest an idea for this project\nlabels: [\"enhancement\"]\nbody:\n - type: markdown\n "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 28,
"preview": "blank_issues_enabled: false\n"
},
{
"path": ".github/release-please-config.json",
"chars": 655,
"preview": "{\n \"$schema\": \"https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json\",\n \"include-compon"
},
{
"path": ".github/release-please-manifest.json",
"chars": 14,
"preview": "{\".\":\"2.4.0\"}\n"
},
{
"path": ".github/workflows/build-upload-release.yml",
"chars": 2636,
"preview": "name: Build and Release\n\non:\n release:\n types: [published]\n\njobs:\n build:\n # This permission is still required b"
},
{
"path": ".github/workflows/dev-builds.yml",
"chars": 1239,
"preview": "name: Dev builds\n\non:\n push:\n branches:\n - master\n pull_request:\n branches-ignore:\n - 'ingametesting'\n"
},
{
"path": ".github/workflows/release-please.yml",
"chars": 476,
"preview": "name: Release Please\n\non:\n push:\n branches:\n - master\n\npermissions:\n contents: write\n pull-requests: write\n\nj"
},
{
"path": ".github/workflows/wrap-issue-form-codeblocks.yml",
"chars": 1949,
"preview": "name: Wrap issue form code blocks\n\non:\n issues:\n types: [opened, edited]\n\npermissions:\n issues: write\n\njobs:\n wrap"
},
{
"path": ".gitignore",
"chars": 320,
"preview": "# ===\n# == IDE settings files\n# ===\n\n# IntelliJ\n/*.eml\n/*.iml\n/.idea\n\n# Eclipse\n/.project\n/.classpath\n/.settings\n\n# ===\n"
},
{
"path": "AGENTS.md",
"chars": 37301,
"preview": "# AGENTS.md\n\n**Prime directive:** If you are reading this file, it is your responsibility as an agent to keep it up to d"
},
{
"path": "CHANGELOG.md",
"chars": 14559,
"preview": "# Changelog\n\n## [2.4.0](https://github.com/kernitus/BukkitOldCombatMechanics/compare/v2.3.0...v2.4.0) (2026-03-08)\n\n\n###"
},
{
"path": "LICENCE",
"chars": 16725,
"preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
},
{
"path": "README.md",
"chars": 5538,
"preview": "<!--\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL"
},
{
"path": "build.gradle.kts",
"chars": 27395,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "gradle.properties",
"chars": 582,
"preview": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not di"
},
{
"path": "gradlew",
"chars": 8595,
"preview": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\")"
},
{
"path": "gradlew.bat",
"chars": 2803,
"preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "settings.gradle.kts",
"chars": 250,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackCompat.kt",
"chars": 7111,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackCooldownHeldItemIntegrationTest.kt",
"chars": 21240,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackCooldownTrackerIntegrationTest.kt",
"chars": 2864,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttackRangeIntegrationTest.kt",
"chars": 13680,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/AttributeModifierCompat.kt",
"chars": 3425,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/ChorusFruitIntegrationTest.kt",
"chars": 6640,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/ConfigMigrationIntegrationTest.kt",
"chars": 4549,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/ConsumableComponentIntegrationTest.kt",
"chars": 94920,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/CopperToolsIntegrationTest.kt",
"chars": 1678,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/CustomWeaponDamageIntegrationTest.kt",
"chars": 8845,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/DisableOffhandIntegrationTest.kt",
"chars": 2824,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/DisableOffhandReflectionIntegrationTest.kt",
"chars": 4054,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/EnderpearlCooldownIntegrationTest.kt",
"chars": 7128,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/FakePlayer.kt",
"chars": 39035,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/FireAspectOverdamageIntegrationTest.kt",
"chars": 45776,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/FishingRodVelocityIntegrationTest.kt",
"chars": 12815,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/GoldenAppleIntegrationTest.kt",
"chars": 16012,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/InGameTester.kt",
"chars": 16561,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/InGameTesterIntegrationTest.kt",
"chars": 7616,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/InvulnerabilityDamageIntegrationTest.kt",
"chars": 26905,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/KotestRunner.kt",
"chars": 8459,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/LegacyFakePlayer12.kt",
"chars": 19924,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/LegacyFakePlayer9.kt",
"chars": 19764,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/ModesetRulesIntegrationTest.kt",
"chars": 10346,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/OCMTest.kt",
"chars": 665,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/OCMTestMain.kt",
"chars": 1951,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/OldArmourDurabilityIntegrationTest.kt",
"chars": 5953,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/OldCriticalHitsIntegrationTest.kt",
"chars": 20985,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/OldPotionEffectsIntegrationTest.kt",
"chars": 64600,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/OldToolDamageMobIntegrationTest.kt",
"chars": 12302,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/PacketCancellationIntegrationTest.kt",
"chars": 11525,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/PaperSwordBlockingDamageReductionIntegrationTest.kt",
"chars": 6128,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/PlayerKnockbackIntegrationTest.kt",
"chars": 14727,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/PlayerRegenIntegrationTest.kt",
"chars": 8937,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/SpigotFunctionChooserIntegrationTest.kt",
"chars": 4112,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/SwordBlockingIntegrationTest.kt",
"chars": 16287,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/SwordSweepIntegrationTest.kt",
"chars": 5984,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/Tally.kt",
"chars": 707,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/TestResultWriter.kt",
"chars": 1519,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/TesterUtils.kt",
"chars": 2507,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/ToolDamageTooltipIntegrationTest.kt",
"chars": 18518,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/kotlin/kernitus/plugin/OldCombatMechanics/WeaponDurabilityIntegrationTest.kt",
"chars": 27666,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/integrationTest/resources/plugin.yml",
"chars": 509,
"preview": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not di"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/ModuleLoader.java",
"chars": 1517,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/OCMConfigHandler.java",
"chars": 8746,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/OCMMain.java",
"chars": 12333,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/UpdateChecker.java",
"chars": 2804,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/commands/OCMCommandCompleter.java",
"chars": 3093,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/commands/OCMCommandHandler.java",
"chars": 8153,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/hooks/PlaceholderAPIHook.java",
"chars": 3677,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/hooks/api/Hook.java",
"chars": 408,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackCooldown.java",
"chars": 4564,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackFrequency.java",
"chars": 3461,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackRange.java",
"chars": 14774,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleAttackSounds.java",
"chars": 5114,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleChorusFruit.java",
"chars": 5119,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleDisableCrafting.java",
"chars": 1833,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleDisableEnderpearlCooldown.java",
"chars": 6134,
"preview": "/*\r\n * This Source Code Form is subject to the terms of the Mozilla Public\r\n * License, v. 2.0. If a copy of the MPL was"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleDisableOffHand.java",
"chars": 8687,
"preview": "/*\r\n * This Source Code Form is subject to the terms of the Mozilla Public\r\n * License, v. 2.0. If a copy of the MPL was"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleFishingKnockback.java",
"chars": 5923,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleFishingRodVelocity.java",
"chars": 5776,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleGoldenApple.java",
"chars": 15183,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldArmourDurability.java",
"chars": 6241,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldArmourStrength.java",
"chars": 3393,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldBrewingStand.java",
"chars": 1537,
"preview": "/*\r\n * This Source Code Form is subject to the terms of the Mozilla Public\r\n * License, v. 2.0. If a copy of the MPL was"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldBurnDelay.java",
"chars": 1138,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldCriticalHits.java",
"chars": 1523,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldPotionEffects.java",
"chars": 16714,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleOldToolDamage.java",
"chars": 11129,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModulePlayerKnockback.java",
"chars": 10356,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModulePlayerRegen.java",
"chars": 5651,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleProjectileKnockback.java",
"chars": 1551,
"preview": "/*\r\n * This Source Code Form is subject to the terms of the Mozilla Public\r\n * License, v. 2.0. If a copy of the MPL was"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleShieldDamageReduction.java",
"chars": 8069,
"preview": "/*\r\n * This Source Code Form is subject to the terms of the Mozilla Public\r\n * License, v. 2.0. If a copy of the MPL was"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleSwordBlocking.java",
"chars": 48808,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleSwordSweep.java",
"chars": 4486,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/ModuleSwordSweepParticles.java",
"chars": 3190,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/module/OCMModule.java",
"chars": 6346,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/paper/PaperSwordBlocking.java",
"chars": 24536,
"preview": "package kernitus.plugin.OldCombatMechanics.paper;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodH"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/updater/ModuleUpdateChecker.java",
"chars": 1057,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/updater/SpigetUpdateChecker.java",
"chars": 6853,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/updater/VersionChecker.java",
"chars": 1749,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/Config.java",
"chars": 13823,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/ConfigUtils.java",
"chars": 7317,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/EventRegistry.java",
"chars": 1698,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/MathsHelper.java",
"chars": 782,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/Messenger.java",
"chars": 3910,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/TextUtils.java",
"chars": 942,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/AttackCooldownTracker.java",
"chars": 2463,
"preview": "package kernitus.plugin.OldCombatMechanics.utilities.damage;\n\nimport kernitus.plugin.OldCombatMechanics.OCMMain;\nimport "
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/DamageUtils.java",
"chars": 4044,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/DefenceUtils.java",
"chars": 18688,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/EntityDamageByEntityListener.java",
"chars": 18912,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/MobDamage.java",
"chars": 3119,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/NewWeaponDamage.java",
"chars": 2168,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/OCMEntityDamageByEntityEvent.java",
"chars": 15212,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/damage/WeaponDamages.java",
"chars": 1946,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/PotionDurations.java",
"chars": 709,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/PotionEffects.java",
"chars": 1937,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/PotionKey.java",
"chars": 4497,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/potions/WeaknessCompensation.java",
"chars": 2308,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/reflection/Reflector.java",
"chars": 16002,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/reflection/SpigotFunctionChooser.java",
"chars": 9434,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/reflection/VersionCompatUtils.java",
"chars": 3672,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/ModesetListener.java",
"chars": 3981,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/PlayerData.java",
"chars": 1576,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/PlayerDataCodec.java",
"chars": 1473,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/java/kernitus/plugin/OldCombatMechanics/utilities/storage/PlayerStorage.java",
"chars": 5157,
"preview": "/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was n"
},
{
"path": "src/main/resources/config.yml",
"chars": 22276,
"preview": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not di"
},
{
"path": "src/main/resources/plugin.yml",
"chars": 1759,
"preview": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not di"
}
]
About this extraction
This page contains the full source code of the kernitus/BukkitOldCombatMechanics GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 129 files (1.2 MB), approximately 267.2k tokens, and a symbol index with 662 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.