Compare commits

..
62 Commits
Author SHA1 Message Date
Martin 97fd32a7af Release v1.3.3
Signed-off-by: Martin <vluckyyy.biznes@gmail.com>
2024-10-24 21:30:52 +02:00
Michał WojtasandRollczi 20a4e4a2f5 GH-184 Refactor Knockback Mechanic and Add Support for Deny Entry flag (#184)
* Initial commit

* fix spaces

* fix spaces

* Add Kamicjusz's changes. Normalize knockback vector, fix message.

* Set cooldown for messages

* Resolve half of suggestions

* Remove delay and add PVP regions blockade.

* Improve performance of WorldGuardRegionProvider.

* Use lambda

* Resolve @vLuckyyy review

---------

Co-authored-by: Rollczi <ndejlich5@gmail.com>
2024-09-22 21:33:22 +02:00
Martin Sulikowski fe2569918a Release v1.3.2 2024-09-18 19:21:23 +02:00
Michał Wojtas e17effa4cd Fix op in combat (#183) 2024-09-18 15:30:40 +02:00
Michał Wojtas c63df3ab38 GH-181 Add block placing blockade modes and message formatting. (#181)
* Add block placing blockade modes and message formatting.
2024-09-11 19:25:12 +02:00
Martin 24fade593b Release v1.3.1 2024-08-11 17:38:53 +02:00
renovate[bot] 4b12968a7b GH-177 Update all dependencies (#177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-11 17:25:42 +02:00
Martin 9ad0063aa5 Java 21 support. 2024-08-11 17:15:57 +02:00
Michał Wojtas 7f6c6341f1 GH-175 Fix readme (#175) 2024-07-17 23:10:39 +02:00
Martin 0620f7fb92 Release v1.3.0 2024-07-16 22:11:51 +02:00
Michał WojtasandRollczi d18b2f013a GH-174 Add progress animation to boss bar, fix issue with boss bar not disappearing (#174)
* Add progress animation to boss bar, fix issue with boss bar not disappearing

* Fix spaces

* Temporary solution for empty duration formatting.

* Remove unused cases solved by @Rollczi

---------

Co-authored-by: Rollczi <ndejlich5@gmail.com>
2024-07-16 22:00:08 +02:00
Michał Wojtas 7b89dfdcfc Fix formatting of placeholder to match minecraft health system (#173) 2024-07-13 16:06:55 +02:00
Michał Wojtas 595370f6a4 GH-170 Add PlaceHolderAPI, enchance FightTag class (#170)
* Add PlaceHolderAPI, create basic placeholders. Add tagger as a person who starts tag countdown for player.

* Update README.md with placeholders. Update return value of placeholders

* Resolve Piotrulla's comments.

* Add PlaceHolder to BridgeService. Resolve Rollczi's comments

* Resolve @vLuckyyy review

* Fixes

* Fix infinite loop :>
2024-07-03 16:19:23 +02:00
Martin Sulikowski e732bdf842 GH-165 Use eternalcode commons. (#169)
* Use eternalcode commons.

* Make DurationUtil final.
2024-05-19 23:21:50 +02:00
Martin 6b6329c4a6 Make worldguard compileOnly. 2024-05-19 20:07:17 +02:00
Martin Sulikowski 7a6707b20e GH-164 Normalize gradle architecture. (#166)
* Normalize gradle architecture.

* Add missing version placeholder.
2024-05-19 20:02:29 +02:00
Martin 7a10e268bb Change api version in readme. 2024-04-30 17:57:28 +02:00
Martin adce4729f0 Release v1.2.2 2024-04-30 17:57:04 +02:00
renovate[bot] 8560f17bac GH-160 Update all dependencies (#160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 17:27:28 +02:00
renovate[bot] d2d7a4f02c GH-159 Update all dependencies (#159)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-23 23:00:05 +02:00
Michał Wojtas cf33b23720 Include admins in combat on default (#158) 2024-04-22 14:16:59 +02:00
renovate[bot] e261e06635 GH-157 Update all dependencies (#157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-19 21:40:27 +02:00
renovate[bot] 2856ababff GH-155 Update gradle/gradle-build-action digest to 8baac4c (#155)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-16 22:32:52 +02:00
renovate[bot] f061e1738e GH-154 Update all dependencies (#154)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-11 22:40:53 +02:00
renovate[bot] 4118983479 GH-153 Update gradle/gradle-build-action digest to fe59895 (#153)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-07 22:02:05 +02:00
renovate[bot] 988859ddbe GH-152 Update dependency checkstyle to v10.15.0 (#152)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-02 20:30:37 +02:00
renovate[bot] 305ab9e299 GH-151 Update dependency commons-io:commons-io to v2.16.0 (#151)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-02 20:29:15 +02:00
renovate[bot] 5aaf8a4f08 GH-150 Update all dependencies (#150)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-25 23:09:11 +01:00
renovate[bot] 153657c554 GH-149 Update all dependencies (#149)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-20 16:49:16 +01:00
renovate[bot] 5123176cb3 GH-148 Update dependency checkstyle to v10.14.1 (#148)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-12 23:55:31 +01:00
renovate[bot] 1665f39151 GH-142 Update all dependencies (#142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-08 23:45:16 +01:00
Martin Sulikowski 94113ea326 Fix checkstyle. 2024-03-08 23:37:05 +01:00
renovate[bot] 7f581dbaac GH-146 Update actions/cache action to v4 (#146)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-06 21:51:30 +01:00
imgbot[bot]andImgBotApp 2dd25cc27e GH-147 [ImgBot] Optimize images (#147)
/assets/readme-banner.png -- 102.61kb -> 85.78kb (16.4%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2024-03-06 21:49:34 +01:00
Martin Sulikowski 3ca562f515 Update readme banner. 2024-02-18 16:33:55 +01:00
Martin Sulikowski 383c405198 Bump Minecraft version in readme to 1.20.4. 2024-01-16 15:02:11 +01:00
Norbert Dejlich c522f7c078 Bump dependency version in README.md to 1.2.1 2024-01-16 00:10:39 +01:00
Martin Sulikowski fb6efa0646 Release v1.2.1-SNAPSHOT 2024-01-15 22:11:14 +01:00
Martin Sulikowski ad7899f10e Release v1.2.1 2024-01-15 21:51:21 +01:00
Martin Sulikowski ab5e6a7cf3 GH-141 Remove config backup system (Resolve #141) 2024-01-15 21:47:59 +01:00
Martin Sulikowski 42ae17bf8d GH-145 Add method to get region center and use it for knockback in region. (#145) 2024-01-15 21:45:27 +01:00
Martin Sulikowski 20d342d0ca Update eternalcombat-api version in readme. 2024-01-10 14:12:24 +01:00
Martin Sulikowski c4adeb1aed Fix checkstyle violations. 2024-01-09 17:59:31 +01:00
Martin Sulikowski d2d0c5b608 Release v1.2.0 2024-01-09 17:59:23 +01:00
Krystian Michalski eb1febab85 GH-144 Fix fight tag for admin. (#144) 2024-01-09 17:28:14 +01:00
Krystian Michalski 8153ba55ab GH-143 Fix region teleport by example: Chorus, EnderPearl. (#143) 2023-12-27 13:11:49 +01:00
renovate[bot]andMartin Sulikowski 89784f3412 GH-135 Update all dependencies (#135)
* Update all dependencies

* Revert checkstyle.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Martin Sulikowski <vLuckyyy.biznes@gmail.com>
2023-12-24 12:03:27 +01:00
renovate[bot] 49e82385d4 GH-136 Update all dependencies to v4 (#136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-24 12:02:54 +01:00
IgorandOsnixer 190034f5aa GH-139 Prevent flying during combat feature (#139)
* Prevent flying during combat feature

* add check is player flying and replace google cache -> caffeine cache

---------

Co-authored-by: Osnixer <Osnixeroo@gmail.com>
2023-12-24 01:28:28 +01:00
Martin SulikowskiandDMK 1db744c4ec GH-132 Add option to exclude admins from combat. (#132)
* Add option to exclude admins from combat.

* revert hashmap

* Update eternalcombat-api/src/main/java/com/eternalcode/combat/fight/FightManager.java

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>

* Fix

* merge master

---------

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>
2023-11-20 23:54:05 +01:00
Michał WojtasandDMK 94d7baf791 GH-134 Remove damage fields from FightTag (#134)
* Add custom Causes, fix code for remove items from drop, remove fields from fightTag, fix issues

* Fix air errors

* Code styles fixes

* Update eternalcombat-api/src/main/java/com/eternalcode/combat/fight/controller/FightUnTagController.java

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>

* Update eternalcombat-api/src/main/java/com/eternalcode/combat/fight/logout/LogoutController.java

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>

* Update eternalcombat-api/src/main/java/com/eternalcode/combat/fight/logout/LogoutController.java

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>

* Update eternalcombat-api/src/main/java/com/eternalcode/combat/fight/logout/LogoutService.java

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>

* Update eternalcombat-api/src/main/java/com/eternalcode/combat/fight/logout/LogoutService.java

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>

* Fix compile error

* Change logoutManager to logoutService var name. Remove unused dependency

* Follow Rollczi's suggestion

---------

Co-authored-by: DMK <81445555+imDMK@users.noreply.github.com>
2023-11-19 22:01:51 +01:00
renovate[bot] c25af3f6d3 GH-133 Update all dependencies (#133)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-19 20:48:55 +01:00
renovate[bot] 3b38c82b47 GH-128 Update all dependencies (#128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-13 22:53:57 +01:00
Martin Sulikowski 526f831fcb Add SpigotMC group to renovate.json configuration 2023-11-13 22:49:55 +01:00
Martin Sulikowski e4151cbbe0 Enable major/minor separation and pruning stale branches in Renovate 2023-11-13 22:39:55 +01:00
Martin Sulikowski 47d9fed823 GH-126 Central Dependency Management using toml file. (#126)
* Centralized Dependency and Project Configuration Management.

* Fix

* Move to buildSrc
2023-10-21 15:10:15 +02:00
Martin Sulikowski 0890b2abbd Fix CI to follow new path in module. 2023-10-19 19:38:56 +02:00
Martin Sulikowski be99a8646d GH-125 Add info about developer api. (#125) 2023-10-19 19:32:44 +02:00
renovate[bot] b58266c868 GH-103 Update all dependencies (#103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-19 19:26:23 +02:00
DMK eaa02a4166 GH-115 Fix death cause (#119) 2023-10-19 19:26:01 +02:00
Martin Sulikowski 08e99ded13 Disable custom effects default. 2023-10-15 13:03:48 +02:00
failutee e867e7e697 GH-123 Add null-check for DropResult (#123) 2023-10-15 00:58:22 +02:00
54 changed files with 832 additions and 675 deletions

No files matched your search

+6 -6
View File
@@ -16,14 +16,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Gradle
uses: actions/cache@v3.3.1
uses: actions/cache@v4.0.2
with:
path: ~/.gradle/caches
key: >-
@@ -33,11 +33,11 @@ jobs:
- name: Make gradlew executable
run: chmod +x gradlew
- name: Build with Gradle
uses: gradle/gradle-build-action@e3426b1f20c7262e0fed7b855aae1325633af3b4
uses: gradle/gradle-build-action@093dfe9d598ec5a42246855d09b49dc76803c005
with:
arguments: shadowJar
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3.1.2
uses: actions/upload-artifact@v4.3.6
with:
name: 'Successfully build EternalCombat'
path: build/libs/*.jar
path: eternalcombat-plugin/build/libs/*.jar
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
+51 -1
View File
@@ -15,11 +15,24 @@
### Information
EternalCombat has been tested on Minecraft versions 1.17.1 to 1.20.1, but it should work on most other versions. If you
EternalCombat has been tested on Minecraft versions 1.17.1 to 1.21.1, but it should work on most other versions. If you
encounter any compatibility issues, please report them in the
[Issues tab](https://github.com/EternalCodeTeam/EternalCombat/issues). The plugin requires Java 17 or later, so please
make sure it is installed on your server.
## PlaceholderAPI
EternalCombat supports PlaceholderAPI, which allows you to use placeholders in other plugins that support it.
To use placeholders, follow PlaceholderAPI's [instructions](https://wiki.placeholderapi.com/users/) and use the placeholders provided by EternalCombat.
Provided placeholders:
- `%eternalcombat_opponent%` - Returns the name of the player with whom the player is fighting.
- `%eternalcombat_opponent_health%` - Returns opponent health in format `00.00`
- `%eternalcombat_remaining_seconds%` - Returns the number of seconds remaining until the player is no longer in combat.
- `%eternalcombat_remaining_millis%` - Returns the number of milliseconds remaining until the player is no longer in combat.
If the player is not in combat, the placeholders will return an empty string.
If combat was not caused by other player, opponent placeholders will return empty string.
## Building
Build EternalCombat using:
@@ -35,6 +48,43 @@ Build EternalCombat using:
- `eternalcombat.receiveupdates` - Allows you to receive notifications about the new version of the plugin when
attaching
### Developer API
#### 1. Add repository:
with Gradle:
```kts
maven {
url = uri("https://repo.eternalcode.pl/releases")
}
```
with Maven:
```xml
<repository>
<id>eternalcode-reposilite-releases</id>
<name>EternalCode Repository</name>
<url>https://repo.eternalcode.pl/releases</url>
</repository>
```
#### 2. Add dependency:
with Gradle:
```kts
compileOnly("com.eternalcode:eternalcombat-api:1.3.3")
```
with Maven:
```xml
<dependency>
<groupId>com.eternalcode</groupId>
<artifactId>eternalcombat-api</artifactId>
<version>1.3.3</version>
<scope>provided</scope>
</dependency>
```
### Contributing
We welcome all contributions to the EternalCombat project! Please check out [contributing](.github/CONTRIBUTING.md) for
Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 86 KiB

+29
View File
@@ -0,0 +1,29 @@
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25")
implementation("io.github.goooler.shadow:shadow-gradle-plugin:8.1.8")
implementation("net.minecrell:plugin-yml:0.6.0")
implementation("xyz.jpenilla:run-task:2.3.0")
}
sourceSets {
main {
java.setSrcDirs(emptyList<String>())
groovy.setSrcDirs(emptyList<String>())
resources.setSrcDirs(emptyList<String>())
}
test {
java.setSrcDirs(emptyList<String>())
kotlin.setSrcDirs(emptyList<String>())
groovy.setSrcDirs(emptyList<String>())
resources.setSrcDirs(emptyList<String>())
}
}
+36
View File
@@ -0,0 +1,36 @@
object Versions {
const val SPIGOT_API = "1.17.1-R0.1-SNAPSHOT"
const val JUNIT_JUPITER_API = "5.10.3"
const val JUNIT_JUPITER_PARAMS = "5.10.3"
const val JUNIT_JUPITER_ENGINE = "5.10.3"
const val ETERNALCODE_COMMONS = "1.1.3"
// TODO: Multification.
const val ADVENTURE_PLATFORM_BUKKIT = "4.3.4"
const val ADVENTURE_TEXT_MINIMESSAGE = "4.17.0"
const val LITE_COMMANDS = "2.8.9"
const val OKAERI_CONFIGS_YAML_BUKKIT = "5.0.3"
const val OKAERI_CONFIGS_SERDES_COMMONS = "5.0.3"
const val OKAERI_CONFIGS_SERDES_BUKKIT = "5.0.3"
const val PANDA_UTILITIES = "0.5.2-alpha"
const val GIT_CHECK = "1.0.0"
const val APACHE_COMMONS = "2.16.1"
const val CAFFEINE = "3.1.8"
const val B_STATS_BUKKIT = "3.0.2"
const val WORLD_GUARD_BUKKIT = "7.0.9"
const val PLACEHOLDER_API = "2.11.6"
}
@@ -0,0 +1,19 @@
plugins {
`java-library`
}
dependencies {
testImplementation("org.spigotmc:spigot-api:${Versions.SPIGOT_API}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT_JUPITER_API}")
testImplementation("org.junit.jupiter:junit-jupiter-params:${Versions.JUNIT_JUPITER_PARAMS}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.JUNIT_JUPITER_ENGINE}")
}
tasks.test {
useJUnitPlatform()
}
sourceSets.test {
java.setSrcDirs(listOf("test"))
resources.setSrcDirs(emptyList<String>())
}
@@ -0,0 +1,42 @@
plugins {
`java-library`
checkstyle
}
group = "com.eternalcode"
version = "1.3.3"
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
checkstyle {
toolVersion = "10.17.0"
configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
maxErrors = 0
maxWarnings = 0
}
// https://github.com/JabRef/jabref/pull/10812/files#diff-49a96e7eea8a94af862798a45174e6ac43eb4f8b4bd40759b5da63ba31ec3ef7R267
configurations.named("checkstyle") {
resolutionStrategy {
capabilitiesResolution {
withCapability("com.google.collections:google-collections") {
select("com.google.guava:guava:33.2.1-jre")
}
}
}
}
tasks.compileJava {
options.compilerArgs = listOf("-Xlint:deprecation", "-parameters")
options.encoding = "UTF-8"
options.release = 17
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
@@ -0,0 +1,34 @@
plugins {
`java-library`
`maven-publish`
}
group = "com.eternalcode"
version = "1.3.3"
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
create<MavenPublication>("maven") {
artifactId = "eternalcombat-api"
from(project.components["java"])
}
}
repositories {
mavenLocal()
maven {
name = "eternalcodeReleases"
url = uri("https://repo.eternalcode.pl/releases")
credentials {
username = System.getenv("ETERNAL_CODE_MAVEN_USERNAME")
password = System.getenv("ETERNAL_CODE_MAVEN_PASSWORD")
}
}
}
}
@@ -0,0 +1,15 @@
plugins {
`java-library`
}
repositories {
mavenCentral()
maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://repo.eternalcode.pl/releases") }
maven { url = uri("https://storehouse.okaeri.eu/repository/maven-public/") }
maven { url = uri("https://repo.panda-lang.org/releases") }
maven { url = uri("https://maven.enginehub.org/repo/") }
maven { url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/")}
}
+25 -86
View File
@@ -1,109 +1,48 @@
plugins {
`java-library`
checkstyle
id("maven-publish")
}
group = "com.eternalcode"
version = "1.1.1"
checkstyle {
toolVersion = "10.12.2"
configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
maxErrors = 0
maxWarnings = 0
}
repositories {
mavenCentral()
maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://repo.eternalcode.pl/releases") }
maven { url = uri("https://storehouse.okaeri.eu/repository/maven-public/") }
maven { url = uri("https://repo.panda-lang.org/releases") }
maven { url = uri("https://maven.enginehub.org/repo/") }
`eternalcombat-java`
`eternalcombat-repositories`
`eternalcombat-publish`
`eternalcombat-java-unit-test`
}
dependencies {
// Spigot api
compileOnlyApi("org.spigotmc:spigot-api:1.19.3-R0.1-SNAPSHOT")
compileOnlyApi("org.spigotmc:spigot-api:${Versions.SPIGOT_API}")
// kyori
api("net.kyori:adventure-platform-bukkit:4.3.0")
api("net.kyori:adventure-text-minimessage:4.14.0")
api("net.kyori:adventure-platform-bukkit:${Versions.ADVENTURE_PLATFORM_BUKKIT}")
api("net.kyori:adventure-text-minimessage:${Versions.ADVENTURE_TEXT_MINIMESSAGE}")
// litecommands
api("dev.rollczi.litecommands:bukkit-adventure:2.8.9")
api("dev.rollczi.litecommands:bukkit-adventure:${Versions.LITE_COMMANDS}")
// Okaeri configs
api("eu.okaeri:okaeri-configs-yaml-bukkit:5.0.0-beta.5")
api("eu.okaeri:okaeri-configs-serdes-commons:5.0.0-beta.5")
api("eu.okaeri:okaeri-configs-serdes-bukkit:5.0.0-beta.5")
api("eu.okaeri:okaeri-configs-yaml-bukkit:${Versions.OKAERI_CONFIGS_YAML_BUKKIT}")
api("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS_SERDES_COMMONS}")
api("eu.okaeri:okaeri-configs-serdes-bukkit:${Versions.OKAERI_CONFIGS_SERDES_BUKKIT}")
// Panda utilities
api("org.panda-lang:panda-utilities:0.5.2-alpha")
api("org.panda-lang:panda-utilities:${Versions.PANDA_UTILITIES}")
// GitCheck
api("com.eternalcode:gitcheck:1.0.0")
api("com.eternalcode:gitcheck:${Versions.GIT_CHECK}")
// commons
api("commons-io:commons-io:2.13.0")
api("commons-io:commons-io:${Versions.APACHE_COMMONS}")
// bstats
api("org.bstats:bstats-bukkit:3.0.2")
api("org.bstats:bstats-bukkit:${Versions.B_STATS_BUKKIT}")
// caffeine
api("com.github.ben-manes.caffeine:caffeine:${Versions.CAFFEINE}")
api("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}")
api("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}")
// worldguard
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.9")
compileOnly("com.sk89q.worldguard:worldguard-bukkit:${Versions.WORLD_GUARD_BUKKIT}")
// PlaceholderAPI
compileOnlyApi("me.clip:placeholderapi:${Versions.PLACEHOLDER_API}")
// tests
testImplementation("org.spigotmc:spigot-api:1.19.3-R0.1-SNAPSHOT")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.getByName<Test>("test") {
useJUnitPlatform()
}
// Publish
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
create<MavenPublication>("maven") {
artifactId = "eternalcombat-api"
from(project.components["java"])
}
}
}
publishing {
repositories {
mavenLocal()
maven {
name = "eternalcodeReleases"
url = uri("https://repo.eternalcode.pl/releases")
credentials {
username = System.getenv("ETERNAL_CODE_MAVEN_USERNAME")
password = System.getenv("ETERNAL_CODE_MAVEN_PASSWORD")
}
}
}
}
@@ -1,9 +1,13 @@
package com.eternalcode.combat.bridge;
import com.eternalcode.combat.bridge.placeholder.FightTagPlaceholder;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.region.DefaultRegionProvider;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.region.WorldGuardRegionProvider;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import java.util.logging.Logger;
@@ -13,33 +17,40 @@ public class BridgeService {
private final PluginConfig pluginConfig;
private final PluginManager pluginManager;
private final Logger logger;
private final Plugin plugin;
private RegionProvider regionProvider;
public BridgeService(PluginConfig pluginConfig, PluginManager pluginManager, Logger logger) {
public BridgeService(PluginConfig pluginConfig, PluginManager pluginManager, Logger logger, Plugin plugin) {
this.pluginConfig = pluginConfig;
this.pluginManager = pluginManager;
this.logger = logger;
this.plugin = plugin;
}
public void init() {
public void init(FightManager fightManager, Server server) {
this.initialize("WorldGuard",
() -> this.regionProvider = new WorldGuardRegionProvider(this.pluginConfig.settings.blockedRegions),
() -> this.regionProvider = new WorldGuardRegionProvider(this.pluginConfig.settings.blockedRegions, this.pluginConfig),
() -> {
this.regionProvider = new DefaultRegionProvider(this.pluginConfig.settings.blockedRegionRadius);
this.regionProvider = new DefaultRegionProvider(this.pluginConfig.settings.blockedRegionRadius);
this.logger.warning("WorldGuard is not installed, set to default region provider.");
});
this.logger.warning("WorldGuard is not installed, set to default region provider.");
});
this.initialize("PlaceholderAPI",
() -> new FightTagPlaceholder(fightManager, server, plugin).register(),
() -> this.logger.warning("PlaceholderAPI is not installed, placeholders will not be registered.")
);
}
private void initialize(String pluginName, BridgeInitializer initializer, Runnable runnable) {
private void initialize(String pluginName, BridgeInitializer initializer, Runnable failureHandler) {
if (this.pluginManager.isPluginEnabled(pluginName)) {
initializer.initialize();
this.logger.info("Successfully initialized " + pluginName + " bridge.");
}
else {
runnable.run();
failureHandler.run();
}
}
@@ -0,0 +1,98 @@
package com.eternalcode.combat.bridge.placeholder;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.FightTag;
import com.eternalcode.combat.util.DurationUtil;
import com.eternalcode.commons.time.DurationParser;
import java.util.Optional;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public class FightTagPlaceholder extends PlaceholderExpansion {
private final FightManager fightManager;
private final Server server;
private final Plugin plugin;
private static final String IDENTIFIER = "eternalcombat";
public FightTagPlaceholder(FightManager fightManager, Server server, Plugin plugin) {
this.fightManager = fightManager;
this.server = server;
this.plugin = plugin;
}
@Override
public boolean canRegister() {
return true;
}
@Override
public @NotNull String getIdentifier() {
return IDENTIFIER;
}
@Override
public @NotNull String getAuthor() {
return plugin.getDescription().getAuthors().get(0);
}
@Override
public @NotNull String getVersion() {
return plugin.getDescription().getVersion();
}
@Override
public String onRequest(OfflinePlayer player, String identifier) {
if (identifier.equals("remaining_seconds")) {
return this.getFightTag(player)
.map(fightTag -> DurationUtil.format(fightTag.getRemainingDuration()))
.orElse("");
}
if (identifier.equals("remaining_millis")) {
return this.getFightTag(player)
.map(fightTag -> DurationParser.TIME_UNITS.format(fightTag.getRemainingDuration()))
.orElse("");
}
if (identifier.equals("opponent")) {
return getTagger(player)
.map(tagger -> tagger.getName())
.orElse("");
}
if (identifier.equals("opponent_health")) {
return getTagger(player)
.map(tagger -> String.format("%.2f", tagger.getHealth()))
.orElse("");
}
return null;
}
private @NotNull Optional<Player> getTagger(OfflinePlayer player) {
return this.getFightTag(player)
.map(fightTag -> fightTag.getTagger())
.map(taggerId -> this.server.getPlayer(taggerId));
}
private Optional<FightTag> getFightTag(OfflinePlayer player) {
Player onlinePlayer = player.getPlayer();
if (onlinePlayer != null) {
if (!this.fightManager.isInCombat(onlinePlayer.getUniqueId())) {
return Optional.empty();
}
return Optional.of(this.fightManager.getTag(onlinePlayer.getUniqueId()));
}
return Optional.empty();
}
}
@@ -1,119 +0,0 @@
package com.eternalcode.combat.config;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
public class ConfigBackupService {
private static final String BACKUP_FOLDER_NAME = "backup";
private static final String BACKUP_FILE_EXTENSION = ".bak";
private final File dataFolder;
public ConfigBackupService(File dataFolder) {
this.dataFolder = dataFolder;
}
public void createBackup() {
File backupFolder = new File(this.dataFolder, BACKUP_FOLDER_NAME);
if (!backupFolder.exists()) {
backupFolder.mkdirs();
}
LocalDateTime currentDateTime = LocalDateTime.now();
String backupFolderName = BACKUP_FOLDER_NAME + "_" + currentDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm"));
File currentBackupFolder = new File(backupFolder, backupFolderName);
if (!currentBackupFolder.exists()) {
currentBackupFolder.mkdirs();
}
this.copyFolderContents(this.dataFolder, currentBackupFolder);
this.deleteIfOlderDirectory(backupFolder);
}
private void copyFolderContents(File sourceFolder, File targetFolder) {
if (!sourceFolder.exists() || !sourceFolder.isDirectory()) {
return;
}
if (!targetFolder.exists()) {
boolean targetFolderCreated = targetFolder.mkdirs();
if (!targetFolderCreated) {
return;
}
}
File[] filesToBackup = sourceFolder.listFiles();
if (filesToBackup == null) {
return;
}
for (File file : filesToBackup) {
if (file.isDirectory() && !file.getName().equals(BACKUP_FOLDER_NAME)) {
File subFolder = new File(targetFolder, file.getName());
this.copyFolderContents(file, subFolder);
continue;
}
if (file.isFile() && !file.getName().endsWith(BACKUP_FILE_EXTENSION)) {
File backupFile = new File(targetFolder, file.getName() + BACKUP_FILE_EXTENSION);
this.copyToBackupFile(file, backupFile);
}
}
}
private void copyToBackupFile(File targetFolder, File path) {
try {
Files.copy(targetFolder.toPath(), path.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException exception) {
exception.printStackTrace();
}
}
// delete backup folders older than X days
private void deleteIfOlderDirectory(File backupFolder) {
File[] backupFolders = backupFolder.listFiles(File::isDirectory);
if (backupFolders == null) {
return;
}
for (File folder : backupFolders) {
String folderName = folder.getName();
if (!folderName.startsWith(BACKUP_FOLDER_NAME)) {
continue;
}
try {
LocalDate folderDate = LocalDate.parse(folderName.substring(BACKUP_FOLDER_NAME.length() + 1, folderName.lastIndexOf("_")));
LocalTime folderTime = LocalTime.parse(folderName.substring(folderName.lastIndexOf("_") + 1).replace("-", ":"));
LocalDateTime folderDateTime = LocalDateTime.of(folderDate, folderTime);
LocalDateTime currentDateTime = LocalDateTime.now();
long hours = ChronoUnit.HOURS.between(folderDateTime, currentDateTime);
if (hours > 72) {
FileUtils.deleteDirectory(folder);
}
}
catch (DateTimeParseException | IOException exception) {
exception.printStackTrace();
}
}
}
}
@@ -13,17 +13,9 @@ import java.util.Set;
public class ConfigService {
private final ConfigBackupService backupService;
private final Set<OkaeriConfig> configs = new HashSet<>();
public ConfigService(ConfigBackupService backupService) {
this.backupService = backupService;
}
public <T extends OkaeriConfig> T create(Class<T> config, File file) {
this.backupService.createBackup();
T configFile = ConfigManager.create(config);
configFile.withConfigurer(new YamlBukkitConfigurer(), new SerdesCommons(), new SerdesBukkit());
@@ -46,8 +46,15 @@ public class PluginConfig extends OkaeriConfig {
@Comment("# List of regions to block")
public List<String> blockedRegions = Collections.singletonList("your_region");
@Comment("# Set the knock multiplier for the blocked region")
public double blockedRegionKnockMultiplier = 1.2;
@Comment({
"# Set the knock multiplier for the blocked region",
"# Values can be decimal. Do NOT use negative values.",
"# Setting it around 1 knocks the player around 2-4 blocks away."
})
public double blockedRegionKnockMultiplier = 1;
@Comment({"# Should the player be prevented from entering regions with WorldGuard flag PVP set to DENY "})
public boolean shouldPreventPvpRegions = true;
@Comment("# Set the radius of the blocked region if you aren't using WorldGuard basen on default spawn region!")
public int blockedRegionRadius = 10;
@@ -55,6 +62,12 @@ public class PluginConfig extends OkaeriConfig {
@Comment("# Release attacker after victim dies?")
public boolean shouldReleaseAttacker = true;
@Comment({"# If you want to exclude admins from combat, ",
"# Setting this to true - admins cannot be tagged and will not tag other players on hit",
"# Setting this to false - admins can be tagged and can tag other players on hit"
})
public boolean excludeAdminFromCombat = false;
@Comment("# Command blocking mode, available modes: WHITELIST, BLACKLIST")
public WhitelistBlacklistMode commandBlockingMode = WhitelistBlacklistMode.BLACKLIST;
@@ -72,6 +85,9 @@ public class PluginConfig extends OkaeriConfig {
@Comment("# Block the use of elytra?")
public boolean shouldPreventElytraUsage = true;
@Comment("# Block flying? (flying players will fall to the ground)")
public boolean shouldPreventFlying = true;
@Comment("# Disable the use of elytra on damage?")
public boolean shouldElytraDisableOnDamage = true;
@@ -81,8 +97,21 @@ public class PluginConfig extends OkaeriConfig {
@Comment("# Whether to block the placement of blocks?")
public boolean shouldPreventBlockPlacing = true;
@Comment("# The minimum Y level at which block placing is blocked")
public int minBlockPlacingLevel = 40;
@Comment({ "# Block the placement of blocks above or below a certain Y coordinate",
"# Select the mode for block placing, available modes: ABOVE, BELOW"
})
public BlockPlacingMode blockPlacingMode = BlockPlacingMode.ABOVE;
@Comment("# Block placing mode custom name used if messages")
public String blockPlacingModeName = "above";
@Comment("# Set the Y coordinate for block placing relative to mode selected above")
public int blockPlacingYCoordinate = 40;
public enum BlockPlacingMode {
ABOVE,
BELOW
}
@Comment({
"# Disable placing specific blocks?",
@@ -168,11 +197,13 @@ public class PluginConfig extends OkaeriConfig {
@Comment("# Message sent when player tries to open inventory, but the inventory open is blocked")
public String inventoryBlockedDuringCombat = "&cYou cannot open this inventory during combat!";
@Comment("# Message sent when player tries to place a block, but the block place is blocked")
public String blockPlacingBlockedDuringCombat = "&cYou cannot place above 40Y coordinate during combat!";
@Comment({"# Message sent when player tries to place a block, but the block place is blocked",
"# Placeholder {Y} is replaced with the Y coordinate set in the config",
"# Placeholder {MODE} is replaced with the mode set in the config"})
public String blockPlacingBlockedDuringCombat = "&cYou cannot place {MODE} {Y} coordinate during combat!";
@Comment("# Message sent when player tries to enter a region")
public String cantEnterOnRegion = "&cYou can't enter on this region during combat!";
public String cantEnterOnRegion = "&cYou can't enter this region during combat!";
public static class AdminMessages extends OkaeriConfig {
@Comment("# Message sent when the configuration is reloaded")
@@ -1,7 +1,5 @@
package com.eternalcode.combat.drop;
import com.eternalcode.combat.fight.FightDeathCause;
import com.eternalcode.combat.fight.FightTag;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -15,18 +13,13 @@ public class Drop {
private final Player player;
private final Player killer;
private final FightTag fightTag;
private final FightDeathCause deathCause;
private final List<ItemStack> droppedItems;
private final int droppedExp;
private Drop(Player player, Player killer, FightTag fightTag, FightDeathCause deathCause, List<ItemStack> droppedItems, int droppedExp) {
private Drop(Player player, Player killer, List<ItemStack> droppedItems, int droppedExp) {
this.player = player;
this.killer = killer;
this.fightTag = fightTag;
this.deathCause = deathCause;
this.droppedItems = droppedItems;
this.droppedExp = droppedExp;
}
@@ -43,14 +36,6 @@ public class Drop {
return this.killer;
}
public FightTag getFightTag() {
return this.fightTag;
}
public FightDeathCause getDeathCause() {
return this.deathCause;
}
public boolean hasKiller() {
return this.killer != null;
}
@@ -67,8 +52,6 @@ public class Drop {
private Player player;
private Player killer;
private FightTag fightTag;
private FightDeathCause deathCause;
private List<ItemStack> droppedItems;
private int droppedExp;
@@ -82,16 +65,6 @@ public class Drop {
return this;
}
public Builder fightTag(@NotNull FightTag fightTag) {
this.fightTag = fightTag;
return this;
}
public Builder deathCause(@NotNull FightDeathCause deathCause) {
this.deathCause = deathCause;
return this;
}
public Builder droppedItems(@NotNull List<ItemStack> droppedItems) {
this.droppedItems = new ArrayList<>(droppedItems);
return this;
@@ -106,8 +79,6 @@ public class Drop {
return new Drop(
this.player,
this.killer,
this.fightTag,
this.deathCause,
this.droppedItems,
this.droppedExp
);
@@ -1,9 +1,9 @@
package com.eternalcode.combat.drop;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.FightTag;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
@@ -27,7 +27,7 @@ public class DropController implements Listener {
this.fightManager = fightManager;
}
@EventHandler
@EventHandler(priority = EventPriority.LOW)
void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
@@ -36,22 +36,21 @@ public class DropController implements Listener {
if (dropType == DropType.UNCHANGED || !this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
FightTag fightTag = this.fightManager.getTag(player.getUniqueId());
List<ItemStack> drops = event.getDrops();
Drop drop = Drop.builder()
.player(player)
.killer(player.getKiller())
.fightTag(fightTag)
.deathCause(fightTag.getDeathCause())
.droppedItems(drops)
.droppedExp(player.getTotalExperience())
.build();
DropResult result = this.dropManager.modify(dropType, drop);
if (result == null) {
return;
}
drops.clear();
drops.addAll(result.droppedItems());
@@ -5,8 +5,8 @@ import com.eternalcode.combat.drop.DropModifier;
import com.eternalcode.combat.drop.DropResult;
import com.eternalcode.combat.drop.DropSettings;
import com.eternalcode.combat.drop.DropType;
import com.eternalcode.combat.fight.FightDeathCause;
import com.eternalcode.combat.fight.FightTag;
import com.eternalcode.combat.fight.logout.Logout;
import com.eternalcode.combat.fight.logout.LogoutService;
import com.eternalcode.combat.util.InventoryUtil;
import com.eternalcode.combat.util.MathUtil;
import com.eternalcode.combat.util.RemoveItemResult;
@@ -15,13 +15,16 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Optional;
public class PlayersHealthDropModifier implements DropModifier {
private final DropSettings settings;
private final LogoutService logoutService;
public PlayersHealthDropModifier(DropSettings settings) {
public PlayersHealthDropModifier(DropSettings settings, LogoutService logoutService) {
this.settings = settings;
this.logoutService = logoutService;
}
@Override
@@ -31,17 +34,20 @@ public class PlayersHealthDropModifier implements DropModifier {
@Override
public DropResult modifyDrop(Drop drop) {
if (drop.getDeathCause() != FightDeathCause.ESCAPE) {
Optional<Logout> logoutOptional = this.logoutService.nextLogoutFor(drop.getPlayer().getUniqueId());
if (logoutOptional.isEmpty()) {
return null;
}
Logout logout = logoutOptional.get();
Player player = drop.getPlayer();
FightTag fightTag = drop.getFightTag();
List<ItemStack> droppedItems = drop.getDroppedItems();
double maxHealth = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue();
double health = fightTag.getHealthBeforeDeath();
double health = logout.health();
int percentHealth = MathUtil.getRoundedCountPercentage(health, maxHealth);
int reversedPercent = MathUtil.clamp(100 - percentHealth, this.settings.playersHealthPercentClamp, 100);
@@ -1,10 +0,0 @@
package com.eternalcode.combat.fight;
public enum FightDeathCause {
UNKNOWN,
ESCAPE,
KILLED_BY_PLAYER,
KILLED_BY_ENTITY
}
@@ -13,6 +13,8 @@ import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class FightManager {
@@ -44,6 +46,11 @@ public class FightManager {
}
public FightTagEvent tag(UUID target, Duration delay, CauseOfTag causeOfTag) {
return this.tag(target, delay, causeOfTag, null);
}
@ApiStatus.Experimental
public FightTagEvent tag(UUID target, Duration delay, CauseOfTag causeOfTag, @Nullable UUID tagger) {
FightTagEvent event = this.eventCaller.publishEvent(new FightTagEvent(target, causeOfTag));
if (event.isCancelled()) {
@@ -52,7 +59,7 @@ public class FightManager {
Instant now = Instant.now();
Instant endOfCombatLog = now.plus(delay);
FightTag fightTag = new FightTag(target, endOfCombatLog);
FightTag fightTag = new FightTag(target, endOfCombatLog, tagger);
this.fights.put(target, fightTag);
return event;
@@ -3,21 +3,19 @@ package com.eternalcode.combat.fight;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class FightTag {
private final UUID taggedPlayer;
private final Instant endOfCombatLog;
private final @Nullable UUID tagger;
private double healthBeforeDeath;
private FightDeathCause deathCause;
FightTag(UUID personToAddCombat, Instant endOfCombatLog) {
FightTag(UUID personToAddCombat, Instant endOfCombatLog, @Nullable UUID tagger) {
this.taggedPlayer = personToAddCombat;
this.endOfCombatLog = endOfCombatLog;
this.healthBeforeDeath = 0;
this.deathCause = FightDeathCause.UNKNOWN;
this.tagger = tagger;
}
public UUID getTaggedPlayer() {
@@ -42,19 +40,10 @@ public class FightTag {
return between;
}
public double getHealthBeforeDeath() {
return this.healthBeforeDeath;
@Nullable
@ApiStatus.Experimental
public UUID getTagger() {
return this.tagger;
}
public void setHealthBeforeDeath(double health) {
this.healthBeforeDeath = health;
}
public FightDeathCause getDeathCause() {
return this.deathCause;
}
public void setDeathCause(FightDeathCause deathCause) {
this.deathCause = deathCause;
}
}
@@ -12,7 +12,6 @@ import org.bukkit.entity.Player;
import panda.utilities.text.Formatter;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
@@ -74,25 +73,19 @@ public class FightBossBarService {
}
BossBar bossBar = fightBossBar.bossBar();
float fightBossBarProgress = fightBossBar.progress();
if (fightBossBarProgress < 0.0F) {
Instant now = Instant.now();
long combatDurationMillis = this.pluginConfig.settings.combatDuration.toMillis();
long remainingDurationMillis = fightTag.getRemainingDuration().toMillis();
Instant endOfCombatLog = fightTag.getEndOfCombatLog();
float progress = (float) remainingDurationMillis / combatDurationMillis;
Duration between = Duration.between(now, endOfCombatLog);
Duration combatDuration = fightBossBar.combatDuration();
float difference = (float) between.toMillis() / combatDuration.toMillis();
float progress = Math.max(0.0F, Math.min(1.0F, difference));
bossBar.progress(progress);
if (progress > 1.0F) {
progress = 1.0F;
}
Component name = this.miniMessage.deserialize(message);
bossBar.name(name);
bossBar.progress(progress);
}
private void show(Player player, FightBossBar fightBossBar) {
@@ -14,9 +14,11 @@ import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityToggleGlideEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
import java.util.List;
import java.util.UUID;
import panda.utilities.text.Formatter;
public class FightActionBlockerController implements Listener {
@@ -48,22 +50,33 @@ public class FightActionBlockerController implements Listener {
List<Material> specificBlocksToPreventPlacing = this.config.settings.specificBlocksToPreventPlacing;
boolean isBlockPlaceLevel = level > this.config.settings.minBlockPlacingLevel;
if (isBlockPlaceLevel && specificBlocksToPreventPlacing.isEmpty()) {
boolean isPlacementBlocked = isPlacementBlocked(level);
Formatter formatter = new Formatter()
.register("{Y}", this.config.settings.blockPlacingYCoordinate)
.register("{MODE}", this.config.settings.blockPlacingModeName);
if (isPlacementBlocked && specificBlocksToPreventPlacing.isEmpty()) {
event.setCancelled(true);
this.announcer.sendMessage(player, this.config.messages.blockPlacingBlockedDuringCombat);
this.announcer.sendMessage(player, formatter.format(this.config.messages.blockPlacingBlockedDuringCombat));
}
Material blockMaterial = block.getType();
boolean isBlockInDisabledList = specificBlocksToPreventPlacing.contains(blockMaterial);
if (isBlockPlaceLevel && isBlockInDisabledList) {
if (isPlacementBlocked && isBlockInDisabledList) {
event.setCancelled(true);
this.announcer.sendMessage(player, this.config.messages.blockPlacingBlockedDuringCombat);
this.announcer.sendMessage(player, formatter.format(this.config.messages.blockPlacingBlockedDuringCombat));
}
}
private boolean isPlacementBlocked(int level) {
return this.config.settings.blockPlacingMode == PluginConfig.Settings.BlockPlacingMode.ABOVE
? level > this.config.settings.blockPlacingYCoordinate
: level < this.config.settings.blockPlacingYCoordinate;
}
@EventHandler
public void onToggleGlide(EntityToggleGlideEvent event) {
void onToggleGlide(EntityToggleGlideEvent event) {
if (!this.config.settings.shouldPreventElytraUsage) {
return;
}
@@ -83,7 +96,27 @@ public class FightActionBlockerController implements Listener {
}
@EventHandler
public void onDamage(EntityDamageEvent event) {
void onFly(PlayerToggleFlightEvent event) {
if (!this.config.settings.shouldPreventFlying) {
return;
}
Player player = event.getPlayer();
UUID uniqueId = player.getUniqueId();
if (!this.fightManager.isInCombat(uniqueId)) {
return;
}
if (event.isFlying()) {
player.setAllowFlight(false);
event.setCancelled(true);
}
}
@EventHandler
void onDamage(EntityDamageEvent event) {
if (!this.config.settings.shouldElytraDisableOnDamage) {
return;
}
@@ -117,7 +150,7 @@ public class FightActionBlockerController implements Listener {
}
@EventHandler
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
UUID playerUniqueId = player.getUniqueId();
@@ -1,59 +0,0 @@
package com.eternalcode.combat.fight.controller;
import com.eternalcode.combat.fight.FightDeathCause;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.FightTag;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class FightDeathCauseController implements Listener {
private final FightManager fightManager;
public FightDeathCauseController(FightManager fightManager) {
this.fightManager = fightManager;
}
@EventHandler
public void onEntityDamage(EntityDamageByEntityEvent event) {
Entity victimEntity = event.getEntity();
Entity damagerEntity = event.getDamager();
if (!(victimEntity instanceof Player victim)) {
return;
}
if (!this.fightManager.isInCombat(victim.getUniqueId())) {
return;
}
if (victim.getHealth() > event.getFinalDamage()) {
return;
}
FightTag fightTag = this.fightManager.getTag(damagerEntity.getUniqueId());
if (damagerEntity instanceof Player) {
fightTag.setDeathCause(FightDeathCause.KILLED_BY_PLAYER);
return;
}
fightTag.setDeathCause(FightDeathCause.KILLED_BY_ENTITY);
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
if (!this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
FightTag fightTag = this.fightManager.getTag(player.getUniqueId());
fightTag.setDeathCause(FightDeathCause.ESCAPE);
}
}
@@ -4,6 +4,7 @@ import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.bossbar.FightBossBarService;
import com.eternalcode.combat.fight.event.FightTagEvent;
import com.eternalcode.combat.fight.event.FightUntagEvent;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import org.bukkit.Server;
import org.bukkit.entity.Player;
@@ -43,19 +44,14 @@ public class FightMessageController implements Listener {
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onUnTag(FightTagEvent event) {
void onUnTag(FightUntagEvent event) {
Player player = this.server.getPlayer(event.getPlayer());
if (player == null) {
throw new IllegalStateException("Player cannot be null!");
}
if (!this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
this.announcer.sendMessage(player, this.config.messages.playerUntagged);
this.bossBarService.hide(event.getPlayer());
}
}
@@ -1,10 +1,9 @@
package com.eternalcode.combat.fight.controller;
import com.eternalcode.combat.WhitelistBlacklistMode;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import com.eternalcode.combat.fight.event.CauseOfTag;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
@@ -23,12 +22,10 @@ public class FightTagController implements Listener {
private final FightManager fightManager;
private final PluginConfig config;
private final NotificationAnnouncer announcer;
public FightTagController(FightManager fightManager, PluginConfig config, NotificationAnnouncer announcer) {
public FightTagController(FightManager fightManager, PluginConfig config) {
this.fightManager = fightManager;
this.config = config;
this.announcer = announcer;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@@ -38,7 +35,7 @@ public class FightTagController implements Listener {
}
List<EntityType> disabledProjectileEntities = this.config.settings.disabledProjectileEntities;
if (event.getDamager() instanceof Projectile projectile && disabledProjectileEntities.contains(projectile.getType())) {
return;
}
@@ -47,19 +44,38 @@ public class FightTagController implements Listener {
return;
}
Player personToAddCombatTime = this.getDamager(event);
Player attacker = this.getDamager(event);
if (personToAddCombatTime == null) {
if (attacker == null) {
return;
}
Duration combatTime = this.config.settings.combatDuration;
UUID attackedUniqueId = attackedPlayerByPerson.getUniqueId();
UUID personToAddCombatTimeUniqueId = personToAddCombatTime.getUniqueId();
UUID attackerUniqueId = attacker.getUniqueId();
this.fightManager.tag(attackedUniqueId, combatTime, CauseOfTag.PLAYER);
this.fightManager.tag(personToAddCombatTimeUniqueId, combatTime, CauseOfTag.PLAYER);
if (attacker.isOp() && this.config.settings.excludeAdminFromCombat) {
return;
}
if (attackedPlayerByPerson.isOp() && this.config.settings.excludeAdminFromCombat) {
return;
}
if (this.config.settings.shouldPreventFlying) {
if (attackedPlayerByPerson.isFlying()) {
attackedPlayerByPerson.setFlying(false);
attackedPlayerByPerson.setAllowFlight(false);
}
if (attacker.isFlying()) {
attacker.setFlying(false);
attacker.setAllowFlight(false);
}
}
this.fightManager.tag(attackedUniqueId, combatTime, CauseOfTag.PLAYER, attackerUniqueId);
this.fightManager.tag(attackerUniqueId, combatTime, CauseOfTag.PLAYER, attackedUniqueId);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@@ -3,6 +3,7 @@ package com.eternalcode.combat.fight.controller;
import com.eternalcode.combat.fight.event.CauseOfUnTag;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.logout.LogoutService;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -12,10 +13,12 @@ public class FightUnTagController implements Listener {
private final FightManager fightManager;
private final PluginConfig config;
private final LogoutService logoutService;
public FightUnTagController(FightManager fightManager, PluginConfig config) {
public FightUnTagController(FightManager fightManager, PluginConfig config, LogoutService logoutService) {
this.fightManager = fightManager;
this.config = config;
this.logoutService = logoutService;
}
@EventHandler
@@ -27,12 +30,29 @@ public class FightUnTagController implements Listener {
return;
}
CauseOfUnTag causeOfUnTag = (killer == null ? CauseOfUnTag.DEATH_BY_PLAYER : CauseOfUnTag.DEATH);
CauseOfUnTag cause = this.getDeathCause(player, killer);
this.fightManager.untag(player.getUniqueId(), causeOfUnTag);
this.fightManager.untag(player.getUniqueId(), cause);
if (killer != null && this.config.settings.shouldReleaseAttacker) {
this.fightManager.untag(killer.getUniqueId(), CauseOfUnTag.ATTACKER_RELEASE);
}
}
private CauseOfUnTag getDeathCause(Player player, Player killer) {
if (this.logoutService.hasLoggedOut(player.getUniqueId())) {
return CauseOfUnTag.LOGOUT;
}
if (killer == null) {
return CauseOfUnTag.DEATH;
}
if (this.fightManager.isInCombat(killer.getUniqueId())) {
return CauseOfUnTag.DEATH_BY_PLAYER;
}
return CauseOfUnTag.DEATH;
}
}
@@ -10,7 +10,7 @@ import java.util.Map;
public class FightEffectSettings extends OkaeriConfig {
@Comment({"# Do you want to add effects to players in combat?"})
public boolean customEffectsEnabled = true;
public boolean customEffectsEnabled = false;
@Comment({
"# If the option above is set to true, you can add effects to players in combat below",
@@ -3,5 +3,6 @@ package com.eternalcode.combat.fight.event;
public enum CauseOfTag {
PLAYER,
NON_PLAYER,
COMMAND
COMMAND,
CUSTOM
}
@@ -6,5 +6,6 @@ public enum CauseOfUnTag {
DEATH_BY_PLAYER,
LOGOUT,
COMMAND,
ATTACKER_RELEASE
ATTACKER_RELEASE,
CUSTOM
}
@@ -0,0 +1,6 @@
package com.eternalcode.combat.fight.logout;
import java.util.UUID;
public record Logout(UUID player, double health) {
}
@@ -1,45 +1,47 @@
package com.eternalcode.combat.fight.controller;
package com.eternalcode.combat.fight.logout;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.FightTag;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import panda.utilities.text.Formatter;
public class FightEscapeController implements Listener {
public class LogoutController implements Listener {
private final FightManager fightManager;
private final PluginConfig config;
private final LogoutService logoutService;
private final NotificationAnnouncer announcer;
private final PluginConfig config;
public FightEscapeController(FightManager fightManager, PluginConfig config, NotificationAnnouncer announcer) {
public LogoutController(FightManager fightManager, LogoutService logoutService, NotificationAnnouncer announcer, PluginConfig config) {
this.fightManager = fightManager;
this.config = config;
this.logoutService = logoutService;
this.announcer = announcer;
this.config = config;
}
@EventHandler
void onPlayerQuit(PlayerQuitEvent event) {
@EventHandler(priority = EventPriority.HIGH)
void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
if (!this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
this.logoutService.punishForLogout(player);
player.setHealth(0.0);
Formatter formatter = new Formatter()
.register("{PLAYER}", player.getName());
.register("{PLAYER}", player.getName());
String format = formatter.format(this.config.messages.playerLoggedOutDuringCombat);
this.announcer.broadcast(format);
FightTag fightTag = this.fightManager.getTag(player.getUniqueId());
fightTag.setHealthBeforeDeath(player.getHealth());
player.setHealth(0.0); // Untagged in PlayerDeathEvent
}
}
@@ -0,0 +1,37 @@
package com.eternalcode.combat.fight.logout;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class LogoutService {
private final Cache<UUID, Logout> logouts = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
public void punishForLogout(Player player) {
UUID uniqueId = player.getUniqueId();
this.logouts.put(uniqueId, new Logout(uniqueId, player.getHealth()));
}
public Optional<Logout> nextLogoutFor(UUID player) {
Logout logout = this.logouts.getIfPresent(player);
if (logout == null) {
return Optional.empty();
}
this.logouts.invalidate(player);
return Optional.of(logout);
}
public boolean hasLoggedOut(UUID player) {
return this.logouts.asMap().containsKey(player);
}
}
@@ -1,7 +1,7 @@
package com.eternalcode.combat.fight.pearl;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
import java.time.Instant;
@@ -14,7 +14,7 @@ public class FightPearlManager {
public FightPearlManager(FightPearlSettings pearlSettings) {
this.pearlSettings = pearlSettings;
this.pearlDelays = CacheBuilder.newBuilder()
this.pearlDelays = Caffeine.newBuilder()
.expireAfterWrite(pearlSettings.pearlThrowDelay)
.build();
}
@@ -45,7 +45,6 @@ public final class NotificationAnnouncer {
case BOSS_BAR -> {
BossBarNotification bossBarNotification = (BossBarNotification) notification;
BossBar bossBar = bossBarNotification.create(message);
audience.showBossBar(bossBar);
@@ -1,6 +1,8 @@
package com.eternalcode.combat.region;
import java.util.Optional;
import org.bukkit.Location;
import org.bukkit.util.BlockVector;
public class DefaultRegionProvider implements RegionProvider {
@@ -11,22 +13,24 @@ public class DefaultRegionProvider implements RegionProvider {
}
@Override
public boolean isInRegion(Location location) {
public Optional<Region> getRegion(Location location) {
Location spawnLocation = location.getWorld().getSpawnLocation();
double x = spawnLocation.getX();
double z = spawnLocation.getZ();
Point min = new Point(x - this.radius, z - this.radius);
Point max = new Point(x + this.radius, z + this.radius);
BlockVector min = new BlockVector(x - this.radius, 0, z - this.radius);
BlockVector max = new BlockVector(x + this.radius, 0, z + this.radius);
return this.contains(min, max, location.getX(), location.getZ());
if (this.contains(min, max, location.getX(), location.getZ())) {
return Optional.of(() -> new Location(location.getWorld(), x, location.getY(), z));
}
return Optional.empty();
}
public boolean contains(Point min, Point max, double x, double z) {
return x >= min.x() && x < max.x()
&& z >= min.z() && z < max.z();
private boolean contains(BlockVector min, BlockVector max, double x, double z) {
return x >= min.getX() && x < max.getX()
&& z >= min.getZ() && z < max.getZ();
}
public record Point(double x, double z) {}
}
@@ -0,0 +1,9 @@
package com.eternalcode.combat.region;
import org.bukkit.Location;
public interface Region {
Location getCenter();
}
@@ -3,11 +3,13 @@ package com.eternalcode.combat.region;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NotificationAnnouncer;
import java.util.Optional;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.util.Vector;
public class RegionController implements Listener {
@@ -43,18 +45,40 @@ public class RegionController implements Listener {
int zFrom = locationFrom.getBlockZ();
if (xTo != xFrom || yTo != yFrom || zTo != zFrom) {
if (!this.regionProvider.isInRegion(locationTo)) {
Optional<Region> regionOptional = regionProvider.getRegion(locationTo);
if (regionOptional.isEmpty()) {
return;
}
Location playerLocation = player.getLocation().subtract(player.getWorld().getSpawnLocation());
double distance = playerLocation.distance(player.getWorld().getSpawnLocation());
Vector knockback = new Vector(0, 3, 0).multiply(this.pluginConfig.settings.blockedRegionKnockMultiplier / distance);
Vector vector = playerLocation.toVector().add(knockback);
Region region = regionOptional.get();
Location centerOfRegion = region.getCenter();
Location subtract = player.getLocation().subtract(centerOfRegion);
player.setVelocity(vector);
Vector knockbackVector = new Vector(subtract.getX(), 0, subtract.getZ()).normalize();
Vector configuredVector = new Vector(
this.pluginConfig.settings.blockedRegionKnockMultiplier,
0.5,
this.pluginConfig.settings.blockedRegionKnockMultiplier);
player.setVelocity(knockbackVector.multiply(configuredVector));
this.announcer.sendMessage(player, this.pluginConfig.messages.cantEnterOnRegion);
}
}
@EventHandler
public void onPlayerTeleport(PlayerTeleportEvent event) {
Player player = event.getPlayer();
if (!this.fightManager.isInCombat(player.getUniqueId())) {
return;
}
Location targetLocation = event.getTo();
if (this.regionProvider.isInRegion(targetLocation)) {
event.setCancelled(true);
this.announcer.sendMessage(player, this.pluginConfig.messages.cantEnterOnRegion);
}
}
}
@@ -1,10 +1,14 @@
package com.eternalcode.combat.region;
import java.util.Optional;
import org.bukkit.Location;
@FunctionalInterface
public interface RegionProvider {
boolean isInRegion(Location location);
Optional<Region> getRegion(Location location);
default boolean isInRegion(Location location) {
return getRegion(location).isPresent();
}
}
@@ -1,32 +1,75 @@
package com.eternalcode.combat.region;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import java.util.Optional;
import java.util.TreeSet;
import org.bukkit.Location;
import java.util.List;
public class WorldGuardRegionProvider implements RegionProvider {
private final List<String> regions;
private final RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer();
private final TreeSet<String> regions = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
private final PluginConfig pluginConfig;
public WorldGuardRegionProvider(List<String> regions) {
this.regions = regions;
public WorldGuardRegionProvider(List<String> regions, PluginConfig pluginConfig) {
this.regions.addAll(regions);
this.pluginConfig = pluginConfig;
}
@Override
public boolean isInRegion(Location location) {
return this.regions.stream().anyMatch(region -> this.isInCombatRegion(location, region));
}
private boolean isInCombatRegion(Location location, String regionName) {
RegionContainer regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionQuery regionQuery = regionContainer.createQuery();
public Optional<Region> getRegion(Location location) {
RegionQuery regionQuery = this.regionContainer.createQuery();
ApplicableRegionSet applicableRegions = regionQuery.getApplicableRegions(BukkitAdapter.adapt(location));
return applicableRegions.getRegions().stream().anyMatch(region -> region.getId().equalsIgnoreCase(regionName));
for (ProtectedRegion region : applicableRegions.getRegions()) {
if (!isCombatRegion(region)) {
continue;
}
return Optional.of(new RegionImpl(location, region));
}
return Optional.empty();
}
private boolean isCombatRegion(ProtectedRegion region) {
if (this.regions.contains(region.getId())) {
return true;
}
if (this.pluginConfig.settings.shouldPreventPvpRegions) {
StateFlag.State flag = region.getFlag(Flags.PVP);
if (flag != null) {
return flag.equals(StateFlag.State.DENY);
}
}
return false;
}
private record RegionImpl(Location contextLocation, ProtectedRegion region) implements Region {
@Override
public Location getCenter() {
BlockVector3 min = region.getMinimumPoint();
BlockVector3 max = region.getMaximumPoint();
double x = (double) (min.getX() + max.getX()) / 2;
double z = (double) (min.getZ() + max.getZ()) / 2;
return new Location(contextLocation.getWorld(), x, contextLocation.getY(), z);
}
}
}
@@ -14,6 +14,7 @@ import java.util.concurrent.CompletableFuture;
public class UpdaterNotificationController implements Listener {
private static final String NEW_VERSION_AVAILABLE = "<b><gradient:#8a1212:#fc6b03>EternalCombat:</gradient></b> <color:#fce303>New version of EternalCombat is available, please update!";
private static final String RECEIVE_UPDATES_PERMISSION = "eternalcombat.receiveupdates";
private final UpdaterService updaterService;
private final PluginConfig pluginConfig;
@@ -32,7 +33,7 @@ public class UpdaterNotificationController implements Listener {
Player player = event.getPlayer();
Audience audience = this.audienceProvider.player(player.getUniqueId());
if (!player.hasPermission("eternalcombat.receiveupdates") || !this.pluginConfig.settings.shouldReceivePluginUpdates) {
if (!player.hasPermission(RECEIVE_UPDATES_PERMISSION) || !this.pluginConfig.settings.shouldReceivePluginUpdates) {
return;
}
@@ -1,25 +1,28 @@
package com.eternalcode.combat.util;
import com.eternalcode.commons.time.DurationParser;
import com.eternalcode.commons.time.TemporalAmountParser;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
public final class DurationUtil {
private DurationUtil() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
private static final TemporalAmountParser<Duration> WITHOUT_MILLS = new DurationParser()
.withUnit("s", ChronoUnit.SECONDS)
.withUnit("m", ChronoUnit.MINUTES)
.withUnit("h", ChronoUnit.HOURS)
.withUnit("d", ChronoUnit.DAYS)
.roundOff(ChronoUnit.MILLIS);
public DurationUtil() {
throw new UnsupportedOperationException("This class cannot be instantiated");
}
public static String format(Duration duration) {
return format(duration, true);
}
public static String format(Duration duration, boolean removeMillis) {
if (removeMillis) {
duration = Duration.ofSeconds(duration.toSeconds());
if (duration.toMillis() < 1000) {
return "0s";
}
return duration.toString()
.substring(2)
.replaceAll("(\\d[HMS])(?!$)", "$1 ")
.toLowerCase();
return WITHOUT_MILLS.format(duration);
}
}
@@ -4,9 +4,7 @@ import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.ToIntFunction;
@@ -23,49 +21,31 @@ public class InventoryUtil {
}
public static RemoveItemResult removeRandomItems(List<ItemStack> list, int itemsToDelete) {
List<ItemStack> restItems = new ArrayList<>(list);
Map<Integer, ItemStack> removedItems = new HashMap<>();
List<ItemStack> currentItems = new ArrayList<>(list);
List<ItemStack> removedItems = new ArrayList<>();
for (int i = 0; i < itemsToDelete; i++) {
if (restItems.isEmpty()) {
break;
}
int currentItemsToDelete = itemsToDelete;
while (currentItemsToDelete > 0) {
int randomIndex = RANDOM.nextInt(list.size());
ItemStack currentItem = currentItems.get(randomIndex);
int randomIndex = RANDOM.nextInt(restItems.size());
int amount = currentItem.getAmount();
int randomAmount = RANDOM.nextInt(0, Math.min(currentItemsToDelete, amount) + 1);
ItemStack item = restItems.get(randomIndex);
int newAmount = item.getAmount() - 1;
if (newAmount <= 0) {
restItems.remove(randomIndex);
incrementItem(removedItems, randomIndex, item);
if (amount <= 0 || randomAmount <= 0) {
continue;
}
item.setAmount(newAmount);
incrementItem(removedItems, randomIndex, item);
ItemStack removedItem = currentItem.clone();
removedItem.setAmount(randomAmount);
removedItems.add(removedItem);
currentItem.setAmount(amount - randomAmount);
currentItemsToDelete -= randomAmount;
}
return new RemoveItemResult(restItems, new ArrayList<>(removedItems.values()));
}
// TODO: kinda shitty, but no time for a proper solution now
private static void incrementItem(Map<Integer, ItemStack> items, int index, ItemStack itemStack) {
ItemStack itemInList = items.get(index);
if (itemInList == null) {
ItemStack cloned = itemStack.clone();
cloned.setAmount(1);
items.put(index, cloned);
return;
}
if (!itemInList.isSimilar(itemStack)) {
throw new IllegalStateException("Items are not similar!");
}
itemInList.setAmount(itemInList.getAmount() + 1);
return new RemoveItemResult(currentItems, removedItems);
}
}
@@ -1,18 +0,0 @@
package com.eternalcode.combat.util.legacy;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
public final class Legacy {
private static final LegacyComponentSerializer AMPERSAND_SERIALIZER = LegacyComponentSerializer.builder()
.character('&')
.hexColors()
.useUnusualXRepeatedCharacterHexFormat()
.build();
public static Component component(String text) {
return AMPERSAND_SERIALIZER.deserialize(text);
}
}
@@ -1,15 +0,0 @@
package com.eternalcode.combat.util.legacy;
import net.kyori.adventure.text.Component;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public final class LegacyColorProcessor implements UnaryOperator<Component> {
@Override
public Component apply(Component component) {
return component.replaceText(builder -> builder.match(Pattern.compile(".*"))
.replacement((matchResult, builder1) -> Legacy.component(matchResult.group())));
}
}
@@ -1,53 +0,0 @@
package com.eternalcode.combat.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
class DurationUtilTest {
private static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{ "3m 30s", true, Duration.ofMinutes(3).plusSeconds(30).plusMillis(500) },
{ "3m 30.5s", false, Duration.ofMinutes(3).plusSeconds(30).plusMillis(500) },
{ "59m 59s", true, Duration.ofMinutes(59).plusSeconds(59) },
{ "1h 1m", true, Duration.ofHours(1).plusMinutes(1).plusMillis(1) },
{ "0s", true, Duration.ofMillis(500) },
{ "0.5s", false, Duration.ofMillis(500) },
{ "0s", true, Duration.ofMillis(500) },
{ "0s", false, Duration.ZERO },
{ "30s", true, Duration.ofSeconds(30) },
{ "30s", false, Duration.ofSeconds(30) },
{ "30s", true, Duration.ofSeconds(30).plusMillis(500) },
{ "30.5s", false, Duration.ofSeconds(30).plusMillis(500) },
{ "1h 30m 30s", true, Duration.ofHours(1).plusMinutes(30).plusSeconds(30).plusMillis(500) },
{ "1h 30m 30.5s", false, Duration.ofHours(1).plusMinutes(30).plusSeconds(30).plusMillis(500) },
{ "1h 1m 1s", true, Duration.ofHours(1).plusMinutes(1).plusSeconds(1) },
{ "23h 59m 59s", true, Duration.ofHours(23).plusMinutes(59).plusSeconds(59) },
{ "23h 59m 59s", true, Duration.ofHours(23).plusMinutes(59).plusSeconds(59).plusMillis(999) },
{ "26h 3m 4s", true, Duration.ofDays(1).plusHours(2).plusMinutes(3).plusSeconds(4).plusMillis(500) },
{ "1h 1m 1s", true, Duration.ofHours(1).plusMinutes(1).plusSeconds(1).plusMillis(1) },
{ "1h 1s", true, Duration.ofHours(1).plusSeconds(1) },
{ "1m", true, Duration.ofMinutes(1).plusMillis(1) },
});
}
@ParameterizedTest
@MethodSource("data")
void testFormat(String expected, boolean removeMillis, Duration duration) {
String actual = DurationUtil.format(duration, removeMillis);
assertEquals(expected, actual);
}
}
+11 -41
View File
@@ -1,32 +1,10 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
`java-library`
checkstyle
`eternalcombat-java`
`eternalcombat-repositories`
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
id("com.github.johnrengelman.shadow") version "8.1.1"
id("xyz.jpenilla.run-paper") version "2.1.0"
}
checkstyle {
toolVersion = "10.12.2"
configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
maxErrors = 0
maxWarnings = 0
}
repositories {
mavenCentral()
maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://repo.eternalcode.pl/releases") }
maven { url = uri("https://storehouse.okaeri.eu/repository/maven-public/") }
maven { url = uri("https://repo.panda-lang.org/releases") }
maven { url = uri("https://maven.enginehub.org/repo/") }
id("net.minecrell.plugin-yml.bukkit")
id("io.github.goooler.shadow")
id("xyz.jpenilla.run-paper")
}
dependencies {
@@ -39,27 +17,17 @@ bukkit {
apiVersion = "1.13"
prefix = "EternalCombat"
name = "EternalCombat"
softDepend = listOf("WorldGuard")
softDepend = listOf("WorldGuard", "PlaceholderAPI")
version = "${project.version}"
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.withType<JavaCompile> {
options.compilerArgs = listOf("-Xlint:deprecation", "-parameters")
options.encoding = "UTF-8"
}
tasks {
runServer {
minecraftVersion("1.19.4")
minecraftVersion("1.21.1")
}
}
tasks.withType<ShadowJar> {
tasks.shadowJar {
archiveFileName.set("EternalCombat v${project.version}.jar")
dependsOn("checkstyleMain")
@@ -86,7 +54,9 @@ tasks.withType<ShadowJar> {
"com.eternalcode.gitcheck",
"org.json.simple",
"org.apache.commons",
"javassist"
"javassist",
"com.github.benmanes.caffeine",
"com.eternalcode.commons"
).forEach { pack ->
relocate(pack, "$prefix.$pack")
}
@@ -3,7 +3,6 @@ package com.eternalcode.combat;
import com.eternalcode.combat.bridge.BridgeService;
import com.eternalcode.combat.command.InvalidUsage;
import com.eternalcode.combat.command.PermissionMessage;
import com.eternalcode.combat.config.ConfigBackupService;
import com.eternalcode.combat.config.ConfigService;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.drop.DropController;
@@ -12,8 +11,6 @@ import com.eternalcode.combat.drop.DropManager;
import com.eternalcode.combat.drop.impl.PercentDropModifier;
import com.eternalcode.combat.drop.impl.PlayersHealthDropModifier;
import com.eternalcode.combat.fight.controller.FightActionBlockerController;
import com.eternalcode.combat.fight.controller.FightDeathCauseController;
import com.eternalcode.combat.fight.controller.FightEscapeController;
import com.eternalcode.combat.fight.controller.FightMessageController;
import com.eternalcode.combat.fight.controller.FightTagController;
import com.eternalcode.combat.fight.controller.FightUnTagController;
@@ -23,6 +20,8 @@ import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.FightTask;
import com.eternalcode.combat.fight.bossbar.FightBossBarService;
import com.eternalcode.combat.fight.effect.FightEffectService;
import com.eternalcode.combat.fight.logout.LogoutController;
import com.eternalcode.combat.fight.logout.LogoutService;
import com.eternalcode.combat.fight.pearl.FightPearlController;
import com.eternalcode.combat.fight.pearl.FightPearlManager;
import com.eternalcode.combat.fight.tagout.FightTagOutController;
@@ -33,7 +32,8 @@ import com.eternalcode.combat.region.RegionController;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.updater.UpdaterNotificationController;
import com.eternalcode.combat.updater.UpdaterService;
import com.eternalcode.combat.util.legacy.LegacyColorProcessor;
import com.eternalcode.commons.adventure.AdventureLegacyColorPostProcessor;
import com.eternalcode.commons.adventure.AdventureLegacyColorPreProcessor;
import com.google.common.base.Stopwatch;
import dev.rollczi.litecommands.LiteCommands;
import dev.rollczi.litecommands.bukkit.adventure.platform.LiteBukkitAdventurePlatformFactory;
@@ -54,12 +54,19 @@ import java.util.stream.Stream;
public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
private static final String FALLBACK_PREFIX = "eternalcombat";
private static final int BSTATS_METRICS_ID = 17803;
private FightManager fightManager;
private FightPearlManager fightPearlManager;
private FightTagOutService fightTagOutService;
private FightEffectService fightEffectService;
private LogoutService logoutService;
private DropManager dropManager;
private DropKeepInventoryManager dropKeepInventoryManager;
private RegionProvider regionProvider;
private PluginConfig pluginConfig;
@@ -74,8 +81,7 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
File dataFolder = this.getDataFolder();
ConfigBackupService backupService = new ConfigBackupService(dataFolder);
ConfigService configService = new ConfigService(backupService);
ConfigService configService = new ConfigService();
EventCaller eventCaller = new EventCaller(server);
@@ -85,6 +91,7 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
this.fightPearlManager = new FightPearlManager(this.pluginConfig.pearl);
this.fightTagOutService = new FightTagOutService();
this.fightEffectService = new FightEffectService();
this.logoutService = new LogoutService();
this.dropManager = new DropManager();
this.dropKeepInventoryManager = new DropKeepInventoryManager();
@@ -92,18 +99,19 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
this.audienceProvider = BukkitAudiences.create(this);
MiniMessage miniMessage = MiniMessage.builder()
.postProcessor(new LegacyColorProcessor())
.postProcessor(new AdventureLegacyColorPostProcessor())
.preProcessor(new AdventureLegacyColorPreProcessor())
.build();
FightBossBarService fightBossBarService = new FightBossBarService(this.pluginConfig, this.audienceProvider, miniMessage);
BridgeService bridgeService = new BridgeService(this.pluginConfig, server.getPluginManager(), this.getLogger());
bridgeService.init();
BridgeService bridgeService = new BridgeService(this.pluginConfig, server.getPluginManager(), this.getLogger(), this);
bridgeService.init(this.fightManager, server);
this.regionProvider = bridgeService.getRegionProvider();
NotificationAnnouncer notificationAnnouncer = new NotificationAnnouncer(this.audienceProvider, miniMessage);
this.liteCommands = LiteBukkitAdventurePlatformFactory.builder(server, "eternalcombat", this.audienceProvider)
this.liteCommands = LiteBukkitAdventurePlatformFactory.builder(server, FALLBACK_PREFIX, this.audienceProvider)
.argument(Player.class, new BukkitPlayerArgument<>(this.getServer(), this.pluginConfig.messages.playerNotFound))
.contextualBind(Player.class, new BukkitOnlyPlayerContextual<>(this.pluginConfig.messages.admin.onlyForPlayers))
@@ -118,19 +126,19 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
FightTask fightTask = new FightTask(server, this.pluginConfig, this.fightManager, fightBossBarService, notificationAnnouncer);
this.getServer().getScheduler().runTaskTimer(this, fightTask, 20L, 20L);
new Metrics(this, 17803);
new Metrics(this, BSTATS_METRICS_ID);
Stream.of(
new PercentDropModifier(this.pluginConfig.dropSettings),
new PlayersHealthDropModifier(this.pluginConfig.dropSettings)
new PlayersHealthDropModifier(this.pluginConfig.dropSettings, this.logoutService)
).forEach(this.dropManager::registerModifier);
Stream.of(
new FightDeathCauseController(this.fightManager),
new DropController(this.dropManager, this.dropKeepInventoryManager, this.pluginConfig.dropSettings, this.fightManager),
new FightTagController(this.fightManager, this.pluginConfig, notificationAnnouncer),
new FightUnTagController(this.fightManager, this.pluginConfig),
new FightEscapeController(this.fightManager, this.pluginConfig, notificationAnnouncer),
new FightTagController(this.fightManager, this.pluginConfig),
new LogoutController(this.fightManager, this.logoutService, notificationAnnouncer, this.pluginConfig),
new FightUnTagController(this.fightManager, this.pluginConfig, this.logoutService),
new FightActionBlockerController(this.fightManager, notificationAnnouncer, this.pluginConfig),
new FightPearlController(this.pluginConfig.pearl, notificationAnnouncer, this.fightManager, this.fightPearlManager),
new UpdaterNotificationController(updaterService, this.pluginConfig, this.audienceProvider, miniMessage),
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+12 -9
View File
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -145,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -202,11 +205,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
Vendored
+12 -10
View File
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
+10 -2
View File
@@ -15,7 +15,15 @@
"matchPackagePatterns": [
"*"
]
},
{
"groupName": "spigot dependencies",
"groupSlug": "spigotmc",
"matchPackagePatterns": [
"org.spigotmc*"
]
}
],
"separateMajorMinor": false
}
"separateMajorMinor": true,
"pruneStaleBranches": true
}