├── images └── dmgindicatorscreen.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── scripts └── installSpigot.sh ├── NMS ├── 1_16_R3 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_16_R3.java ├── 1_19_R3 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_19_R3.java ├── 1_20_R1 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_20_R1.java ├── 1_17_R1 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_17_R1.java ├── 1_18_R1 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_18_R1.java ├── 1_19_R1 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_19_R1.java ├── 1_19_R2 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_19_R2.java ├── 1_20_R2 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_20_R2.java ├── 1_20_R3 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_20_R3.java ├── 1_20_R4 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_20_R4.java ├── 1_21_R1 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_21_R1.java ├── 1_21_R2 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_21_R2.java ├── 1_21_R3 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_21_R3.java ├── 1_21_R4 │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── packetManager │ │ └── PacketManager1_21_R4.java └── 1_21_R5 │ ├── build.gradle.kts │ └── src │ └── main │ └── java │ └── io │ └── github │ └── magiccheese1 │ └── damageindicator │ └── packetManager │ └── PacketManager1_21_R5.java ├── API ├── build.gradle.kts └── src │ └── main │ └── java │ └── io │ └── github │ └── magiccheese1 │ └── damageindicator │ ├── IndicatorEntity.java │ ├── DamageIndicator.java │ └── packetManager │ └── PacketManager.java ├── settings.gradle.kts ├── DamageIndicator ├── src │ ├── main │ │ ├── java │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── magiccheese1 │ │ │ │ └── damageindicator │ │ │ │ ├── config │ │ │ │ ├── Options.java │ │ │ │ └── ConfigUtility.java │ │ │ │ ├── CommandReload.java │ │ │ │ ├── Utility.java │ │ │ │ ├── IndicatorEntityImpl.java │ │ │ │ ├── DamageIndicatorImpl.java │ │ │ │ └── BukkitEventListener.java │ │ └── resources │ │ │ └── config.yml │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── magiccheese1 │ │ └── damageindicator │ │ └── TextUtilityTest.java └── build.gradle.kts ├── LICENSE ├── .editorconfig ├── .gitignore ├── README.md ├── gradlew.bat ├── .github └── workflows │ └── release.yml └── gradlew /images/dmgindicatorscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagicCheese1/Damage-Indicator/HEAD/images/dmgindicatorscreen.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagicCheese1/Damage-Indicator/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /scripts/installSpigot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir $1 4 | cp BuildTools.jar $1/BuildTools.jar 5 | cd $1 6 | java -jar BuildTools.jar -rev $1 --remapped 7 | cd .. 8 | -------------------------------------------------------------------------------- /NMS/1_16_R3/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | } 4 | 5 | dependencies { 6 | compileOnly("org.spigotmc:spigot:1.16.5-R0.1-SNAPSHOT") 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly(project(":API")) 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /API/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `maven-publish` 3 | } 4 | 5 | java { 6 | withSourcesJar() 7 | withJavadocJar() 8 | } 9 | 10 | dependencies { 11 | compileOnly("org.jetbrains:annotations:22.0.0") 12 | compileOnly("org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT") 13 | 14 | } 15 | 16 | publishing { 17 | publications.create("damageindicator-api") { 18 | artifactId = "damageindicator-api" 19 | from(components["java"]) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /NMS/1_19_R3/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | dependencies { 6 | compileOnly("org.jetbrains:annotations:22.0.0") 7 | compileOnly("org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT:remapped-mojang") 8 | 9 | implementation(project(":API")) 10 | } 11 | 12 | tasks { 13 | remap { 14 | version.set("1.19.4") 15 | 16 | inputTask.set(jar) 17 | 18 | archiveName.set("${project.name}-${project.version}.jar") 19 | } 20 | } 21 | 22 | tasks.named("assemble") { 23 | dependsOn("remap") 24 | } 25 | -------------------------------------------------------------------------------- /NMS/1_20_R1/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | dependencies { 6 | compileOnly("org.jetbrains:annotations:22.0.0") 7 | compileOnly("org.spigotmc:spigot:1.20.1-R0.1-SNAPSHOT:remapped-mojang") 8 | 9 | implementation(project(":API")) 10 | } 11 | 12 | tasks { 13 | remap { 14 | version.set("1.20.1") 15 | 16 | inputTask.set(jar) 17 | 18 | archiveName.set("${project.name}-${project.version}.jar") 19 | } 20 | } 21 | 22 | tasks.named("assemble") { 23 | dependsOn("remap") 24 | } 25 | -------------------------------------------------------------------------------- /NMS/1_17_R1/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.17.1-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.17.1") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_18_R1/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.18.2-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.18.2") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_19_R1/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.19.2-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.19.2") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_19_R2/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.19.3") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_20_R2/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.20.2-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.20.2") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_20_R3/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.20.4-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.20.4") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_20_R4/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.20.6") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_21_R1/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.21.1-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.21.1") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_21_R2/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.21.3-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.21.3") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_21_R3/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.21.4-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.21.4") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_21_R4/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.21.5-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.21.5") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /NMS/1_21_R5/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("io.github.patrick.remapper") version "1.4.2" 4 | } 5 | 6 | dependencies { 7 | compileOnly("org.jetbrains:annotations:22.0.0") 8 | compileOnly("org.spigotmc:spigot:1.21.6-R0.1-SNAPSHOT:remapped-mojang") 9 | 10 | implementation(project(":API")) 11 | } 12 | 13 | tasks { 14 | remap { 15 | version.set("1.21.6") 16 | 17 | inputTask.set(jar) 18 | 19 | archiveName.set("${project.name}-${project.version}.jar") 20 | } 21 | } 22 | 23 | tasks.named("assemble") { 24 | dependsOn("remap") 25 | } 26 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "DamageIndicator" 2 | 3 | plugins { 4 | id("org.gradle.toolchains.foojay-resolver-convention") version ("0.4.0") 5 | } 6 | 7 | include( 8 | "API", 9 | "DamageIndicator", 10 | ":NMS:1_16_R3", 11 | ":NMS:1_17_R1", 12 | ":NMS:1_18_R1", 13 | ":NMS:1_19_R1", 14 | ":NMS:1_19_R2", 15 | ":NMS:1_19_R3", 16 | ":NMS:1_20_R1", 17 | ":NMS:1_20_R2", 18 | ":NMS:1_20_R3", 19 | ":NMS:1_20_R4", 20 | ":NMS:1_21_R1", 21 | ":NMS:1_21_R2", 22 | ":NMS:1_21_R3", 23 | ":NMS:1_21_R4", 24 | ":NMS:1_21_R5", 25 | ) 26 | 27 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/java/io/github/magiccheese1/damageindicator/config/Options.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.config; 2 | 3 | public class Options { 4 | 5 | public static final String SHOW_DAMAGE_ONLY = "ShowToDamagerOnly"; 6 | public static final String FORMAT_INDICATOR = "IndicatorFormat"; 7 | public static final String CRITICAL_FORMAT = "CriticalIndicatorFormat"; 8 | public static final String POISON_FORMAT = "PoisonIndicatorFormat"; 9 | public static final String BURN_FORMAT = "BurnIndicatorFormat"; 10 | public static final String INSTANT_DAMAGE_FORMAT = "InstantDamageIndicatorFormat"; 11 | public static final String INDICATOR_TIME = "IndicatorTime"; 12 | public static final String FORMAT_LOCALE = "FormatLocale"; 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kacper Płażewski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | max_line_length = 120 9 | insert_final_newline = true 10 | 11 | [{*.xml,*.yml}] 12 | indent_size = 2 13 | 14 | 15 | [*.java] 16 | # java docs 17 | ij_java_doc_enable_formatting = true 18 | ij_java_doc_add_blank_line_after_description = true 19 | ij_java_doc_add_blank_line_after_param_comments = true 20 | ij_java_doc_add_blank_line_after_return = true 21 | ij_java_doc_add_p_tag_on_empty_lines = true 22 | ij_java_doc_align_exception_comments = true 23 | ij_java_doc_align_param_comments = true 24 | ij_java_doc_indent_on_continuation = true 25 | ij_java_doc_keep_empty_lines = true 26 | ij_java_doc_param_description_on_new_line = false 27 | ij_java_doc_preserve_line_breaks = true 28 | ij_java_doc_use_throws_not_exception_tag = true 29 | ij_java_doc_do_not_wrap_if_one_line = true 30 | ij_java_wrap_comments = true 31 | ij_java_wrap_long_lines = true 32 | ij_java_insert_override_annotation = true 33 | ij_java_packages_to_use_import_on_demand = 999999999 34 | ij_java_class_count_to_use_import_on_demand = 99999999 35 | ij_java_names_count_to_use_import_on_demand = 99999999 36 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/java/io/github/magiccheese1/damageindicator/CommandReload.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.command.Command; 5 | import org.bukkit.command.CommandExecutor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.plugin.Plugin; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class CommandReload implements CommandExecutor { 11 | 12 | private final Plugin plugin; 13 | 14 | public CommandReload(final @NotNull Plugin plugin) { 15 | this.plugin = plugin; 16 | } 17 | 18 | @Override 19 | public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String label, 20 | @NotNull String[] args) { 21 | if (commandSender.hasPermission("Damageindicator.admin")) { 22 | if (args.length >= 1) { 23 | if (args[0].equalsIgnoreCase("reload")) { 24 | this.plugin.reloadConfig(); 25 | commandSender.sendMessage(ChatColor.YELLOW + "DamageIndicator plugin has been reloaded"); 26 | return true; 27 | } 28 | } 29 | } 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/java/io/github/magiccheese1/damageindicator/Utility.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.potion.PotionData; 5 | import org.bukkit.potion.PotionEffectType; 6 | 7 | /** 8 | * Provides basic utility functionality. 9 | */ 10 | public class Utility { 11 | 12 | /** 13 | * Determine if the direct hit was a critical hit 14 | * 15 | * @param damager - The damaging player 16 | */ 17 | @SuppressWarnings("deprecation") 18 | public static boolean isCritical(Player damager) { 19 | return damager.getAttackCooldown() > 0.9F && damager.getFallDistance() > 0.0F 20 | && !damager.isOnGround() && !damager.isInWater() && damager.getActivePotionEffects().stream() 21 | .noneMatch(o -> o.getType().equals(PotionEffectType.BLINDNESS)) 22 | && damager.getVehicle() == null && !damager.isSprinting(); 23 | } 24 | 25 | public static int poisonArrowEffectDuration(PotionData basePotionData) { 26 | if (basePotionData.isUpgraded()) return 54; 27 | if (basePotionData.isExtended()) return 225; 28 | return 112; 29 | } 30 | 31 | public static int poisonLingeringPotionEffectDuration(PotionData basePotionData) { 32 | if (basePotionData.isUpgraded()) return 108; 33 | if (basePotionData.isExtended()) return 450; 34 | return 225; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | ## Should the indicator be shown to everyone(false) or just the player who dealt the damage(true) 2 | ShowToDamagerOnly: true 3 | 4 | ## The format for showing the damage 5 | # Read about decimal formats to learn how to configure the indicator format. 6 | # https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/DecimalFormat.html 7 | 8 | # Hex colours are supported using the legacy format (e.g. &x&F&F&F&F&F&F to represent white), but may also be defined 9 | # using a smaller version unique to the damage indicator plugin (e.g. &#FFFFFF to represent white). Note that the usage 10 | # of 0's in the hex code would mess with the decimal formatting, hence they should be escaped using single quotes, such 11 | # as this: 12 | # IndicatorFormat: "'&#FFC300'-0.#&4❤" 13 | 14 | # using Hex colours is optional. The legacy minecraft colour codes are still supported. 15 | IndicatorFormat: "&7-0.#&4❤" 16 | CriticalIndicatorFormat: "&c-0.#&4❤" 17 | PoisonIndicatorFormat: "&7-0.#'e8a24'❤" 18 | BurnIndicatorFormat: "&7-0.#'&#f59e42'❤" 19 | InstantDamageIndicatorFormat: "&7-0.#'򔻐'❤" 20 | 21 | ## The decimal format's locale (BCP47) 22 | # This determines the locale used for formatting. The formatter uses this e.g. when deciding between a dot ('0.0') 23 | # or a comma ('0,0') for decimal numbers. 24 | FormatLocale: "en-US" 25 | 26 | ## For how long an indicator should be shown. (Use seconds) 27 | IndicatorTime: 1.5 28 | -------------------------------------------------------------------------------- /API/src/main/java/io/github/magiccheese1/damageindicator/IndicatorEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.entity.LivingEntity; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.scheduler.BukkitTask; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.text.DecimalFormat; 10 | import java.util.Collection; 11 | 12 | /** 13 | * Represents a temporary entity used for displaying a damage indication to a player in the world. 14 | *

15 | * Use {@link DamageIndicator#spawnIndicator(LivingEntity, Player, DecimalFormat, double)} to spawn an indicator. 16 | */ 17 | public interface IndicatorEntity { 18 | 19 | /** 20 | * Shows the Indicator to players. Can be used to respawn Indicator after it being destroyed. 21 | */ 22 | void spawn(); 23 | 24 | double getValue(); 25 | 26 | @NotNull Location getLocation(); 27 | 28 | @NotNull DecimalFormat getFormat(); 29 | 30 | /** 31 | * This method schedules the destruction of the Indicator. 32 | * 33 | * @param tickDelay The destruction delay in ticks 34 | * 35 | * @return the created BukkitTask. use {@link BukkitTask#cancel()} to cancel destruction 36 | */ 37 | BukkitTask scheduleDestroy(long tickDelay); 38 | 39 | /** 40 | * This method destroys the Indicator on the client. 41 | * The Indicator can be spawned again. 42 | */ 43 | void destroy(); 44 | 45 | Collection getVisibleTo(); 46 | 47 | boolean isAlive(); 48 | } 49 | -------------------------------------------------------------------------------- /DamageIndicator/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | id("com.github.johnrengelman.shadow") version "8.1.0" 4 | id("net.minecrell.plugin-yml.bukkit") version "0.5.3" // Generates plugin.yml 5 | } 6 | 7 | dependencies { 8 | implementation("com.tchristofferson:ConfigUpdater:2.0-SNAPSHOT") 9 | compileOnly("org.jetbrains:annotations:22.0.0") 10 | compileOnly("org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT") 11 | implementation(project(":API")) 12 | implementation(project(":NMS:1_16_R3")) 13 | implementation(project(":NMS:1_17_R1")) 14 | implementation(project(":NMS:1_18_R1")) 15 | implementation(project(":NMS:1_19_R1")) 16 | implementation(project(":NMS:1_19_R2")) 17 | implementation(project(":NMS:1_19_R3")) 18 | implementation(project(":NMS:1_20_R1")) 19 | implementation(project(":NMS:1_20_R2")) 20 | implementation(project(":NMS:1_20_R3")) 21 | implementation(project(":NMS:1_20_R4")) 22 | implementation(project(":NMS:1_21_R1")) 23 | implementation(project(":NMS:1_21_R2")) 24 | implementation(project(":NMS:1_21_R3")) 25 | implementation(project(":NMS:1_21_R4")) 26 | implementation(project(":NMS:1_21_R5")) 27 | 28 | 29 | testImplementation("junit:junit:4.13.1") 30 | testImplementation("org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT") 31 | } 32 | tasks { 33 | test { 34 | useJUnit() 35 | } 36 | shadowJar { 37 | relocate("com.tchristofferson.ConfigUpdater", "io.github.magiccheese1.damageindicator.ConfigUpdater") 38 | } 39 | build { 40 | dependsOn(shadowJar) 41 | } 42 | } 43 | 44 | bukkit { 45 | main = "io.github.magiccheese1.damageindicator.DamageIndicatorImpl" 46 | apiVersion = "1.16" 47 | commands { 48 | register("damageindicator") { 49 | description = "Reload" 50 | permission = "Damageindicator.admin" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DamageIndicator/src/test/java/io/github/magiccheese1/damageindicator/TextUtilityTest.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import net.md_5.bungee.api.ChatColor; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.text.DecimalFormat; 8 | 9 | import static io.github.magiccheese1.damageindicator.config.ConfigUtility.convertEasyHexToLegacy; 10 | 11 | public class TextUtilityTest { 12 | 13 | @Test 14 | public void testBasic() { 15 | Assert.assertEquals( 16 | "§x§F§F§F§F§F§F This is white", 17 | convertEasyHexToLegacy("§#FFFFFF This is white") 18 | ); 19 | } 20 | 21 | @Test 22 | public void testMultiple() { 23 | Assert.assertEquals( 24 | "§x§F§F§F§F§F§F White and §x§3§3§F§F§5§7 green", 25 | convertEasyHexToLegacy("§#FFFFFF White and §#33FF57 green") 26 | ); 27 | } 28 | 29 | @Test 30 | public void testIgnored() { 31 | Assert.assertEquals( 32 | "§#FFFFF White with 5 and #33FF57 green without", 33 | convertEasyHexToLegacy("§#FFFFF White with 5 and #33FF57 green without") 34 | ); 35 | } 36 | 37 | @Test 38 | public void testRealConfigValue() { 39 | Assert.assertEquals( 40 | "§x§3§3§F§F§5§7-0.#&4❤", 41 | convertEasyHexToLegacy("!FF57-0.#&4❤") 42 | ); 43 | } 44 | 45 | @Test 46 | public void testInDecimalFormat() { 47 | final String postHexConversion = convertEasyHexToLegacy("!FF57-0.#&4❤"); 48 | final String postChatColourConversion = ChatColor.translateAlternateColorCodes('&', postHexConversion); 49 | 50 | final DecimalFormat decimalFormat = new DecimalFormat(postChatColourConversion); 51 | Assert.assertEquals( 52 | "§x§3§3§F§F§5§7-1§4❤", 53 | decimalFormat.format(1d) 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | # mpeltonen/sbt-idea plugin 11 | .idea_modules/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Package Files # 26 | *.jar 27 | *.war 28 | *.nar 29 | *.ear 30 | *.zip 31 | *.tar.gz 32 | *.rar 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | 37 | *~ 38 | 39 | # temporary files which can be created if a process still has a handle open of a deleted file 40 | .fuse_hidden* 41 | 42 | # KDE directory preferences 43 | .directory 44 | 45 | # Linux trash folder which might appear on any partition or disk 46 | .Trash-* 47 | 48 | # .nfs files are created when an open file is removed but is still being accessed 49 | .nfs* 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | # Windows thumbnail cache files 79 | Thumbs.db 80 | Thumbs.db:encryptable 81 | ehthumbs.db 82 | ehthumbs_vista.db 83 | 84 | # Dump file 85 | *.stackdump 86 | 87 | # Folder config file 88 | [Dd]esktop.ini 89 | 90 | # Recycle Bin used on file shares 91 | $RECYCLE.BIN/ 92 | 93 | # Windows Installer files 94 | *.cab 95 | *.msi 96 | *.msix 97 | *.msm 98 | *.msp 99 | 100 | # Windows shortcuts 101 | *.lnk 102 | 103 | .gradle 104 | build/ 105 | 106 | # Ignore Gradle GUI config 107 | gradle-app.setting 108 | 109 | # Cache of project 110 | .gradletasknamecache 111 | 112 | **/build/ 113 | 114 | # Common working directory 115 | run/ 116 | 117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 118 | !gradle-wrapper.jar 119 | 120 | cache 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Damage Indicator 2 | 3 | This is a plugin for Minecraft Bukkit/Spigot/Paper servers. It adds indicators that show how much damage you dealt to an entity. 4 | ![](/images/dmgindicatorscreen.png) 5 | 6 | ## Installing 7 | 8 | 1. Download the newest release from the [releases page](https://github.com/MagicCheese1/Damage-Indicator/releases) 9 | 10 | 2. Put the downloaded jar in your plugins folder 11 | 12 | 3. Enjoy 13 | 14 | ## Configuration 15 | 16 | You can find the config file at \plugins\DamageIndicator\config.yml. By default, it should look like this: 17 | 18 | ```yaml 19 | ## Should the indicator be shown to everyone(false) or just the player who dealt the damage(true) 20 | ShowToDamagerOnly: true 21 | 22 | ## The format for showing the damage 23 | # Read about decimal formats to learn how to configure the indicator format. 24 | # https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/DecimalFormat.html 25 | 26 | # Hex colours are supported using the legacy format (e.g. &x&F&F&F&F&F&F to represent white), but may also be defined 27 | # using a smaller version unique to the damage indicator plugin (e.g. &#FFFFFF to represent white). Note that the usage 28 | # of 0's in the hex code would mess with the decimal formatting, hence they should be escaped using single quotes, such 29 | # as this: 30 | # IndicatorFormat: "'&#FFC300'-0.#&4❤" 31 | 32 | # using Hex colours is optional. The legacy minecraft colour codes are still supported. 33 | IndicatorFormat: "&7-0.#&4❤" 34 | CriticalIndicatorFormat: "&c-0.#&4❤" 35 | PoisonIndicatorFormat: "&7-0.#'e8a24'❤" 36 | BurnIndicatorFormat: "&7-0.#'&#f59e42'❤" 37 | 38 | ## The decimal format's locale (BCP47) 39 | # This determines the locale used for formatting. The formatter uses this e.g. when deciding between a dot ('0.0') 40 | # or a comma ('0,0') for decimal numbers. 41 | FormatLocale: "en-US" 42 | 43 | ## For how long an indicator should be shown. (Use seconds) 44 | IndicatorTime: 1.5 45 | 46 | ``` 47 | 48 | ## Commands and permissions 49 | 50 | `/damageindicator reload` reloads the config. requires `Damageindicator.admin` 51 | 52 | 53 | ## Building 54 | 55 | 1. Clone this repo 56 | 2. open a terminal and type `gradlew build` 57 | 3. The jar file is at /build/libs/DamageIndicator.jar 58 | 59 | ## License 60 | 61 | This repo is licensed under the [MIT LICENSE](/LICENSE) 62 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /NMS/1_18_R1/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_18_R1.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddMobPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.18 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_18_R1 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new ClientboundAddMobPacket((LivingEntity) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 41 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher, forceUpdateAll); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 47 | final int entityId = ((Entity) entity).getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 58 | worldServer, 59 | location.getX(), location.getY(), location.getZ() 60 | ); 61 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 62 | armorStand.setMarker(true); 63 | armorStand.setInvisible(true); 64 | armorStand.setCustomNameVisible(true); 65 | armorStand.setCustomName(name); 66 | 67 | return entityArmorStand; 68 | } 69 | 70 | @Override 71 | public void sendPacket(@NotNull Object packet, Collection players) { 72 | for (Player player : players) { 73 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 74 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 75 | playerConnection.send((Packet) packet); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /NMS/1_20_R1/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_20_R1.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.20 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_20_R1 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new ClientboundAddEntityPacket((LivingEntity) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 41 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 47 | final int entityId = ((Entity) entity).getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 58 | worldServer, 59 | location.getX(), location.getY(), location.getZ() 60 | ); 61 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 62 | armorStand.setMarker(true); 63 | armorStand.setInvisible(true); 64 | armorStand.setCustomNameVisible(true); 65 | armorStand.setCustomName(name); 66 | 67 | return entityArmorStand; 68 | } 69 | 70 | @Override 71 | public void sendPacket(@NotNull Object packet, Collection players) { 72 | for (Player player : players) { 73 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 74 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 75 | playerConnection.send((Packet) packet); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /NMS/1_20_R2/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_20_R2.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.20 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_20_R2 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new ClientboundAddEntityPacket((LivingEntity) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 41 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 47 | final int entityId = ((Entity) entity).getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 58 | worldServer, 59 | location.getX(), location.getY(), location.getZ() 60 | ); 61 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 62 | armorStand.setMarker(true); 63 | armorStand.setInvisible(true); 64 | armorStand.setCustomNameVisible(true); 65 | armorStand.setCustomName(name); 66 | 67 | return entityArmorStand; 68 | } 69 | 70 | @Override 71 | public void sendPacket(@NotNull Object packet, Collection players) { 72 | for (Player player : players) { 73 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 74 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 75 | playerConnection.send((Packet) packet); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /NMS/1_20_R3/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_20_R3.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.20 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_20_R3 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new ClientboundAddEntityPacket((LivingEntity) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 41 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 47 | final int entityId = ((Entity) entity).getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 58 | worldServer, 59 | location.getX(), location.getY(), location.getZ() 60 | ); 61 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 62 | armorStand.setMarker(true); 63 | armorStand.setInvisible(true); 64 | armorStand.setCustomNameVisible(true); 65 | armorStand.setCustomName(name); 66 | 67 | return entityArmorStand; 68 | } 69 | 70 | @Override 71 | public void sendPacket(@NotNull Object packet, Collection players) { 72 | for (Player player : players) { 73 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 74 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 75 | playerConnection.send((Packet) packet); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /NMS/1_20_R4/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_20_R4.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.20 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_20_R4 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new ClientboundAddEntityPacket((LivingEntity) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 41 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 47 | final int entityId = ((Entity) entity).getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 58 | worldServer, 59 | location.getX(), location.getY(), location.getZ() 60 | ); 61 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 62 | armorStand.setMarker(true); 63 | armorStand.setInvisible(true); 64 | armorStand.setCustomNameVisible(true); 65 | armorStand.setCustomName(name); 66 | 67 | return entityArmorStand; 68 | } 69 | 70 | @Override 71 | public void sendPacket(@NotNull Object packet, Collection players) { 72 | for (Player player : players) { 73 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 74 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 75 | playerConnection.send((Packet) packet); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/java/io/github/magiccheese1/damageindicator/config/ConfigUtility.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.config; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.configuration.file.FileConfiguration; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.text.DecimalFormat; 8 | import java.text.NumberFormat; 9 | import java.util.Locale; 10 | import java.util.Optional; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * Provides basic utility functionality for configuration. 16 | */ 17 | public class ConfigUtility { 18 | private static final Pattern EASY_HEX_PATTERN = Pattern.compile("[§&]#[a-fA-F0-9]{6}"); 19 | 20 | /** 21 | * Parses a decimal format from the provided configuration file. 22 | * The decimal format will previously be translated using {@link ChatColor#translateAlternateColorCodes(char, 23 | * String)}. 24 | * 25 | * @param configuration the configuration instance to pull the string format from. 26 | * @param path the path that correlates to the damage format inside the file configuration. 27 | * 28 | * @return the parsed decimal format. If the configuration did not contain the path, the returned optional will be 29 | * empty. 30 | */ 31 | @NotNull 32 | public static Optional getConfigurationDamageFormat(@NotNull final FileConfiguration configuration, 33 | @NotNull String path) { 34 | final String stringFormat = configuration.getString(path); 35 | final String formatLocale = Optional.ofNullable(configuration.getString(Options.FORMAT_LOCALE)) 36 | .orElse("en-US"); 37 | if (stringFormat == null) return Optional.empty(); 38 | DecimalFormat format = ((DecimalFormat) NumberFormat.getNumberInstance(Locale.forLanguageTag(formatLocale))); 39 | format.applyPattern( 40 | ChatColor.translateAlternateColorCodes('&', convertEasyHexToLegacy(stringFormat))); 41 | return Optional.of(format); 42 | } 43 | 44 | /** 45 | * Replaces any occurrence of the easy hex format in the passed string with the legacy minecraft hex colour format. 46 | * More specifically, the easy hex format is defined by the colour escape character (§) followed by a hashtag and 47 | * six characters defining the hex colour. An example of this would be `§#FFFFFF` for white. 48 | *

49 | * As a note, the legacy hex format would contain the six hex characters all escaped by a § and prefixed with §x. 50 | * An example would be `§x§F§F§F§F§F§F` for white. 51 | * 52 | * @param easyHexString the source string containing the easy hex colour. 53 | * 54 | * @return the now fully legacy conform string containing the legacy hex representation. 55 | */ 56 | @NotNull 57 | public static String convertEasyHexToLegacy(@NotNull final String easyHexString) { 58 | final Matcher matcher = EASY_HEX_PATTERN.matcher(easyHexString); 59 | return matcher.replaceAll(r -> { 60 | final String group = r.group(); 61 | final StringBuilder builder = new StringBuilder(14); // 7 chars in total, all escaped. 62 | builder.append("§x"); 63 | 64 | for (int i = 2; i < 8; i++) { 65 | builder.append("§").append(group.charAt(i)); 66 | } 67 | return builder.toString(); 68 | }); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /NMS/1_19_R1/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_19_R1.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.19 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_19_R1 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new ClientboundAddEntityPacket((LivingEntity) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 41 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher, forceUpdateAll); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 47 | final int entityId = ((Entity) entity).getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 58 | worldServer, 59 | location.getX(), location.getY(), location.getZ() 60 | ); 61 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 62 | armorStand.setMarker(true); 63 | armorStand.setInvisible(true); 64 | armorStand.setCustomNameVisible(true); 65 | armorStand.setCustomName(name); 66 | 67 | return entityArmorStand; 68 | } 69 | 70 | @Override 71 | public void sendPacket(@NotNull Object packet, Collection players) { 72 | for (Player player : players) { 73 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 74 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 75 | playerConnection.send((Packet) packet); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /NMS/1_19_R3/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_19_R3.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.19 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_19_R3 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new ClientboundAddEntityPacket((LivingEntity) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 41 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 47 | final int entityId = ((Entity) entity).getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = 58 | new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /NMS/1_17_R1/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_17_R1.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddMobPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.entity.LivingEntity; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.17 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_17_R1 implements PacketManager { 28 | @NotNull 29 | @Override 30 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 31 | return new ClientboundAddMobPacket((LivingEntity) entity); 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 37 | final Entity entity1 = (Entity) entity; 38 | final int entityId = entity1.getId(); 39 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 40 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher, forceUpdateAll); 41 | } 42 | 43 | @NotNull 44 | @Override 45 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 46 | final Entity entity1 = (Entity) entity; 47 | final int entityId = entity1.getId(); 48 | return new ClientboundRemoveEntitiesPacket(entityId); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 54 | final World world = location.getWorld(); 55 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 56 | 57 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = 58 | new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /NMS/1_19_R2/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_19_R2.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager; 4 | import net.minecraft.network.protocol.Packet; 5 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 6 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 7 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 8 | import net.minecraft.network.syncher.SynchedEntityData; 9 | import net.minecraft.server.level.ServerLevel; 10 | import net.minecraft.server.level.ServerPlayer; 11 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 12 | import net.minecraft.world.entity.Entity; 13 | import net.minecraft.world.entity.LivingEntity; 14 | import org.bukkit.Location; 15 | import org.bukkit.World; 16 | import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; 17 | import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; 18 | import org.bukkit.entity.ArmorStand; 19 | import org.bukkit.entity.Player; 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import java.util.Collection; 23 | 24 | /** 25 | * Implementation of the packet manager for the 1.19 minecraft java version. 26 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 27 | */ 28 | public final class PacketManager1_19_R2 implements PacketManager { 29 | 30 | @NotNull 31 | @Override 32 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 33 | return new ClientboundAddEntityPacket((LivingEntity) entity); 34 | } 35 | 36 | @NotNull 37 | @Override 38 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 39 | final Entity entity1 = (Entity) entity; 40 | final int entityId = entity1.getId(); 41 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 42 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 48 | final int entityId = ((Entity) entity).getId(); 49 | return new ClientboundRemoveEntitiesPacket(entityId); 50 | } 51 | 52 | @NotNull 53 | @Override 54 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 55 | final World world = location.getWorld(); 56 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 57 | 58 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /NMS/1_16_R3/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_16_R3.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import com.google.common.base.Preconditions; 4 | import net.minecraft.server.v1_16_R3.DataWatcher; 5 | import net.minecraft.server.v1_16_R3.Entity; 6 | import net.minecraft.server.v1_16_R3.EntityArmorStand; 7 | import net.minecraft.server.v1_16_R3.EntityLiving; 8 | import net.minecraft.server.v1_16_R3.EntityPlayer; 9 | import net.minecraft.server.v1_16_R3.Packet; 10 | import net.minecraft.server.v1_16_R3.PacketPlayOutEntityDestroy; 11 | import net.minecraft.server.v1_16_R3.PacketPlayOutEntityMetadata; 12 | import net.minecraft.server.v1_16_R3.PacketPlayOutSpawnEntityLiving; 13 | import net.minecraft.server.v1_16_R3.PlayerConnection; 14 | import net.minecraft.server.v1_16_R3.WorldServer; 15 | import org.bukkit.Location; 16 | import org.bukkit.World; 17 | import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; 18 | import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; 19 | import org.bukkit.entity.ArmorStand; 20 | import org.bukkit.entity.Player; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | import java.util.Collection; 24 | 25 | /** 26 | * Implementation of the packet manager for the 1.16 minecraft java version. 27 | */ 28 | public final class PacketManager1_16_R3 implements PacketManager { 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | return new PacketPlayOutSpawnEntityLiving((EntityLiving) entity); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 38 | final Entity entity1 = (Entity) entity; 39 | final int entityId = entity1.getId(); 40 | final DataWatcher dataWatcher = entity1.getDataWatcher(); 41 | return new PacketPlayOutEntityMetadata(entityId, dataWatcher, forceUpdateAll); 42 | } 43 | 44 | @NotNull 45 | @Override 46 | @SuppressWarnings("PrimitiveArrayArgumentToVarargsMethod") 47 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 48 | final Entity entity1 = (Entity) entity; 49 | final int entityId = entity1.getId(); 50 | return new PacketPlayOutEntityDestroy(entityId); 51 | } 52 | 53 | @NotNull 54 | @Override 55 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 56 | final World world = location.getWorld(); 57 | Preconditions.checkArgument(world != null, "provided location did not have a world assigned"); 58 | 59 | final WorldServer worldServer = ((CraftWorld) world).getHandle(); 60 | 61 | final EntityArmorStand entityArmorStand = new EntityArmorStand( 62 | worldServer, 63 | location.getX(), location.getY(), location.getZ() 64 | ); 65 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 66 | armorStand.setMarker(true); 67 | armorStand.setInvisible(true); 68 | armorStand.setCustomNameVisible(true); 69 | armorStand.setCustomName(name); 70 | return entityArmorStand; 71 | } 72 | 73 | @Override 74 | public void sendPacket(@NotNull Object packet, Collection players) { 75 | for (Player player : players) { 76 | final EntityPlayer handle = ((CraftPlayer) player).getHandle(); 77 | final PlayerConnection playerConnection = handle.playerConnection; 78 | playerConnection.sendPacket((Packet) packet); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /NMS/1_21_R1/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_21_R1.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.21 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_21_R1 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | Entity entity1 = (Entity) entity; 33 | return new ClientboundAddEntityPacket(entity1.getId(), entity1.getUUID(), entity1.getX(), entity1.getY(), entity1.getZ(), entity1.getXRot(), entity1.getYRot(),entity1.getType(),0, Vec3.ZERO, entity1.getYHeadRot()); 34 | } 35 | 36 | @NotNull 37 | @Override 38 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 39 | final Entity entity1 = (Entity) entity; 40 | final int entityId = entity1.getId(); 41 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 42 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 48 | final int entityId = ((Entity) entity).getId(); 49 | return new ClientboundRemoveEntitiesPacket(entityId); 50 | } 51 | 52 | @NotNull 53 | @Override 54 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 55 | final World world = location.getWorld(); 56 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 57 | 58 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /NMS/1_21_R2/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_21_R2.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_21_R2.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.21 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_21_R2 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | Entity entity1 = (Entity) entity; 33 | return new ClientboundAddEntityPacket(entity1.getId(), entity1.getUUID(), entity1.getX(), entity1.getY(), entity1.getZ(), entity1.getXRot(), entity1.getYRot(),entity1.getType(),0, Vec3.ZERO, entity1.getYHeadRot()); 34 | } 35 | 36 | @NotNull 37 | @Override 38 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 39 | final Entity entity1 = (Entity) entity; 40 | final int entityId = entity1.getId(); 41 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 42 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 48 | final int entityId = ((Entity) entity).getId(); 49 | return new ClientboundRemoveEntitiesPacket(entityId); 50 | } 51 | 52 | @NotNull 53 | @Override 54 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 55 | final World world = location.getWorld(); 56 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 57 | 58 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /NMS/1_21_R3/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_21_R3.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_21_R3.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.21 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_21_R3 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | Entity entity1 = (Entity) entity; 33 | return new ClientboundAddEntityPacket(entity1.getId(), entity1.getUUID(), entity1.getX(), entity1.getY(), entity1.getZ(), entity1.getXRot(), entity1.getYRot(),entity1.getType(),0, Vec3.ZERO, entity1.getYHeadRot()); 34 | } 35 | 36 | @NotNull 37 | @Override 38 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 39 | final Entity entity1 = (Entity) entity; 40 | final int entityId = entity1.getId(); 41 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 42 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 48 | final int entityId = ((Entity) entity).getId(); 49 | return new ClientboundRemoveEntitiesPacket(entityId); 50 | } 51 | 52 | @NotNull 53 | @Override 54 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 55 | final World world = location.getWorld(); 56 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 57 | 58 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /NMS/1_21_R4/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_21_R4.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_21_R4.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.21.5 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_21_R4 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | Entity entity1 = (Entity) entity; 33 | return new ClientboundAddEntityPacket(entity1.getId(), entity1.getUUID(), entity1.getX(), entity1.getY(), entity1.getZ(), entity1.getXRot(), entity1.getYRot(),entity1.getType(),0, Vec3.ZERO, entity1.getYHeadRot()); 34 | } 35 | 36 | @NotNull 37 | @Override 38 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 39 | final Entity entity1 = (Entity) entity; 40 | final int entityId = entity1.getId(); 41 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 42 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 48 | final int entityId = ((Entity) entity).getId(); 49 | return new ClientboundRemoveEntitiesPacket(entityId); 50 | } 51 | 52 | @NotNull 53 | @Override 54 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 55 | final World world = location.getWorld(); 56 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 57 | 58 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /NMS/1_21_R5/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager1_21_R5.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import net.minecraft.network.protocol.Packet; 4 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 5 | import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; 6 | import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; 7 | import net.minecraft.network.syncher.SynchedEntityData; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 11 | import net.minecraft.world.entity.Entity; 12 | import net.minecraft.world.phys.Vec3; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.craftbukkit.v1_21_R5.CraftWorld; 16 | import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer; 17 | import org.bukkit.entity.ArmorStand; 18 | import org.bukkit.entity.Player; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.Collection; 22 | 23 | /** 24 | * Implementation of the packet manager for the 1.21.6 minecraft java version. 25 | * The implementation uses a mixture of direct calls against the re-obfuscated server internals and reflection. 26 | */ 27 | public final class PacketManager1_21_R5 implements PacketManager { 28 | 29 | @NotNull 30 | @Override 31 | public Object buildEntitySpawnPacket(@NotNull Object entity) { 32 | Entity entity1 = (Entity) entity; 33 | return new ClientboundAddEntityPacket(entity1.getId(), entity1.getUUID(), entity1.getX(), entity1.getY(), entity1.getZ(), entity1.getXRot(), entity1.getYRot(),entity1.getType(),0, Vec3.ZERO, entity1.getYHeadRot()); 34 | } 35 | 36 | @NotNull 37 | @Override 38 | public Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll) { 39 | final Entity entity1 = (Entity) entity; 40 | final int entityId = entity1.getId(); 41 | final SynchedEntityData dataWatcher = entity1.getEntityData(); 42 | return new ClientboundSetEntityDataPacket(entityId, dataWatcher.packDirty()); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public Object buildEntityDestroyPacket(@NotNull Object entity) { 48 | final int entityId = ((Entity) entity).getId(); 49 | return new ClientboundRemoveEntitiesPacket(entityId); 50 | } 51 | 52 | @NotNull 53 | @Override 54 | public Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) { 55 | final World world = location.getWorld(); 56 | final ServerLevel worldServer = ((CraftWorld) world).getHandle(); 57 | 58 | final net.minecraft.world.entity.decoration.ArmorStand entityArmorStand = new net.minecraft.world.entity.decoration.ArmorStand( 59 | worldServer, 60 | location.getX(), location.getY(), location.getZ() 61 | ); 62 | final ArmorStand armorStand = (ArmorStand) entityArmorStand.getBukkitEntity(); 63 | armorStand.setMarker(true); 64 | armorStand.setInvisible(true); 65 | armorStand.setCustomNameVisible(true); 66 | armorStand.setCustomName(name); 67 | 68 | return entityArmorStand; 69 | } 70 | 71 | @Override 72 | public void sendPacket(@NotNull Object packet, Collection players) { 73 | for (Player player : players) { 74 | final ServerPlayer handle = ((CraftPlayer) player).getHandle(); 75 | final ServerGamePacketListenerImpl playerConnection = handle.connection; 76 | playerConnection.send((Packet) packet); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/java/io/github/magiccheese1/damageindicator/IndicatorEntityImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.Location; 6 | import org.bukkit.entity.LivingEntity; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.plugin.Plugin; 9 | import org.bukkit.scheduler.BukkitTask; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.text.DecimalFormat; 13 | import java.util.Collection; 14 | 15 | 16 | /** 17 | * Object for representing indicators. 18 | * use {@link DamageIndicatorImpl#spawnIndicator(LivingEntity, Player, DecimalFormat, double)} for spawning a new one. 19 | * IndicatorEntity is not an actual minecraft entity. DamageIndicator uses fake entities that only exist in the clients 20 | * world. 21 | */ 22 | public class IndicatorEntityImpl implements IndicatorEntity { 23 | private final Plugin plugin; 24 | private final PacketManager packetManager; 25 | private final Location location; 26 | private final double value; 27 | private final DecimalFormat format; 28 | 29 | private final Collection visibleTo; 30 | private Object armorstandEntity; 31 | private boolean alive = false; 32 | 33 | /** 34 | * Use {@link #spawn()} to show indicator to players. 35 | * Creating a IndicatorEntity with 36 | * {@link DamageIndicatorImpl#spawnIndicator(LivingEntity, Player, DecimalFormat, double)} preferred. 37 | */ 38 | public IndicatorEntityImpl(Plugin plugin, PacketManager packetManager, Location location, double value, 39 | DecimalFormat format, 40 | Collection visibleTo) { 41 | this.plugin = plugin; 42 | this.packetManager = packetManager; 43 | this.location = location; 44 | this.value = value; 45 | this.format = format; 46 | this.visibleTo = visibleTo; 47 | } 48 | 49 | @Override 50 | public void spawn() { 51 | armorstandEntity = packetManager.buildEntityArmorStand(getLocation(), getFormat().format(value)); 52 | 53 | Object entitySpawnPacket = packetManager.buildEntitySpawnPacket(armorstandEntity); 54 | Object entityMetadataPacket = packetManager.buildEntityMetadataPacket(armorstandEntity, false); 55 | 56 | packetManager.sendPacket(entitySpawnPacket, visibleTo); 57 | packetManager.sendPacket(entityMetadataPacket, visibleTo); 58 | 59 | alive = true; 60 | } 61 | 62 | @Override 63 | public double getValue() { 64 | return value; 65 | } 66 | 67 | @Override 68 | @NotNull 69 | public Location getLocation() { 70 | return location; 71 | } 72 | 73 | @Override 74 | @NotNull 75 | public DecimalFormat getFormat() { 76 | return format; 77 | } 78 | 79 | 80 | @Override 81 | public BukkitTask scheduleDestroy(long tickDelay) { 82 | return Bukkit.getScheduler().runTaskLater(this.plugin, this::destroy, tickDelay); 83 | } 84 | 85 | @Override 86 | public void destroy() { 87 | final Object entityDestroyPacket = packetManager.buildEntityDestroyPacket(armorstandEntity); 88 | 89 | packetManager.sendPacket(entityDestroyPacket, visibleTo); 90 | 91 | alive = false; 92 | } 93 | 94 | @Override 95 | public Collection getVisibleTo() { 96 | return visibleTo; 97 | } 98 | 99 | @Override 100 | public boolean isAlive() { 101 | return alive; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release-project 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | 12 | - name: Cache Spigot and gradle 13 | id: cache-spigot-gradle 14 | uses: actions/cache@v4 15 | with: 16 | path: | 17 | ~/.m2/ 18 | ~/.gradle/caches/ 19 | ~/.gradle/wrapper/ 20 | ./BuildTools/ 21 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'BuildTools/buildtools.jar') }} 22 | 23 | - name: prepare Buildtools 24 | run: mkdir BuildTools -p && cd BuildTools && wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar 25 | 26 | - name: prepare installSpigot script 27 | run: cd BuildTools && cp ../scripts/installSpigot.sh ./installSpigot.sh && chmod -x ./installSpigot.sh 28 | 29 | - name: set up JDK 8 30 | uses: actions/setup-java@v4 31 | with: 32 | distribution: 'temurin' 33 | java-version: '8' 34 | 35 | - name: install spigot 1.16.5 36 | run: | 37 | cd BuildTools 38 | chmod +x ./installSpigot.sh 39 | ./installSpigot.sh 1.16.5 40 | 41 | - name: set up JDK 17 42 | uses: actions/setup-java@v4 43 | with: 44 | distribution: 'temurin' 45 | java-version: '17' 46 | - name: install spigot 1.17.1, 1.18.2, 1.19.x 47 | run: | 48 | cd BuildTools 49 | chmod +x ./installSpigot.sh 50 | ./installSpigot.sh 1.17.1 51 | ./installSpigot.sh 1.18.2 52 | ./installSpigot.sh 1.19.2 53 | ./installSpigot.sh 1.19.3 54 | ./installSpigot.sh 1.19.4 55 | 56 | - name: set up JDK 21 57 | uses: actions/setup-java@v4 58 | with: 59 | distribution: 'temurin' 60 | java-version: '21' 61 | 62 | - name: install spigot 1.20.x, 1.21.x 63 | run: | 64 | cd BuildTools 65 | chmod +x ./installSpigot.sh 66 | ./installSpigot.sh 1.20.1 67 | ./installSpigot.sh 1.20.2 68 | ./installSpigot.sh 1.20.4 69 | ./installSpigot.sh 1.20.6 70 | ./installSpigot.sh 1.21.1 71 | ./installSpigot.sh 1.21.3 72 | ./installSpigot.sh 1.21.4 73 | ./installSpigot.sh 1.21.5 74 | ./installSpigot.sh 1.21.6 75 | 76 | - name: return to root 77 | run: cd .. 78 | 79 | - name: Setup gradlew permissions 80 | run: chmod +x ./gradlew 81 | 82 | - name: Build with Gradle 83 | run: ./gradlew build 84 | 85 | - name: Cleanup Gradle Cache 86 | # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 87 | # Restoring these files from a GitHub Actions cache might cause problems for future builds. 88 | run: | 89 | rm -f ~/.gradle/caches/modules-2/modules-2.lock 90 | rm -f ~/.gradle/caches/modules-2/gc.properties 91 | 92 | - name: Rename impl output file 93 | if: startsWith(github.ref, 'refs/tags/') 94 | run: find ./DamageIndicator/build/libs/ -type f -name '*-all.jar' -print0 -quit | xargs --null -I{} mv {} "./DamageIndicator/build/libs/DamageIndicator.jar" 95 | 96 | - name: Rename API-Javadoc output file 97 | if: startsWith(github.ref, 'refs/tags/') 98 | run: find ./API/build/libs/ -type f -name '*-javadoc.jar' -print0 -quit | xargs --null -I{} mv {} "./API/build/libs/DamageIndicator-API-javadoc.jar" 99 | 100 | - name: Rename API-sources output file 101 | if: startsWith(github.ref, 'refs/tags/') 102 | run: find ./API/build/libs/ -type f -name '*-sources.jar' -print0 -quit | xargs --null -I{} mv {} "./API/build/libs/DamageIndicator-API-sources.jar" 103 | 104 | - name: Rename API output file 105 | if: startsWith(github.ref, 'refs/tags/') 106 | run: find ./API/build/libs/ -type f -name '*.jar' -print0 -quit | xargs --null -I{} mv {} "./API/build/libs/DamageIndicator-API.jar" 107 | 108 | 109 | 110 | - name: Release 111 | uses: softprops/action-gh-release@v1 112 | if: startsWith(github.ref, 'refs/tags/') 113 | with: 114 | files: | 115 | DamageIndicator/build/libs/DamageIndicator.jar 116 | API/build/libs/DamageIndicator-API.jar 117 | API/build/libs/DamageIndicator-API-javadoc.jar 118 | API/build/libs/DamageIndicator-API-sources.jar 119 | 120 | env: 121 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 122 | -------------------------------------------------------------------------------- /API/src/main/java/io/github/magiccheese1/damageindicator/DamageIndicator.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Location; 5 | import org.bukkit.entity.LivingEntity; 6 | import org.bukkit.entity.Player; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.text.DecimalFormat; 11 | import java.util.Objects; 12 | 13 | public interface DamageIndicator { 14 | 15 | /** 16 | * Provides the shared singleton instance of the damage indicator interface. 17 | * 18 | * @return the instance. 19 | */ 20 | @NotNull 21 | static DamageIndicator instance() { 22 | return Objects.requireNonNull( 23 | Bukkit.getServicesManager().load(DamageIndicator.class), 24 | "Failed to load DamageIndicator implementation" 25 | ); 26 | } 27 | 28 | /** 29 | * Function for spawning a DamageIndicator. 30 | * 31 | * @param location where to spawn the Indicator. 32 | * @param credit the player that has dealt the damage. (see ShowToDamagerOnly in config) 33 | * @param format the format to show the damage in. 34 | * @param value the amount of damage that was dealt. 35 | * @param lifespan how long the Indicator will be visible for. If lifespan is 0, it will not be destroyed 36 | * automatically. NOTICE: The Indicator is not guaranteed to be visible for anyone who rejoined 37 | * the server or was far away when it was spawned. The Indicators are not meant to work like 38 | * holograms. 39 | * 40 | * @return the indicator object. 41 | */ 42 | @NotNull 43 | IndicatorEntity spawnIndicator(@NotNull Location location, 44 | @Nullable Player credit, 45 | @NotNull DecimalFormat format, 46 | double value, 47 | long lifespan); 48 | 49 | /** 50 | * Function for spawning a DamageIndicator. 51 | * 52 | * @param entity The entity that is used for finding a valid indicator location. 53 | * @param credit The player that has dealt the damage. (see ShowToDamagerOnly in config) 54 | * @param format The format to show the damage in. 55 | * @param value The amount of damage that was dealt. 56 | * @param lifespan How long the Indicator will be visible for. If lifespan is 0, it will not be destroyed 57 | * automatically. NOTICE: The Indicator is not guaranteed to be visible for anyone who rejoined 58 | * the server or was far away when it was spawned. The Indicators are not meant to work like 59 | * holograms. 60 | * 61 | * @return the indicator object. 62 | */ 63 | @NotNull 64 | IndicatorEntity spawnIndicator(@NotNull LivingEntity entity, 65 | @Nullable Player credit, 66 | @NotNull DecimalFormat format, 67 | double value, 68 | long lifespan); 69 | 70 | /** 71 | * Function for spawning a DamageIndicator. 72 | * 73 | * @param entity The entity that is used for finding a valid indicator location. 74 | * @param credit The player that has dealt the damage. (see ShowToDamagerOnly in config) 75 | * @param format The format to show the damage in. 76 | * @param value The amount of damage that was dealt. 77 | * 78 | * @return the indicator object. 79 | */ 80 | @NotNull 81 | IndicatorEntity spawnIndicator(@NotNull LivingEntity entity, 82 | @Nullable Player credit, 83 | @NotNull DecimalFormat format, 84 | double value); 85 | 86 | /** 87 | * Function for spawning a DamageIndicator. 88 | * 89 | * @param location Where to spawn the Indicator. 90 | * @param credit The credit that has dealt the damage. (see ShowToDamagerOnly in config) 91 | * @param format The format to show the damage in. 92 | * @param value The amount of damage that was dealt. 93 | * 94 | * @return the indicator object. 95 | */ 96 | @NotNull 97 | IndicatorEntity spawnIndicator(@NotNull Location location, 98 | @Nullable Player credit, 99 | @NotNull DecimalFormat format, 100 | double value); 101 | 102 | DecimalFormat getDamageFormat(); 103 | 104 | DecimalFormat getCriticalDamageFormat(); 105 | 106 | DecimalFormat getPoisonDamageFormat(); 107 | } 108 | -------------------------------------------------------------------------------- /API/src/main/java/io/github/magiccheese1/damageindicator/packetManager/PacketManager.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator.packetManager; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.entity.ArmorStand; 5 | import org.bukkit.entity.Player; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | /** 12 | * Abstraction interface representing any form of interaction with the server internal implementation. 13 | * As the server implementation may differ between minecraft releases, this interface is implemented for each supported 14 | * minecraft version. 15 | */ 16 | public interface PacketManager { 17 | 18 | /** 19 | * Creates a new entity spawn packet for a virtual entity instance. 20 | * The packet, when send to the client, causes the client to display the entity instance at the respective location. 21 | * The spawn packet, by definition, does not include any metadata about the entity such as the custom name. 22 | * 23 | * @param entity the entity instance for which the spawn packet is to be constructed. This instance should be an 24 | * instance of the server internal entity class, instead of the spigot/bukkit classes. It should hence 25 | * most likely be constructed through {@link #buildEntityArmorStand(Location, String)}. 26 | * 27 | * @return the created packet instance. This packet may be send to a player using {@link 28 | * #sendPacket(Object, Player)}. 29 | */ 30 | @NotNull 31 | Object buildEntitySpawnPacket(@NotNull Object entity); 32 | 33 | /** 34 | * Creates a new entity metadata packet for a virtual entity instance. 35 | * The packet, when send to the client, causes the client to update its local data watcher for the entity, updating 36 | * data like the custom name or the pose. 37 | * 38 | * @param entity the entity instance for which the metadata packet is to be constructed. This instance 39 | * should be an instance of the server internal entity class, instead of the spigot/bukkit 40 | * classes. It should hence most likely be constructed through {@link 41 | * #buildEntityArmorStand(Location, String)}. 42 | * @param forceUpdateAll whether or not the entire data watcher should be published to the client. If forceUpdateAll 43 | * is {@link Boolean#FALSE} only flags marked as dirty will be published to the client. 44 | * 45 | * @return the created packet instance. This packet may be send to a player using 46 | * {@link #sendPacket(Object, Player)}. 47 | */ 48 | @NotNull 49 | Object buildEntityMetadataPacket(@NotNull Object entity, boolean forceUpdateAll); 50 | 51 | /** 52 | * Creates a new entity destroy packet for a virtual entity instance. 53 | * The packet, when send to the client, causes the client to no longer display the entity for the user. 54 | * 55 | * @param entity the entity instance for which the destroy-packet is to be constructed. This instance should be an 56 | * instance of the server internal entity class, instead of the spigot/bukkit classes. 57 | * It should hence most likely be constructed through {@link #buildEntityArmorStand(Location, 58 | * String)}. 59 | * 60 | * @return the created packet instance. This packet may be send to a player using 61 | * {@link #sendPacket(Object, Player)}. 62 | */ 63 | @NotNull 64 | Object buildEntityDestroyPacket(@NotNull Object entity); 65 | 66 | /** 67 | * Creates a new instance of the server internal {@link ArmorStand} representation at any given location. 68 | * Besides defining the location, this method will also give the armor stand a specific custom name which is also 69 | * made visible. 70 | * The armor stand additionally is made invisible and updated to a marker, effectively setting removing its hitbox. 71 | * 72 | * @param location the location at which the armor stand should be spawned. 73 | * @param name the custom display name of the armor stand. This display name is passed in the bukkit legacy 74 | * format, using § denoted colour codes. 75 | * 76 | * @return the created entity armor stand instance. This instance is not actually added to any world but will have a 77 | * unique entity id for client side tracking. 78 | * 79 | * @throws IllegalArgumentException if the provided location did not have a world assigned to it. 80 | */ 81 | @NotNull 82 | Object buildEntityArmorStand(@NotNull Location location, @NotNull String name) throws IllegalArgumentException; 83 | 84 | /** 85 | * Queues a new packet in the packet queue of a player. 86 | * This method will not flush the packet queue, the passed packet will hence be published to the player in the next 87 | * full server tick. 88 | * 89 | * @param packet the server internal representation of the packet that should be send to the player. 90 | * @param player the player instance that is going to receive the packet. 91 | */ 92 | default void sendPacket(@NotNull Object packet, @NotNull Player player) { 93 | this.sendPacket(packet, List.of(player)); 94 | } 95 | 96 | void sendPacket(@NotNull Object packet, Collection players); 97 | 98 | } 99 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/java/io/github/magiccheese1/damageindicator/DamageIndicatorImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import com.tchristofferson.configupdater.ConfigUpdater; 4 | import io.github.magiccheese1.damageindicator.config.Options; 5 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager; 6 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_16_R3; 7 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_17_R1; 8 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_18_R1; 9 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_19_R1; 10 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_19_R2; 11 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_19_R3; 12 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_20_R1; 13 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_20_R2; 14 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_20_R3; 15 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_20_R4; 16 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_21_R1; 17 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_21_R2; 18 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_21_R3; 19 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_21_R4; 20 | import io.github.magiccheese1.damageindicator.packetManager.PacketManager1_21_R5; 21 | import org.bukkit.Bukkit; 22 | import org.bukkit.Location; 23 | import org.bukkit.entity.Entity; 24 | import org.bukkit.entity.LivingEntity; 25 | import org.bukkit.entity.Player; 26 | import org.bukkit.plugin.ServicePriority; 27 | import org.bukkit.plugin.java.JavaPlugin; 28 | import org.jetbrains.annotations.NotNull; 29 | import org.jetbrains.annotations.Nullable; 30 | 31 | import java.io.File; 32 | import java.io.IOException; 33 | import java.text.DecimalFormat; 34 | import java.util.ArrayList; 35 | import java.util.Collection; 36 | import java.util.Collections; 37 | import java.util.concurrent.ThreadLocalRandom; 38 | 39 | import static io.github.magiccheese1.damageindicator.config.ConfigUtility.getConfigurationDamageFormat; 40 | 41 | public class DamageIndicatorImpl extends JavaPlugin implements DamageIndicator { 42 | PacketManager packetManager; 43 | 44 | @Override 45 | public void onEnable() { 46 | // Save the default config from src/resources/config.yml 47 | saveDefaultConfig(); 48 | // The config needs to exist before using the updater 49 | File configFile = new File(getDataFolder(), "config.yml"); 50 | try { 51 | ConfigUpdater.update(this, "config.yml", configFile, Collections.emptyList()); 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | } 55 | reloadConfig(); 56 | // Register Command 57 | getCommand("damageindicator").setExecutor(new CommandReload(this)); 58 | 59 | // Get current minecraft version 60 | final String serverName = Bukkit.getServer().getClass().getPackage().getName(); 61 | String serverVersion = null; 62 | if (!serverName.contains(".")) { 63 | serverVersion = serverName.split("\\.")[3].trim(); 64 | } else { 65 | serverVersion = Bukkit.getServer().getBukkitVersion().split("-")[0].trim(); 66 | } 67 | switch (serverVersion) { 68 | case "1.16.4", "1.16.5" -> packetManager = new PacketManager1_16_R3(); 69 | case "1.17", "1.17.1" -> packetManager = new PacketManager1_17_R1(); 70 | case "1.18", "1.18.1", "1.18.2" -> packetManager = new PacketManager1_18_R1(); 71 | case "1.19", "1.19.1", "1.19.2" -> packetManager = new PacketManager1_19_R1(); 72 | case "1.19.3" -> packetManager = new PacketManager1_19_R2(); 73 | case "1.19.4" -> packetManager = new PacketManager1_19_R3(); 74 | case "1.20", "1.20.1" -> packetManager = new PacketManager1_20_R1(); 75 | case "1.20.2" -> packetManager = new PacketManager1_20_R2(); 76 | case "1.20.3", "1.20.4" -> packetManager = new PacketManager1_20_R3(); 77 | case "1.20.5", "1.20.6" -> packetManager = new PacketManager1_20_R4(); 78 | case "1.21", "1.21.1", "1.21.2" -> packetManager = new PacketManager1_21_R1(); 79 | case "1.21.3" -> packetManager = new PacketManager1_21_R2(); 80 | case "1.21.4" -> packetManager = new PacketManager1_21_R3(); 81 | case "1.21.5" -> packetManager = new PacketManager1_21_R4(); 82 | case "1.21.6", "1.21.7", "1.21.8" -> packetManager = new PacketManager1_21_R5(); 83 | 84 | default -> throw new RuntimeException("Failed to create version specific server accessor"); 85 | } 86 | getLogger().info(String.format("Using server version accessor for %s", serverVersion)); 87 | getServer().getPluginManager().registerEvents(new BukkitEventListener(this), this); 88 | 89 | getServer().getServicesManager().register(DamageIndicator.class, this, this, ServicePriority.Normal); 90 | } 91 | 92 | private static Location findLocation(LivingEntity entity) { 93 | final ThreadLocalRandom random = ThreadLocalRandom.current(); 94 | for (int i = 0; i < 20; i++) { 95 | Location location = entity.getLocation().add( 96 | random.nextDouble(0, 2) - 1, entity.getEyeHeight(), random.nextDouble(0, 2) - 1); 97 | if (location.getBlock().isPassable()) 98 | return location; 99 | } 100 | return entity.getLocation(); 101 | } 102 | 103 | @Override 104 | @NotNull 105 | public IndicatorEntity spawnIndicator(@NotNull Location location, 106 | @Nullable Player credit, 107 | @NotNull DecimalFormat format, 108 | double value, 109 | long lifespan) { 110 | Collection visibleTo = new ArrayList<>(); 111 | if (getConfig().getBoolean(Options.SHOW_DAMAGE_ONLY) && credit != null) 112 | visibleTo.add(credit); 113 | else { 114 | Collection nearbyEntities = location.getWorld().getNearbyEntities(location, 16, 16, 16); 115 | for (Entity entity : nearbyEntities) 116 | if (entity instanceof Player) 117 | visibleTo.add((Player) entity); 118 | } 119 | IndicatorEntity indicator = new IndicatorEntityImpl(this, packetManager, location, value, format, visibleTo); 120 | indicator.spawn(); 121 | if (lifespan != 0) 122 | indicator.scheduleDestroy(lifespan); 123 | return indicator; 124 | } 125 | 126 | @Override 127 | @NotNull 128 | public IndicatorEntity spawnIndicator(@NotNull LivingEntity entity, 129 | @Nullable Player credit, 130 | @NotNull DecimalFormat format, 131 | double value, 132 | long lifespan) { 133 | return spawnIndicator(findLocation(entity), credit, format, value, lifespan); 134 | } 135 | 136 | @Override 137 | @NotNull 138 | public IndicatorEntity spawnIndicator(@NotNull LivingEntity entity, 139 | @Nullable Player credit, 140 | @NotNull DecimalFormat format, 141 | double value) { 142 | 143 | return spawnIndicator(entity, credit, format, value, (long) (getConfig().getDouble(Options.INDICATOR_TIME, 144 | 1.5) * 20)); 145 | } 146 | 147 | @Override 148 | @NotNull 149 | public IndicatorEntity spawnIndicator(@NotNull Location location, 150 | @Nullable Player credit, 151 | @NotNull DecimalFormat format, 152 | double value) { 153 | return spawnIndicator(location, credit, format, value, (long) (getConfig().getDouble(Options.INDICATOR_TIME, 154 | 1.5) * 20)); 155 | } 156 | 157 | @Override 158 | public DecimalFormat getDamageFormat() { 159 | return getConfigurationDamageFormat(getConfig(), Options.FORMAT_INDICATOR).orElseThrow( 160 | () -> new IllegalStateException("Plugin configuration did not provide indicator format")); 161 | } 162 | 163 | @Override 164 | public DecimalFormat getCriticalDamageFormat() { 165 | return getConfigurationDamageFormat(getConfig(), Options.CRITICAL_FORMAT).orElseThrow( 166 | () -> new IllegalStateException("Plugin configuration did not provide indicator format")); 167 | } 168 | 169 | @Override 170 | public DecimalFormat getPoisonDamageFormat() { 171 | return getConfigurationDamageFormat(getConfig(), Options.POISON_FORMAT).orElseThrow( 172 | () -> new IllegalStateException("Plugin configuration did not provide indicator format")); 173 | } 174 | 175 | 176 | } 177 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /DamageIndicator/src/main/java/io/github/magiccheese1/damageindicator/BukkitEventListener.java: -------------------------------------------------------------------------------- 1 | package io.github.magiccheese1.damageindicator; 2 | 3 | import io.github.magiccheese1.damageindicator.config.Options; 4 | import org.bukkit.NamespacedKey; 5 | import org.bukkit.configuration.file.FileConfiguration; 6 | import org.bukkit.enchantments.Enchantment; 7 | import org.bukkit.entity.AbstractArrow; 8 | import org.bukkit.entity.AreaEffectCloud; 9 | import org.bukkit.entity.ArmorStand; 10 | import org.bukkit.entity.Arrow; 11 | import org.bukkit.entity.LivingEntity; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.entity.Projectile; 14 | import org.bukkit.entity.ThrownPotion; 15 | import org.bukkit.event.EventHandler; 16 | import org.bukkit.event.EventPriority; 17 | import org.bukkit.event.Listener; 18 | import org.bukkit.event.entity.AreaEffectCloudApplyEvent; 19 | import org.bukkit.event.entity.EntityDamageByEntityEvent; 20 | import org.bukkit.event.entity.EntityDamageEvent; 21 | import org.bukkit.event.entity.LingeringPotionSplashEvent; 22 | import org.bukkit.event.entity.PotionSplashEvent; 23 | import org.bukkit.persistence.PersistentDataContainer; 24 | import org.bukkit.persistence.PersistentDataType; 25 | import org.bukkit.potion.PotionData; 26 | import org.bukkit.potion.PotionEffect; 27 | import org.bukkit.potion.PotionEffectType; 28 | import org.bukkit.potion.PotionType; 29 | import org.jetbrains.annotations.NotNull; 30 | 31 | import java.text.DecimalFormat; 32 | import java.util.Objects; 33 | import java.util.UUID; 34 | 35 | import static io.github.magiccheese1.damageindicator.Utility.poisonArrowEffectDuration; 36 | import static io.github.magiccheese1.damageindicator.Utility.poisonLingeringPotionEffectDuration; 37 | import static io.github.magiccheese1.damageindicator.config.ConfigUtility.getConfigurationDamageFormat; 38 | 39 | public class BukkitEventListener implements Listener { 40 | 41 | private final DamageIndicatorImpl damageIndicator; 42 | private final NamespacedKey poisonedByKey; 43 | private final NamespacedKey burnedByKey; 44 | private final NamespacedKey harmedByKey; 45 | 46 | public BukkitEventListener(@NotNull final DamageIndicatorImpl damageIndicator) { 47 | this.damageIndicator = damageIndicator; 48 | this.poisonedByKey = new NamespacedKey(damageIndicator, "poisoned-by"); 49 | this.burnedByKey = new NamespacedKey(damageIndicator, "burned-by"); 50 | this.harmedByKey = new NamespacedKey(damageIndicator, "harmed-by"); 51 | } 52 | 53 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 54 | public void entityDamage(EntityDamageEvent event) { 55 | if (!(event.getEntity() instanceof LivingEntity livingEntity)) return; 56 | 57 | PersistentDataContainer container = event.getEntity().getPersistentDataContainer(); 58 | //POISON DAMAGE 59 | if (container.has(poisonedByKey, PersistentDataType.STRING) && event.getCause() == EntityDamageEvent.DamageCause.POISON) { 60 | 61 | final FileConfiguration configuration = this.damageIndicator.getConfig(); 62 | DecimalFormat poisonFormat = getConfigurationDamageFormat(configuration, Options.POISON_FORMAT).orElseThrow( 63 | () -> new IllegalStateException("Plugin configuration did not provide indicator format") 64 | ); 65 | 66 | damageIndicator.spawnIndicator( 67 | livingEntity, 68 | damageIndicator.getServer().getPlayer(UUID.fromString(container.get(poisonedByKey, 69 | PersistentDataType.STRING))), 70 | poisonFormat, 71 | event.getFinalDamage() 72 | ); 73 | //FIRE DAMAGE 74 | } else if (container.has(burnedByKey, PersistentDataType.STRING) && event.getCause() == EntityDamageEvent.DamageCause.FIRE_TICK) { 75 | final FileConfiguration configuration = this.damageIndicator.getConfig(); 76 | DecimalFormat burnFormat = getConfigurationDamageFormat(configuration, Options.BURN_FORMAT).orElseThrow( 77 | () -> new IllegalStateException("Plugin configuration did not provide indicator format") 78 | ); 79 | 80 | damageIndicator.spawnIndicator( 81 | livingEntity, 82 | damageIndicator.getServer().getPlayer(UUID.fromString(container.get(burnedByKey, 83 | PersistentDataType.STRING))), 84 | burnFormat, 85 | event.getFinalDamage() 86 | ); 87 | } 88 | } 89 | 90 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 91 | public void potionSplash(PotionSplashEvent event) { 92 | if (!(event.getPotion().getShooter() instanceof Player damager)) return; 93 | 94 | PotionEffect effect = event.getPotion().getEffects().stream() 95 | .filter(x -> x.getType().equals(PotionEffectType.POISON)) 96 | .findAny() 97 | .orElse(null); 98 | 99 | if (Objects.isNull(effect)) return; 100 | for (LivingEntity entity : event.getAffectedEntities()) { 101 | markEntityAndQueueUnmark(entity, damager.getUniqueId(), effect.getDuration(), poisonedByKey); 102 | } 103 | } 104 | 105 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 106 | public void lingeringPotionSplash(LingeringPotionSplashEvent event) { 107 | if (!(event.getEntity().getShooter() instanceof Player shooter)) return; 108 | if(event.getAreaEffectCloud().getBasePotionData() == null) return; 109 | if (event.getAreaEffectCloud().getBasePotionData().getType() == PotionType.POISON) { 110 | event.getAreaEffectCloud().getPersistentDataContainer().set( 111 | poisonedByKey, PersistentDataType.STRING, shooter.getUniqueId().toString() 112 | ); 113 | } else if (event.getAreaEffectCloud().getBasePotionData().getType() == PotionType.INSTANT_DAMAGE) { 114 | event.getAreaEffectCloud().getPersistentDataContainer().set( 115 | harmedByKey, PersistentDataType.STRING, shooter.getUniqueId().toString() 116 | ); 117 | } 118 | } 119 | 120 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 121 | public void areaEffectCloudApply(AreaEffectCloudApplyEvent event) { 122 | if(event.getEntity().getBasePotionData() == null) return; 123 | if (event.getEntity().getBasePotionData().getType() != PotionType.POISON) return; 124 | 125 | PersistentDataContainer container = event.getEntity().getPersistentDataContainer(); 126 | if (!container.has(poisonedByKey, PersistentDataType.STRING)) return; 127 | 128 | final UUID uuid = UUID.fromString(container.get(poisonedByKey, PersistentDataType.STRING)); 129 | 130 | for (LivingEntity entity : event.getAffectedEntities()) { 131 | markEntityAndQueueUnmark( 132 | entity, 133 | uuid, 134 | poisonLingeringPotionEffectDuration(event.getEntity().getBasePotionData()), 135 | poisonedByKey 136 | ); 137 | } 138 | } 139 | 140 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 141 | public void entityDamageByEntity(EntityDamageByEntityEvent event) { 142 | if (!(event.getEntity() instanceof final LivingEntity entity)) return; 143 | if (entity instanceof ArmorStand) return; 144 | 145 | final FileConfiguration configuration = this.damageIndicator.getConfig(); 146 | Player damager = null; 147 | DecimalFormat damageFormat = getConfigurationDamageFormat( 148 | configuration, 149 | Options.FORMAT_INDICATOR 150 | ).orElseThrow(() -> new IllegalStateException("Plugin configuration did not provide indicator " + 151 | "format")); 152 | if (event.getDamager() instanceof final AreaEffectCloud areaEffectCloud) { 153 | if(areaEffectCloud.getBasePotionData() == null) return; 154 | if (areaEffectCloud.getBasePotionData().getType() == PotionType.INSTANT_DAMAGE) { 155 | if (areaEffectCloud.getPersistentDataContainer().has(harmedByKey, PersistentDataType.STRING)) { 156 | damager = 157 | damageIndicator.getServer().getPlayer(UUID.fromString( 158 | areaEffectCloud.getPersistentDataContainer().get(harmedByKey, PersistentDataType.STRING))); 159 | damageFormat = 160 | getConfigurationDamageFormat(configuration, Options.INSTANT_DAMAGE_FORMAT).orElseThrow( 161 | () -> new IllegalStateException( 162 | "Plugin configuration did not provide instant damage indicator format" 163 | ) 164 | ); 165 | } 166 | } 167 | } else if (event.getDamager() instanceof final Projectile projectile) { 168 | if (!(projectile.getShooter() instanceof Player player)) return; 169 | 170 | damager = player; 171 | if (damager.getInventory().getItemInMainHand().getEnchantmentLevel(Enchantment.ARROW_FIRE) > 0) 172 | markEntityAndQueueUnmark(entity, damager.getUniqueId(), 100, burnedByKey); 173 | 174 | if (projectile instanceof ThrownPotion potion) { 175 | if (potion.getEffects().stream().anyMatch(e -> e.getType().equals(PotionEffectType.HARM))) { 176 | damageFormat = 177 | getConfigurationDamageFormat(configuration, Options.INSTANT_DAMAGE_FORMAT).orElseThrow( 178 | () -> new IllegalStateException( 179 | "Plugin configuration did not provide instant damage indicator " + 180 | "format")); 181 | } 182 | } else if (projectile instanceof AbstractArrow abstractArrow) { 183 | 184 | if (abstractArrow.isCritical()) { 185 | damageFormat = getConfigurationDamageFormat(configuration, Options.CRITICAL_FORMAT).orElseThrow( 186 | () -> new IllegalStateException("Plugin configuration did not provide critical indicator " + 187 | "format") 188 | ); 189 | } 190 | 191 | if (projectile instanceof Arrow arrow) { 192 | if (arrow.getBasePotionData() != null) { // Can return null in newer versions 193 | PotionData potionData = arrow.getBasePotionData(); 194 | if (potionData.getType() == PotionType.POISON) { 195 | markEntityAndQueueUnmark( 196 | entity, 197 | damager.getUniqueId(), 198 | poisonArrowEffectDuration(((Arrow) projectile).getBasePotionData()), 199 | poisonedByKey 200 | ); 201 | } else if (potionData.getType() == PotionType.INSTANT_DAMAGE) { 202 | damageFormat = 203 | getConfigurationDamageFormat(configuration, Options.INSTANT_DAMAGE_FORMAT).orElseThrow( 204 | () -> new IllegalStateException("Plugin configuration did not provide instant " + 205 | "damage " + 206 | "indicator " + 207 | "format")); 208 | } 209 | } 210 | } 211 | } 212 | } else if (event.getDamager() instanceof Player) { 213 | damager = (Player) event.getDamager(); 214 | 215 | //mark burning 216 | if (damager.getInventory().getItemInMainHand().getEnchantmentLevel(Enchantment.FIRE_ASPECT) > 0) 217 | markEntityAndQueueUnmark(entity, damager.getUniqueId(), 80, burnedByKey); 218 | 219 | if (Utility.isCritical(damager)) { 220 | damageFormat = getConfigurationDamageFormat(configuration, Options.CRITICAL_FORMAT).orElseThrow( 221 | () -> new IllegalStateException("Plugin configuration did not provide critical indicator format") 222 | ); 223 | } 224 | } 225 | 226 | if (damager == null) return; // Could not parse the damaging player from the event. 227 | 228 | damageIndicator.spawnIndicator( 229 | entity, 230 | damager, 231 | damageFormat, 232 | event.getFinalDamage() 233 | ); 234 | } 235 | 236 | private void markEntityAndQueueUnmark(@NotNull LivingEntity entity, 237 | @NotNull UUID damagerUUID, 238 | int effectDuration, NamespacedKey markingKey) { 239 | entity.getPersistentDataContainer().set(markingKey, PersistentDataType.STRING, damagerUUID.toString()); 240 | this.damageIndicator.getServer().getScheduler().runTaskLater(this.damageIndicator, () -> { 241 | if (Objects.equals( 242 | entity.getPersistentDataContainer().get(markingKey, PersistentDataType.STRING), 243 | damagerUUID.toString() 244 | )) { 245 | entity.getPersistentDataContainer().remove(markingKey); 246 | } 247 | }, effectDuration); 248 | } 249 | 250 | 251 | } 252 | --------------------------------------------------------------------------------